androidMVP示例代码分析

这篇文章主要讲解了“android MVP示例代码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“android MVP示例代码分析”吧!

让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:申请域名、虚拟空间、营销软件、网站建设、虹口网站维护、网站推广。

上项目结构图:

android MVP示例代码分析

从包名上很容易分辨出功能:addedittask是添加任务,data是数据管理,statistics是统计,taskdetail是任务详情,tasks是任务浏览之类的。事实上这个项目的关键也就是:  Tasks 、 TaskDetail 、 AddEditTask 、 Statistics 。

这四个关键的地方都有相同之处:

  • 定义了view和presenter的契约

  • Activity负责fragment和presenter的创建

  • Fragment实现了view接口

  • presenter实现了presenter接口

也就是说,几个功能每一个都是MVP的模式,只不过Model层是公用的。而且这个项目里View层都是Fragment,果然google推荐用Fragment自己的项目里也给我们做个示范……其实关于到底是不是要用Fragment,还是有些争议的,那么到底要不要用呢?我觉得对于个体而言,不管你喜不喜欢,都要用一用,试一试,因为人要成长,必须踩坑。对于正式项目而言,则需要综合考量,使用Fragment的利是否大于弊。

扯远了,接下来看一下他代码仓库给的一张结构图:

android MVP示例代码分析

可以看出来左边是数据管理,典型的Model层。而右边呢,你可能认为Activity是Presenter,事实上并不是,Presenter在Activity内,Fragment是View无疑。到这,我觉得关于这个项目结构的简介已经足够了,接下来看代码。

我觉得看一个Android项目的正确姿势应该是先把玩一下app,看一下功能。贴几张app的图:

android MVP示例代码分析

android MVP示例代码分析

android MVP示例代码分析

android MVP示例代码分析

接着就该上入口的Activity看一下了,这个项目的入口Activity是TasksActivity,所在的包是tasks,看一下有哪些东西:

android MVP示例代码分析

***个是自定义View,第二个就是入口Activity了,第三个即上面所说的“契约”,里面包含了View接口和Presenter接口。TasksFilterType则是一个枚举,里面有三个过滤类型:所有,进行中的,完成的。TasksFragment就是MVP中的View了,TasksPresenter则是MVP中的Presenter了。看一下TasksActivity中的初始化代码:

  protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.tasks_act);         Log.e(getClass().getSimpleName(),"onCreate");          // Set up the toolbar.         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);         setSupportActionBar(toolbar);         ActionBar ab = getSupportActionBar();         ab.setHomeAsUpIndicator(R.drawable.ic_menu);         ab.setDisplayHomeAsUpEnabled(true);          /**          * 以下的DrawerLayout暂时不看了          */         // Set up the navigation drawer.         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);         mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark);         NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);         if (navigationView != null) {             setupDrawerContent(navigationView);         }          // 获取fragment并将之添加到视图上         // 悬浮按钮在这个taksFragment里设置的点击事件         TasksFragment tasksFragment =                 (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); //        getSupportFragmentManager().findFragmentById()         if (tasksFragment == null) {             // Create the fragment             tasksFragment = TasksFragment.newInstance();             // 提供方法帮助activity加载ui             // 这个方法其实就是拿到一个事务,然后把这个fragment add到对应的id上了             ActivityUtils.addFragmentToActivity(                     getSupportFragmentManager(), tasksFragment, R.id.contentFrame);         }          // Create the presenter         mTasksPresenter = new TasksPresenter(                 Injection.provideTasksRepository(getApplicationContext()), tasksFragment);          // Load previously saved state, if available.         if (savedInstanceState != null) {             TasksFilterType currentFiltering =                     (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);             mTasksPresenter.setFiltering(currentFiltering);         }     }

首先是初始化toolbar和侧滑,这里不必深入细节,可以跳过这俩。之后初始化fragment和presenter,初始化Fragment先是尝试通过id寻找可能已经存在的Fragment对象,如果没有,则重新创建一个Fragment对象。下一步则是创建一个presenter,***则是让应用在横竖屏状态切换的情况下恢复数据。

接下来看一下View和Presenter的“契约”:

public interface TasksContract {      interface View extends BaseView<Presenter> {          void setLoadingIndicator(boolean active);          void showTasks(List<Task> tasks);          void showAddTask();          void showTaskDetailsUi(String taskId);          void showTaskMarkedComplete();          void showTaskMarkedActive();          void showCompletedTasksCleared();          void showLoadingTasksError();          void showNoTasks();          void showActiveFilterLabel();          void showCompletedFilterLabel();          void showAllFilterLabel();          void showNoActiveTasks();          void showNoCompletedTasks();          void showSuccessfullySavedMessage();          boolean isActive();          void showFilteringPopUpMenu();     }      interface Presenter extends BasePresenter {          void result(int requestCode, int resultCode);          void loadTasks(boolean forceUpdate);          void addNewTask();          void openTaskDetails(@NonNull Task requestedTask);          void completeTask(@NonNull Task completedTask);          void activateTask(@NonNull Task activeTask);          void clearCompletedTasks();          void setFiltering(TasksFilterType requestType);          TasksFilterType getFiltering();     } }

这个接口里包含了View和Presenter,可以看到View和Presenter里的方法比较多,事实上这是应该的。因为在MVP架构里,View只负责根据Presenter的指示绘制UI,View将所有的用户交互交给Presenter处理。所以Presenter的很多方法可能就是对用户的输入的处理,而有输入必然有输出,View接口定义的各个方法便是给Presenter回调的。Presenter通过回调函数将对用户的输入的处理结果推到View中,View再根据这个结果对UI进行相应的更新。而在此项目中,Fragment就是View,在Fragment的各个点击事件中都调用了Presenter的对应方法,将业务逻辑交给Presenter处理。这看起来比传统的MVC强上很多,因为传统MVC中Activity既可以认为是Controller亦可以认为是View,职责难以分离,写到后面可能一个Activity就有上千行的代码,这会为后续的维护带来不少麻烦。而MVP则将业务逻辑抽取到了Presenter中,作为View的Fragment或者Activity职责更加单一,无疑为后续的开发维护带来了便利。

接下来详细的看Presenter的初始化,Presenter的创建是在TasksActivity中完成的,查看其构造函数:

public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {       mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");       mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");        mTasksView.setPresenter(this);   }

前两个检查传入的参数是否为空,接着将其赋值给TasksPresenter内的引用,调用view的setPresenter方法,将自身传入,这样view中就可以使用presenter对象了,比直接从activity中拿看起来要优雅了不少。Presenter具体的逻辑就不看了,都是一些比较简单的代码,回顾一下打开这个app所发生的事件的流程:创建TasksActivity  -> 初始化Toolbar -> 初始化侧滑 -> 创建TasksFragment对象 -> 创建TaskPresenter对象  -> 给Fragment设置Presenter对象 ->  初始化Fragment布局,这样一套流程下来,整个流程就理清了,接下来只是等待用户的输入了。

接下来要看的是从本文开始到现在都一直忽略了的Model:TasksRepository。不过在分析TasksRepository之前,安利一下这个项目里的实体类,写的比较优雅,我们平时写实体类时***也能按照他的套路来写。我为什么说他写的比较优雅呢?因为各个属性或者是带返回值的方法都打上了@Nullable或者@NoNull注解来说明是否可以为空,事实上空指针这个错可以算是平时经常遇到的错了&hellip;&hellip;不过如果你有良好的设计和编码习惯,是可以避免的,带上这两个注解可以在编译期给你相关的提示。不仅如此,这个实体类还复写了equals()、hashCode()和toString()方法,而且实现的方式也符合规范,关于如何复写这三个方法,在《effective  java》上有很好的总结,各位可以去读一下。

/*  * Copyright 2016, The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */  package com.example.android.architecture.blueprints.todoapp.data;  import android.support.annotation.NonNull; import android.support.annotation.Nullable;  import com.google.common.base.Objects; import com.google.common.base.Strings;  import java.util.UUID;  /**  * Immutable model class for a Task.  */ public final class Task {      @NonNull     private final String mId;      @Nullable     private final String mTitle;      @Nullable     private final String mDescription;      private final boolean mCompleted;      /**      * Use this constructor to create a new active Task.      *      * @param title       title of the task      * @param description description of the task      */     public Task(@Nullable String title, @Nullable String description) {         this(title, description, UUID.randomUUID().toString(), false);     }      /**      * Use this constructor to create an active Task if the Task already has an id (copy of another      * Task).      *      * @param title       title of the task      * @param description description of the task      * @param id          id of the task      */     public Task(@Nullable String title, @Nullable String description, @NonNull String id) {         this(title, description, id, false);     }      /**      * Use this constructor to create a new completed Task.      *      * @param title       title of the task      * @param description description of the task      * @param completed   true if the task is completed, false if it's active      */     public Task(@Nullable String title, @Nullable String description, boolean completed) {         this(title, description, UUID.randomUUID().toString(), completed);     }      /**      * Use this constructor to specify a completed Task if the Task already has an id (copy of      * another Task).      *      * @param title       title of the task      * @param description description of the task      * @param id          id of the task      * @param completed   true if the task is completed, false if it's active      */     public Task(@Nullable String title, @Nullable String description,                 @NonNull String id, boolean completed) {         mId = id;         mTitle = title;         mDescription = description;         mCompleted = completed;     }      @NonNull     public String getId() {         return mId;     }      @Nullable     public String getTitle() {         return mTitle;     }      @Nullable     public String getTitleForList() {         if (!Strings.isNullOrEmpty(mTitle)) {             return mTitle;         } else {             return mDescription;         }     }      @Nullable     public String getDescription() {         return mDescription;     }      public boolean isCompleted() {         return mCompleted;     }      public boolean isActive() {         return !mCompleted;     }      public boolean isEmpty() {         return Strings.isNullOrEmpty(mTitle) &amp;&amp;                Strings.isNullOrEmpty(mDescription);     }      @Override     public boolean equals(Object o) {         if (this == o) return true;         if (o == null || getClass() != o.getClass()) return false;         Task task = (Task) o;         return Objects.equal(mId, task.mId) &amp;&amp;                Objects.equal(mTitle, task.mTitle) &amp;&amp;                Objects.equal(mDescription, task.mDescription);     }      @Override     public int hashCode() {         return Objects.hashCode(mId, mTitle, mDescription);     }      @Override     public String toString() {         return "Task with title " + mTitle;     } }

先看一下TasksRepository所在的包的结构:

android MVP示例代码分析

可以从包名上看出local是从本地读取数据,remote是远程读取,当然了,这里只是模拟远程读取。本地采用了数据库存取的方式。在TasksRepository(下文简称TR)内部有两个TasksDataSource的引用:

private final TasksDataSource mTasksRemoteDataSource;   private final TasksDataSource mTasksLocalDataSource;

TasksDataSource是data包内的一个接口,使用接口引用,无非是想解耦,就算以后需求变更,不想采用数据库的方式存储数据,只要实现了这个接口,TR内部的代码也无需变更。TR用了单例,实现方式并不是线程安全的:

/**     * Returns the single instance of this class, creating it if necessary.     *     * @param tasksRemoteDataSource the backend data source     * @param tasksLocalDataSource  the device storage data source     * @return the {@link TasksRepository} instance     */    public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,                                              TasksDataSource tasksLocalDataSource) {        if (INSTANCE == null) {            INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);        }        return INSTANCE;    }

说到底,他根本没有线程安全的必要,至少在这个app里,没有并发创建这个对象的场景,所以够用就行了。在TR内部使用了一个LinkedHashMap作为容器来保存Tasks,主要看一下两个方法,首先是存储:

public void saveTask(@NonNull Task task) {        checkNotNull(task);        mTasksRemoteDataSource.saveTask(task);        mTasksLocalDataSource.saveTask(task);         // Do in memory cache update to keep the app UI up to date        if (mCachedTasks == null) {            mCachedTasks = new LinkedHashMap<>();        }        mCachedTasks.put(task.getId(), task);    }

会将传入的task存储到远程数据源和本地数据源(本地数据库)中,然后将这个task传到mCachedTasks(LinkedHashMap)中。代码比较简单,不做更多的分析,接下来看一下读取Task:

public void getTasks(@NonNull final LoadTasksCallback callback) {        checkNotNull(callback);         // Respond immediately with cache if available and not dirty        if (mCachedTasks != null &amp;&amp; !mCacheIsDirty) {            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));            return;        }         if (mCacheIsDirty) {            // If the cache is dirty we need to fetch new data from the network.            getTasksFromRemoteDataSource(callback);        } else {            // Query the local storage if available. If not, query the network.            mTasksLocalDataSource.getTasks(new LoadTasksCallback() {                @Override                public void onTasksLoaded(List<Task> tasks) {                    refreshCache(tasks);                    callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));                }                 @Override                public void onDataNotAvailable() {                    getTasksFromRemoteDataSource(callback);                }            });        }    }

这个taskId是需要获取Task的id,也是唯一标识,GetTaskCallback则是负责传递数据的接口回调。首先是从内存中读取数据,getTaskWithId方法就是,看一下代码:

private Task getTaskWithId(@NonNull String id) {        checkNotNull(id);        if (mCachedTasks == null || mCachedTasks.isEmpty()) {            return null;        } else {            return mCachedTasks.get(id);        }    }

就从保存task的LinkedHashMap中读取数据。如果这个过程读取不到数据那么接着从本地数据源中读取数据,如果本地数据源也没有拿到这个数据,那么最终就从远程数据源中读取数据。

感谢各位的阅读,以上就是“android MVP示例代码分析”的内容了,经过本文的学习后,相信大家对android MVP示例代码分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!

网页标题:androidMVP示例代码分析
本文链接:https://www.cdcxhl.com/article42/gcjghc.html

成都网站建设公司_创新互联,为您提供搜索引擎优化软件开发静态网站建站公司服务器托管移动网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都app开发公司