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

Android Developer.png

眾所周知棒呛,Android Framework提供了大量靈活的方式來定義如何組織和架構(gòu)一個Android App缭乘,自由,附有價值秸脱,但同時也導致App擁有了大量的類,不一致的命名和架構(gòu)也給測試部蛇,維護和拓展帶來了困難摊唇。由此帶來了很多的App架構(gòu),MVC涯鲁、MVP巷查,MVVM等,前幾天Google官方推出了關(guān)于Android 架構(gòu)藍圖的Sample抹腿,借此來引導Android開發(fā)者解決這些問題岛请,創(chuàng)建自己的App。重點放在代碼結(jié)構(gòu)警绩,測試和維護崇败。目前推出了3個Sample,接下來還會繼續(xù)推出肩祥,至于在項目中用哪個取決于你自己后室。這些Sample不能算是經(jīng)典例子,但可以作為參考混狠。不過從剛推出就在Github趨勢上位列前茅岸霹,也能感受到大家對此的期待。

Demo運行圖

Demo運行圖.png

TODO-MVP

架構(gòu)圖

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

架構(gòu)解析

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

這個是很經(jīng)典的MVP架構(gòu)将饺,從代碼結(jié)構(gòu)上也能看到一些啟發(fā)贡避,按業(yè)務功能進行劃分痛黎,例如addedittask(新增任務),taskdetail(任務詳情)等贸桶,里面含有相關(guān)的Presenter舅逸,View,Activity和Fragment皇筛,這樣做的好處是可以方便的找到業(yè)務功能代碼琉历。
我們以addedittask為例:
AddEditTaskActivity:因為用了Fragment來顯示頁面,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實現(xiàn)類旗笔,業(yè)務邏輯的實現(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ù)進行異步加載,監(jiān)控其數(shù)據(jù)源并在內(nèi)容變化時傳遞新結(jié)果拄踪。關(guān)于Loader的使用蝇恶,可以直接看官方文檔,我就不復述了惶桐。

TODO-DataBinding

架構(gòu)圖

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

架構(gòu)分析

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

總結(jié)

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

參考資料

Android-Architecture
Github
Loader文檔
Data Binding文檔

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市寂纪,隨后出現(xiàn)的幾起案子席吴,更是在濱河造成了極大的恐慌,老刑警劉巖弊攘,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抢腐,死亡現(xiàn)場離奇詭異姑曙,居然都是意外死亡襟交,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門伤靠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捣域,“玉大人啼染,你說我怎么就攤上這事』烂罚” “怎么了迹鹅?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贞言。 經(jīng)常有香客問我斜棚,道長,這世上最難降的妖魔是什么该窗? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任弟蚀,我火速辦了婚禮,結(jié)果婚禮上酗失,老公的妹妹穿的比我還像新娘义钉。我一直安慰自己,他們只是感情好规肴,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布捶闸。 她就那樣靜靜地躺著,像睡著了一般拖刃。 火紅的嫁衣襯著肌膚如雪删壮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天序调,我揣著相機與錄音醉锅,去河邊找鬼。 笑死发绢,一個胖子當著我的面吹牛硬耍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播边酒,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼经柴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了墩朦?” 一聲冷哼從身側(cè)響起坯认,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氓涣,沒想到半個月后牛哺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡劳吠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年引润,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痒玩。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡淳附,死狀恐怖议慰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奴曙,我是刑警寧澤别凹,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站洽糟,受9級特大地震影響炉菲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坤溃,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一颁督、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浇雹,春花似錦沉御、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烂完,卻和暖如春试疙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抠蚣。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工祝旷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘶窄。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓怀跛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柄冲。 傳聞我的和親對象是個殘疾皇子吻谋,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容