Google官方Android App架構(gòu)藍(lán)圖

Android Developer.png

眾所周知,Android Framework提供了大量靈活的方式來定義如何組織和架構(gòu)一個(gè)Android App,自由,附有價(jià)值,但同時(shí)也導(dǎo)致App擁有了大量的類,不一致的命名和架構(gòu)也給測試,維護(hù)和拓展帶來了困難。由此帶來了很多的App架構(gòu),MVC、MVP,MVVM等,前幾天Google官方推出了關(guān)于Android 架構(gòu)藍(lán)圖的Sample,借此來引導(dǎo)Android開發(fā)者解決這些問題,創(chuàng)建自己的App。重點(diǎn)放在代碼結(jié)構(gòu),測試和維護(hù)。目前推出了3個(gè)Sample,接下來還會(huì)繼續(xù)推出,至于在項(xiàng)目中用哪個(gè)取決于你自己。這些Sample不能算是經(jīng)典例子,但可以作為參考。不過從剛推出就在Github趨勢上位列前茅,也能感受到大家對此的期待。

Demo運(yùn)行圖

Demo運(yùn)行圖.png

TODO-MVP

架構(gòu)圖

TODO-MVP架構(gòu)圖.png

架構(gòu)解析

代碼結(jié)構(gòu).png

這個(gè)是很經(jīng)典的MVP架構(gòu),從代碼結(jié)構(gòu)上也能看到一些啟發(fā),按業(yè)務(wù)功能進(jìn)行劃分,例如addedittask(新增任務(wù)),taskdetail(任務(wù)詳情)等,里面含有相關(guān)的Presenter,View,Activity和Fragment,這樣做的好處是可以方便的找到業(yè)務(wù)功能代碼。
我們以addedittask為例:
AddEditTaskActivity:因?yàn)橛昧薋ragment來顯示頁面,Activity沒有過多的邏輯。
AddEditTaskContract:里面包含View和Presenter接口類

public interface AddEditTaskContract {

    interface View extends BaseView<Presenter> {

        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實(shí)現(xiàn)類,業(yè)務(wù)邏輯的實(shí)現(xiàn)

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

架構(gòu)圖

TODO-MVP-Loaders架構(gòu)圖.png

架構(gòu)分析

和TODO-MVP的區(qū)別是通過Loader對數(shù)據(jù)進(jìn)行異步加載,監(jiān)控其數(shù)據(jù)源并在內(nèi)容變化時(shí)傳遞新結(jié)果。關(guān)于Loader的使用,可以直接看官方文檔,我就不復(fù)述了。

TODO-DataBinding

架構(gòu)圖

TODO-DataBinding架構(gòu)圖.png

架構(gòu)分析

和TODO-MVP的區(qū)別是通過Data Binding來綁定app邏輯和layouts文件。關(guān)于Data Binding的使用,可以直接看官方文檔,我就不復(fù)述了。

總結(jié)

通過MVP進(jìn)行業(yè)務(wù)邏輯和視圖的分離,讓View專注于處理數(shù)據(jù)的可視化以及與用戶的交互,同時(shí)讓Repository只關(guān)系數(shù)據(jù)的處理,讓Presenter作為View和Repository之間的紐帶,易于維護(hù)和測試。代碼比較簡單,具體是否使用Loader和Data Binding就看個(gè)人喜好了。雖然Google并沒有把這些當(dāng)做指導(dǎo)文檔,但也算給Android開發(fā)者帶來了一些思路。

參考資料

Android-Architecture
Github
Loader文檔
Data Binding文檔

歡迎關(guān)注我的微博

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容