基于Android真实项目教你一步一步搭建架构2 -- Google官方Mvp架构

前言

笔者上一篇文章说过,上家公司的项目架构是在Google官方todo-mvp-Rxjava的Demo架构基础上进行搭建的,而这个todo-mvp-Rxjava架构是基于todo-mvp加入rxjava搭建的,所以我们还是先来看下Android-architecture项目的todo-mvp分支
官方todo-mvp地址

todo-mvp项目结构

todo-mvp主要使用了mvp架构来实现(如果你对什么是Mvp还不了解,那么请看我的上一篇文章 基于Android真实项目教你一步一步搭建架构1 – 概述),如图

mvp
图中的Repository就是数据源,即M,包括Local数据和Remote数据;Fragment为V;Activity中依赖了P,V(Fragment)与P相互依赖,P依赖了M(Repository),即P分离了M与V;当然有时我们不需要Fragment,那么可以直接使用Activity来作为V

项目的主要包结构

(不含test测试package):
这里写图片描述

除BaseView、BasePresenter两个接口,其他package都以业务功能来划分的:

  • addedittask —— 添加任务
  • data —— 数据源
  • statistics —— 任务统计
  • taskdetail —— 任务详情
  • tasks —— 任务列表
  • util —— 工具类

V和P的Base接口

我们来看下BaseView、BasePresenter两个接口

BaseView:
public interface BaseView<T> {
    void setPresenter(T presenter);
}

BaseView 定义了setPresenter方法,因为view需要依赖于p

BasePresenter:
public interface BasePresenter {
    void start();
}

BasePresenter定义了start方法,这个主要是当作初始化的方法,会在Activity的onResum生命周期里调用进行一些初始化工作

数据源Model

再来看下data模块,M
这里写图片描述

data文件夹:

  • Task — 它就是一个Java bean 而已

local文件夹:

  • TasksLocalDataSource —本地数据源
  • TasksDbHelper — 数据库操作
  • TasksPersistenceContract — 约定了数据库表字段,为TasksDbHelper进行数据库操作定义的,跟MVP结构无关,不懂可以不用理会

remote文件夹:

  • TasksRemoteDataSource — 远程数据,一般可能从网络获取,当然google的demo里没有去访问网络,只是模拟从网络获取数据

source文件夹:

  • TasksDataSource —Task数据操作的接口
  • TasksRepository —实现了TasksDataSource,依赖了TasksLocalDataSource、TasksRemoteDataSource,实现根据不同情形,获取Local或Remote的相关数据,它就是我们所说的Model,P依赖于这一个类来获取数据源,然后交给V展示

我们来看下M实现的接口,有哪些方法去获取数据呢

public interface TasksDataSource {

    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }

    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    void getTasks(@NonNull LoadTasksCallback callback);

    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

    void saveTask(@NonNull Task task);

    void completeTask(@NonNull Task task);

    void completeTask(@NonNull String taskId);

    void activateTask(@NonNull Task task);

    void activateTask(@NonNull String taskId);

    void clearCompletedTasks();

    void refreshTasks();

    void deleteAllTasks();

    void deleteTask(@NonNull String taskId);
}

P可以调用M的getTask等方法来获取数据

tasks模块分析

既然我们Mvp的M找到了,那么接下来挑一个业务模块分析一下V和P的实现(其它模块大同小异),比如tasks

这里写图片描述

  • ScrollChildSwipeRefreshLayout — 这是一个自定义Layout,不用理会
  • TasksActivity — Activity
  • TasksContract — 契约接口(每个功能模块都有一个),约定了两个子接口View和Presenter,及各自的公共方法;分别实现BaseView、BasePresenter两个接口
  • TasksFilterType — enum类,任务过滤类型,可以不用理会
  • TasksFragment — Fragment,实现TasksContract.View,所以它就是V
  • TasksPresenter — Presenter,实现TasksContract.Presenter,所以它就是P

简单的看下TasksContract契约接口就可以了,就是定义存放了V和P的接口而已(写到一起便于管理查看,你也可以分开):

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();
    }
}

M、V、P找到了,它们之间是怎么联系的呢?P和V的初始化在Activity中完成,我们来看下
TasksActivity的主要代码:

public class TasksActivity extends AppCompatActivity {

    private TasksPresenter mTasksPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tasks_act);

        TasksFragment tasksFragment =
                (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
        if (tasksFragment == null) {
            // 创建 fragment ,即View
            tasksFragment = TasksFragment.newInstance();
            ActivityUtils.addFragmentToActivity(
                    getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
        }

        // 创建 presenter,即P,需要传入TasksRepository和fragments,也就是依赖了M和V
        mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment);


        if (savedInstanceState != null) {
            TasksFilterType currentFiltering =
                    (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);
            mTasksPresenter.setFiltering(currentFiltering);
        }
    }
}

我们拿一个具体的业务功能,看看它们是怎么实现相互调用的,比如展示tasks列表
在上面的TasksContract的Presenter中,有一个方法:

void loadTasks(boolean forceUpdate);

相应的TasksContract的View中,有一个方法:

void showTasks(List<Task> tasks);

看下TasksPresenter的相关的一些代码:

public class TasksPresenter implements TasksContract.Presenter {

    private final TasksRepository mTasksRepository;

    private final TasksContract.View mTasksView;

    private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS;

    private boolean mFirstLoad = true;

    public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
        //TasksPresenter依赖了TasksRepository、TasksContract.View,即P依赖了M和V
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
        mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");

        //view是怎么依赖P的,就是通过这个方法将P传进去,此方法在BaseView中定义,还记得吗
        mTasksView.setPresenter(this);
    }

    @Override
    public void start() {
        loadTasks(false);
    }

    @Override
    public void loadTasks(boolean forceUpdate) {
        loadTasks(forceUpdate || mFirstLoad, true);
        mFirstLoad = false;
    }

    private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
        if (showLoadingUI) {
            mTasksView.setLoadingIndicator(true);
        }
        if (forceUpdate) {
            mTasksRepository.refreshTasks();
        }

        EspressoIdlingResource.increment(); 

        //先从M中获取数据
        mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
            @Override
            public void onTasksLoaded(List<Task> tasks) {
                List<Task> tasksToShow = new ArrayList<Task>();

                if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
                    EspressoIdlingResource.decrement(); 
                }

                for (Task task : tasks) {
                    switch (mCurrentFiltering) {
                        case ALL_TASKS:
                            tasksToShow.add(task);
                            break;
                        case ACTIVE_TASKS:
                            if (task.isActive()) {
                                tasksToShow.add(task);
                            }
                            break;
                        case COMPLETED_TASKS:
                            if (task.isCompleted()) {
                                tasksToShow.add(task);
                            }
                            break;
                        default:
                            tasksToShow.add(task);
                            break;
                    }
                }
                if (!mTasksView.isActive()) {
                    return;
                }
                if (showLoadingUI) {
                    mTasksView.setLoadingIndicator(false);
                }
        //获取数据后,再调用processTasks(List tasks)
                processTasks(tasksToShow);
            }

            @Override
            public void onDataNotAvailable() {
                if (!mTasksView.isActive()) {
                    return;
                }
                mTasksView.showLoadingTasksError();
            }
        });
    }

    private void processTasks(List<Task> tasks) {
        if (tasks.isEmpty()) {
            processEmptyTasks();
        } else {
             //数据传进来后,再将数据显示在V上
            mTasksView.showTasks(tasks);
            showFilterLabel();
        }
    }
}

从上,看出TasksPresenter依赖了TasksRepository、TasksContract.View,即P依赖了M和V;当loadTasks(boolean forceUpdate, final boolean showLoadingUI)被调用后,先从M中获取数据,再调用processTasks(List tasks),其内部调用mTasksView.showTasks(tasks)将数据显示在V上;最后还要说的一点是在TasksPresenter的构造方法中,mTasksView.setPresenter(this) 将P传递给了V

再来看下V如何通过P,来获取数据并显示
TasksFragment,即V的主要代码:

public class TasksFragment extends Fragment implements TasksContract.View {

    private TasksContract.Presenter mPresenter;

    public TasksFragment() {
        // Requires empty public constructor
    }

    public static TasksFragment newInstance() {
        return new TasksFragment();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mListAdapter = new TasksAdapter(new ArrayList<Task>(0), mItemListener);
    }

    @Override
    public void onResume() {
        super.onResume();
        //调用了mPresenter.start(),而mPresenter.start()中,就调用了TasksPresenter的loadTasks(false);
        mPresenter.start();
    }

    @Override
    public void setPresenter(@NonNull TasksContract.Presenter presenter) {
    //V调用了这个方法把P传进来的
        mPresenter = checkNotNull(presenter);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
            // 下拉刷新时,也会调用TasksPresenter的loadTasks(false);
                mPresenter.loadTasks(false);
            }
        });

        return root;
    }

    @Override
    public void showTasks(List<Task> tasks) {
        mListAdapter.replaceData(tasks);
        mTasksView.setVisibility(View.VISIBLE);
        mNoTasksView.setVisibility(View.GONE);
    }
}

看TasksFragment的onResume()中,调用了mPresenter.start(),而mPresenter.start()中,就调用了TasksPresenter的loadTasks(false);TasksFragment的onCreateView()中还注册了一个监听回调,即下拉刷新时,也会调用TasksPresenter的loadTasks(false);

至此,一条完整的MVP架构实现的业务链就分析完成了,那么我们再来看下这个图:
mvp
现在看懂了吗,如果还不懂的话,建议你再把文章多看几遍,如果还不懂,那,好吧,没事。下一章,我将新建一个项目,结合实例手把手把教你把这个官方的MVP框架搭起来

累了,好久没写,都不会写文章了,88

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值