Android 架構選型 (MVP+DataBinding)

free46000發表於2017-06-14

通過程式碼對比,詳細講解MVC,MVP,MVVM之間應該如何選擇,以及對Android單元測試的探索。本文的側重點在於如何選擇,並沒有對每種架構模式概念展開詳解(網路上這方面的文章有很多,大家可以自行搜尋)。

大綱

  • MVC or MVP or MVVM?
  • 確定選型:MVP + DataBinding
  • 單元測試(探索階段)

目的

  • 提高開發效率
  • 易於測試
  • 擁抱變化
  • 降低維護成本

Android中的MVC

示例:

展示任務詳情的功能,詳情View層的通過xml來寫的,請求資料相關的程式碼會在Model層提供介面,然後通過Activity對View和Model層進行連線。

程式碼:

M:
public interface TaskModel {
    void loadTask(String taskId, OnTaskListener listener);
}

V:
taskdetail.xml:介面佈局檔案,採用XML進行描述,屬於V層的一部分。

C & V:
TaskDetailActivity:C層和V層
public class TaskDetailActivity {
    private void initView() {
        taskModel.loadTask(taskId, new OnTaskListener() {
            public void onSuccess(Task task) {
                //V層程式碼,但是目前耦合到了C層            
                detailTitle.setText(task.getTitle);
            }
        });
    }  
}複製程式碼

總結:

  • 編寫簡單快速,適合含有簡單邏輯的業務,或是demo程式。
  • Activity的臃腫:xml作為view層,控制能力太弱,無法動態的改變頁面的內容,只能把程式碼寫在activity中,造成了activity既是Controller層,又是View層的問題。
  • 耦合度較高,需求變化改動大,後續維護成本高。
  • Controller混雜著Android程式碼無法Junit。

Android中的MVP

示例:

展示任務詳情的功能,詳情View層的通過xml和Activity來完成,請求資料相關的程式碼會在Model層提供介面,然後通過Presenter對View和Model層進行連線。

程式碼:

M:
public interface TaskModel { 
    void loadTask(String taskId, OnTaskListener listener);
}

V:
taskdetail.xml:介面佈局檔案,採用XML進行描述,屬於V層的一部分。
public class TaskDetailActivity implements TaskDetailView {
    public void showTask(Task task) {
        detailTitle.setText(task.getTitle);
    }  
}

P:
public class TaskDetailPresenter implements Presenter {
    public void getTask() {
        taskModel.loadTask(taskId, new OnTaskListener() {
            public void onSuccess(Task task) {
                //通過介面回撥到V層更新UI
                taskDetailView.showTask(task);
            }
        });
    }  
}複製程式碼

總結:

  • 減少各層之間耦合,易於後續的需求變化,降低維護成本。
  • Presenter層獨立於Android程式碼之外,可以進行Junit測試。
  • 介面和類較多,互相做回撥,程式碼臃腫。
  • Presenter層與View層是通過介面進行互動的,介面粒度不好控制。

Android中的MVVM

示例:

展示任務詳情的功能,詳情View層的通過xml和Activity來完成,請求資料相關的程式碼會在Model層提供介面,然後通過ViewModel對View和Model層進行連線。

程式碼:

M:
public interface TaskModel  ...
    void loadTask(String taskId, OnTaskListener listener);
V:
taskdetail.xml:介面佈局檔案,採用XML進行描述,繫結規則在xml中進行定義。
TaskDetailActivity:Activity主要是初始化和補充的功能。
VM:
TaskDetailViewModel {
        public void getTask() {
              taskModel.loadTask(taskId, new OnTaskListener() {
                    public void onSuccess(Task task) {
                        //通過繫結技術更新UI,做到資料獨立於UI
                        taskDeatailViewBinding.setTask(task);
            }
              });
        }  
}複製程式碼

總結:

  • 和MVP比較像,主要區別在於View和ViewModel / Presenter之間的通訊
  • 相比MVP優勢是通過DataBinding技術為VM和V層進行資料繫結,提高開發效率,由於目前繫結技術的侷限,V層一些介面的處理還是需要Activity的輔助。
  • VM層摻雜Android程式碼無法進行Junit測試。

確定選型

通過以上對比,選擇了MVP+DataBinding,此架構模式基於MVP,並使用DataBinding庫來顯示資料並繫結View。它並不遵循嚴格的MVVM或MVP模式,因為它同時使用了ViewModel和Presenter。

DataBinding

這是我上篇文章我們為什麼要使用DataBinding,裡面通過程式碼的對比,總結說明為什麼要使用DataBinding的技術,有興趣的同學可以閱讀一下,在這裡我把文章裡的一小段總結貼出來:

DataBinding為資料驅動:資料變化後自動更新UI;事件處理:直接找到目標例項處理使用者操作的事件。這樣我們就不需要和UI或者控制元件打交道,只需要在java程式碼中處理業務邏輯就好了,非常清晰,其餘的統一交給binding庫去完成。降低了程式碼耦合度,使得資料獨立於UI,對以後程式的變化和維護都有積極的影響。

MVP+DataBinding

示例:

展示任務詳情的功能,資料和事件繫結與基礎MVP程式碼的對比。

程式碼:

//普通MVP   任務詳情程式碼示例:
public void onCreateView(...) {
    detailTitle = (TextView) root.findViewById(R.id.task_detail_title);
    detailComplete = (CheckBox) root.findViewById(R.id.task_detail_complete);
    detailComplete.setOnCheckedChangeListener(
           (cb, isChecked) -> presenter.completeChanged(task, isChecked)
    );
}
@Override
public void showDescription(String title) {
    detailTitle.setText(title);
}
//xml檔案省略...

//MVP+DataBinding   任務詳情程式碼示例:
@Override
public void showTask(Task task) {
    viewDataBinding.setTask(task);
}
//可以通過佈局檔案直接繫結到資料模型的屬性(xml檔案)
<TextView
    android:id="@+id/task_detail_title"
    android:text="@{task.title}" />
//事件繫結同樣可以直接在佈局檔案中實現(xml檔案)
<CheckBox
    android:id="@+id/task_detail_complete"
    android:checked="@{task.completed}"
    android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />複製程式碼

總結:

  • DataBinding庫提高了開發效率,使得xml佈局檔案用於將資料繫結到UI元素,也可以繫結一個action handler(Presenter)處理使用者操作的事件,可以觀察和設定資料,以便在需要時自動更新(雙向繫結)。
  • 需要對View和Presenter兩層做測試,增加工作量。
  • 目前Android Studio對Databing的支援不是太好(報錯和程式碼自動生成)

單元測試(探索階段)

Presenter

不需要Android環境,因此使用Junit測試即可

  • JUnit:Java語言的單元測試框架
  • Mockito:模擬物件的測試框架

View

使用Google建議的Espresso進行UI的測試(需要依賴Android環境)

  • AndroidJUnitRunner:Android 且與 JUnit 4 相容的測試執行器
  • Espresso:功能性 UI 測試框架

問題

就目前實踐過程中,會發現需要為測試寫一些額外的方法,不是太舒服。並且寫兩層測試,工作量變大。還有覆蓋率以及維護的問題,一直在探索最佳實踐,會在以後的文章裡面和大家分享。

本片文章來自於自己的程式設計實戰,寫的不好的地方請大家幫忙指正,希望能幫助大家選到合適自己的架構模式。
謝謝閱讀。

相關文章