Google官方Android App架构蓝图

Google官方Android App架构蓝图_第1张图片
Android Developer.png

众所周知,Android Framework提供了大量灵活的方式来定义如何组织和架构一个Android App,自由,附有价值,但同时也导致App拥有了大量的类,不一致的命名和架构也给测试,维护和拓展带来了困难。由此带来了很多的App架构,MVC、MVP,MVVM等,前几天Google官方推出了关于Android 架构蓝图的Sample,借此来引导Android开发者解决这些问题,创建自己的App。重点放在代码结构,测试和维护。目前推出了3个Sample,接下来还会继续推出,至于在项目中用哪个取决于你自己。这些Sample不能算是经典例子,但可以作为参考。不过从刚推出就在Github趋势上位列前茅,也能感受到大家对此的期待。

Demo运行图

Google官方Android App架构蓝图_第2张图片
Demo运行图.png

TODO-MVP

架构图

Google官方Android App架构蓝图_第3张图片
TODO-MVP架构图.png

架构解析

Google官方Android App架构蓝图_第4张图片
代码结构.png

这个是很经典的MVP架构,从代码结构上也能看到一些启发,按业务功能进行划分,例如addedittask(新增任务),taskdetail(任务详情)等,里面含有相关的Presenter,View,Activity和Fragment,这样做的好处是可以方便的找到业务功能代码。
我们以addedittask为例:
AddEditTaskActivity:因为用了Fragment来显示页面,Activity没有过多的逻辑。
AddEditTaskContract:里面包含View和Presenter接口类

public interface AddEditTaskContract {

    interface View extends BaseView {

        void showEmptyTaskError();

        void showTasksList();

        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void createTask(String title, String description);

        void updateTask( String title, String description);

        void populateTask();
    }
}

AddEditTaskFragment:

public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
    ......//省略

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

    public AddEditTaskFragment() {
        // Required empty public constructor
    }

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

    @Override
    public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ......//省略
        FloatingActionButton fab =
                (FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);
        fab.setImageResource(R.drawable.ic_done);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isNewTask()) {
                    mPresenter.createTask(
                            mTitle.getText().toString(),
                            mDescription.getText().toString());
                } else {
                    mPresenter.updateTask(
                            mTitle.getText().toString(),
                            mDescription.getText().toString());
                }

            }
        });
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.addtask_frag, container, false);
        ......//省略
        return root;
    }

    @Override
    public void showEmptyTaskError() {
        Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();
    }

    @Override
    public void showTasksList() {
        getActivity().setResult(Activity.RESULT_OK);
        getActivity().finish();
    }

    @Override
    public void setTitle(String title) {
        mTitle.setText(title);
    }

    @Override
    public void setDescription(String description) {
        mDescription.setText(description);
    }

    @Override
    public boolean isActive() {
        return isAdded();
    }

    private boolean isNewTask() {
        return mEditedTaskId == null;
    }
}

AddEditTaskPresenter:Presenter实现类,业务逻辑的实现

public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
        TasksDataSource.GetTaskCallback {
       ......//省略

    /**
     * Creates a presenter for the add/edit view.
     *
     * @param taskId ID of the task to edit or null for a new task
     * @param tasksRepository a repository of data for tasks
     * @param addTaskView the add/edit view
     */
    public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
            @NonNull AddEditTaskContract.View addTaskView) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository);
        mAddTaskView = checkNotNull(addTaskView);

        mAddTaskView.setPresenter(this);
    }

    @Override
    public void start() {
        if (mTaskId != null) {
            populateTask();
        }
    }

    @Override
    public void createTask(String title, String description) {
        Task newTask = new Task(title, description);
        if (newTask.isEmpty()) {
            mAddTaskView.showEmptyTaskError();
        } else {
            mTasksRepository.saveTask(newTask);
            mAddTaskView.showTasksList();
        }
    }

    @Override
    public void updateTask(String title, String description) {
        if (mTaskId == null) {
            throw new RuntimeException("updateTask() was called but task is new.");
        }
        mTasksRepository.saveTask(new Task(title, description, mTaskId));
        mAddTaskView.showTasksList(); // After an edit, go back to the list.
    }

    @Override
    public void populateTask() {
        if (mTaskId == null) {
            throw new RuntimeException("populateTask() was called but task is new.");
        }
        mTasksRepository.getTask(mTaskId, this);
    }

    @Override
    public void onTaskLoaded(Task task) {
        // The view may not be able to handle UI updates anymore
        if (mAddTaskView.isActive()) {
            mAddTaskView.setTitle(task.getTitle());
            mAddTaskView.setDescription(task.getDescription());
        }
    }

    @Override
    public void onDataNotAvailable() {
        // The view may not be able to handle UI updates anymore
        if (mAddTaskView.isActive()) {
            mAddTaskView.showEmptyTaskError();
        }
    }
}

TODO-MVP-Loaders

架构图

Google官方Android App架构蓝图_第5张图片
TODO-MVP-Loaders架构图.png

架构分析

和TODO-MVP的区别是通过Loader对数据进行异步加载,监控其数据源并在内容变化时传递新结果。关于Loader的使用,可以直接看官方文档,我就不复述了。

TODO-DataBinding

架构图

Google官方Android App架构蓝图_第6张图片
TODO-DataBinding架构图.png

架构分析

和TODO-MVP的区别是通过Data Binding来绑定app逻辑和layouts文件。关于Data Binding的使用,可以直接看官方文档,我就不复述了。

总结

通过MVP进行业务逻辑和视图的分离,让View专注于处理数据的可视化以及与用户的交互,同时让Repository只关系数据的处理,让Presenter作为View和Repository之间的纽带,易于维护和测试。代码比较简单,具体是否使用Loader和Data Binding就看个人喜好了。虽然Google并没有把这些当做指导文档,但也算给Android开发者带来了一些思路。

参考资料

Android-Architecture
Github
Loader文档
Data Binding文档

欢迎关注我的微博

你可能感兴趣的:(Google官方Android App架构蓝图)