來認識一下谷歌推薦的 MVP 寫法
先說點題外話,最近報名了簡書的日更計劃,所以每天都會寫一篇文章,字數不會太多,但是內容都會經過認真準備的,希望大家也能從中學到一些有用的知識,一起進步。
今天介紹的是谷歌推薦的 MVP
寫法,它的專案地址是 ofollow,noindex">android-architecture 。
我對它的主要程式碼結構做了一個截圖,今天就下面這種圖來認識一下規範的 MVP
寫法。

程式碼結構
從圖中我們可以看出它的程式碼結構是 按功能模組來分包
資料是放在 data
下,工具類是放在 utils
下。
它使用的 MVP 架構通過閱讀原始碼可以知道是如下的架構:

谷歌例子中的 MVP 架構
根據上邊的程式碼結構和截圖,今天就從以下幾個方面來分析:
- 第一部分是資料部分在
data
包下 - 第二部分是檢視和中間人部分在
對應的功能模組
包下 - 第三部分是
BaseView
和BasePresenter
- 最後是閱讀別人程式碼學到的一些程式設計思路
第一部分也就是資料部分
在 MVP
中的 M
表示的是 Model
也就是資料。檢視谷歌這個例子中,它的 model
是這樣去定義的,先定義 model
的介面,然後再定義介面的具體實現類。
這也屬於面向物件開發的思路,先把物件的屬性和對應的方法,也就是先做設計,然後再去做具體的開發。
簡單貼一下例子中的原始碼,具體原始碼可 點選檢視 :
介面定義 public interface TasksDataSource { interface LoadTasksCallback { void onTasksLoaded(List<Task> tasks); void onDataNotAvailable(); } ... void completeTask(@NonNull Task task); void completeTask(@NonNull String taskId); ... void deleteTask(@NonNull String taskId); } 介面實現 public class TasksRepository implements TasksDataSource { ... // Prevent direct instantiation. private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource, @NonNull TasksDataSource tasksLocalDataSource) { mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource); mTasksLocalDataSource = checkNotNull(tasksLocalDataSource); } ... }
Model
中主要做的就是資料的 增刪改查操作
,上面的程式碼中有一個 checkNotNull
方法,該方法在 support
包下也有,我這裡貼一下原始碼看看原始碼中是如何判空的
public static <T> T checkNotNull(T reference) { if (reference == null) { throw new NullPointerException(); } return reference; }
看完之後是不是覺得很簡單,但是我們平時就是想不到,所以多閱讀別人優秀的原始碼是多麼重要啊。
第二部分是檢視(View)和 中間人(Presenter)
在這裡它使用了一個 契約類 TasksContract
來存放檢視和中間人。這樣做可以減少類的建立,便於管理和維護。在該契約類中主要是設計 View
要中要實現的方法和 Presenter
中要實現的方法,也屬於面向物件中的先設計後再具體實現。
簡要程式碼如下,具體程式碼可 點選檢視 :
public interface TasksContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); ... void showCompletedFilterLabel(); ... void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); ... void completeTask(@NonNull Task completedTask); ... TasksFilterType getFiltering(); } }
設計完了介面下面就是具體的實現了。再貼一下開頭的 MVP
結構圖

谷歌例子中的 MVP 架構
再看一下 TasksPresenter
的實現:這塊是核心,理解了這裡就基本上理解了這個例子中的 MVP 架構
public class TasksPresenter implements TasksContract.Presenter { // 這裡是 我們前面定義的 Model 具體實現 private final TasksRepository mTasksRepository; // 這裡是 我們前面定義的 View 介面 private final TasksContract.View mTasksView; // 這裡是 通過構造方法將 Model 和 View 注入到 Presenter 中 public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); // 這裡通過介面注入的方式將 Presenter 注入到 View 中,這裡的 setPresenter 是 BaseView 介面中的 方法 mTasksView.setPresenter(this); } // 所以這就對應上了上圖的框架結構圖 ... } ... }
上面的程式碼中講到了構造方法注入和介面注入,是為了實現類和類之間的解耦,用到的兩種 依賴注入
手段。這個知識點我還在整理中過幾天也會跟進完善。
以上就是谷歌 MVP
例子的簡單理解,網上有很多很詳細的介紹部落格,想深入瞭解的可以去搜一下。
在閱讀以上原始碼的時候我還發現了 Activity中是不做UI操作的,都放到 Fragment 中去實現 ,我想了下,這可能也是一種好的開發習慣吧,因為 Fragment
在平板上相容性會更好一些。網上說更好的方式是動態去新增 Fragment
這裡由於只有一個頁面,所以它直接在 activity 的 xml 檔案中寫死了Fragment
這是它將 Fragment
新增到 activity
中的一個工具類,在新增之前還做了一層判空操作,真的是好習慣。
public class ActivityUtils { /** * The {@code fragment} is added to the container view with id {@code frameId}. The operation is * performed by the {@code fragmentManager}. * */ public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager, @NonNull Fragment fragment, int frameId) { checkNotNull(fragmentManager); checkNotNull(fragment); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(frameId, fragment); transaction.commit(); } }
而且它在 Activity
中獲取該 Fragment
例項的時候又做了一次判空操作。如下:
TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); }
文章到這裡就結束了。
閱讀這個例子不僅看懂了 MVP
架構,還學到了一些別人寫程式碼的好習慣,以後得多多閱讀別人優秀的程式碼了,加油!