abstract:還記得一年前,在上一家公司的時候,領導準備接一個案子,客戶那邊給了一份開發(fā)規(guī)范的文檔,上面明確的寫著要采用MVP模式進行開發(fā)。一開始看到這個模式時候,一臉懵逼,什么是MVP?不懂,問一下同事,也沒有人能說清楚,無奈那就百度吧。好簡單粗暴的說明啊,還是一臉懵逼。 后來,不知道為什么案子也沒有接,就這樣不了了之了。最近發(fā)生了很多事,從上一家公司離職,與朋友準備搞公司,搞了差不多2個月,到現(xiàn)在
還記得一年前,在上一家公司的時候,領導準備接一個案子,客戶那邊給了一份開發(fā)規(guī)范的文檔,上面明確的寫著要采用MVP模式進行開發(fā)。一開始看到這個模式時候,一臉懵逼,什么是MVP?不懂,問一下同事,也沒有人能說清楚,無奈那就百度吧。
好簡單粗暴的說明啊,還是一臉懵逼。 后來,不知道為什么案子也沒有接,就這樣不了了之了。
最近發(fā)生了很多事,從上一家公司離職,與朋友準備搞公司,搞了差不多2個月,到現(xiàn)在的從團隊退出。然后準備找工作。。。。
在這期間搞項目的時候,就抽空研究了一下MVP模式,試著用它進行開發(fā)。因為只是一個項目,涉及的還不深,所以叫試水。記錄一下。
網上關于MVP的介紹、講解、示例以及開源的項目很多,我這里就不廢話了,如果現(xiàn)在還有人不了解什么是MVP,那就百度去吧。我這里參考Google的源碼todo-mvp來說。 先看一下目錄結構:
不要問我為什么我截圖的字體顏色是藍色的,我不會告訴你我是用的octotree瀏覽器插件。 這里有兩個Base文件:BaseView、BasePresenter,好像和VP有關,先看一下源碼:
package com.example.android.architecture.blueprints.todoapp; public interface BasePresenter { void start(); } package com.example.android.architecture.blueprints.todoapp; public interface BaseView<T> { void setPresenter(T presenter); }
What ?這是什么鬼?兩個接口?干嗎用的?不知道,不明覺厲。不管了,反正一個對應的V,一個對應的是P就是了。好吧,你們估計再說我這不廢話了。這里看不出什么東西,那就從程序的入口看吧,從AndroidManifest.xml中找到程序的入口是tasks/TasksActivity。
<activity android:name="com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity" android:theme="@style/AppTheme.OverlapSystemBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
打開tasks包,看到如下幾個文件
我的習慣是先不看里面的內容,先看文件名字,大致了解每個文件是干嘛用的,這樣有助于對整體進行把控,所以這里就體現(xiàn)了命名規(guī)范的重要性,關于命名規(guī)范,可以百度,也可以參考我的另外一篇文章:Android 開發(fā)規(guī)范(個人版)。哎呀,又扯遠了,繼續(xù)回來看代碼。
第一個文件,應該是個自定義的布局,好像沒什么太大的關系。 第二個主程序的入口,沒啥說的。 第三個Contract (契約),誰和誰的,不懂,先不管。 第四個Filter Type(過濾器類型),應該是一些類型的定義,好像關系也不大,先不管。 第五個Fragment,不說了 第六個Persenter,這個有關系,而且還很大,那就先看一下它吧。
public class TasksPresenter implements TasksContract.Presenter { .... private final TasksContract.View mTasksView; public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { ... mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); } @Override public void start() { ... } }
省略了一下不必要的代碼,這里可以看到幾個關鍵點, 1、TasksPresenter本身實現(xiàn)了TasksContract.Presenter; 2、構造函數(shù)里面需要傳入一個TasksContract.View; 3、拿到這個tasksView后賦值給了mTasksView,并把自己通過mTasksView.setPresenter(this)方法傳遞出去。
到這里算是有點眉目了。知道了P和V是如何綁定在一起的了。 P綁定V:通過實例化是傳入V; V綁定P: 通過v.setPresenter(P);
但如何使用V呢?繼續(xù)往下,這里用到了TasksContract這個契約,跟蹤一下代碼看一下。
package com.example.android.architecture.blueprints.todoapp.tasks;
package com.example.android.architecture.blueprints.todoapp.tasks; import android.support.annotation.NonNull; import com.example.android.architecture.blueprints.todoapp.BaseView; import com.example.android.architecture.blueprints.todoapp.data.Task; import com.example.android.architecture.blueprints.todoapp.BasePresenter; import java.util.List; /** * 這指定 view 和 presenter 之間的 contract。 * This specifies the contract between the view and the presenter. */ public interface TasksContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); void showTasks(List<Task> tasks); void showAddTask(); void showTaskDetailsUi(String taskId); void showTaskMarkedComplete(); void showTaskMarkedActive(); void showCompletedTasksCleared(); void showLoadingTasksError(); void showNoTasks(); void showActiveFilterLabel(); void showCompletedFilterLabel(); void showAllFilterLabel(); void showNoActiveTasks(); void showNoCompletedTasks(); void showSuccessfullySavedMessage(); boolean isActive(); void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); } }
看到這里就有點意思了,契約類里面主要做了兩件事,
定義了一個繼承自BaseView的接口(View), 并聲明需要實現(xiàn)的方法。
定義一個繼承自BasePresenter的接口并繼承(Presenter),并聲明需要實現(xiàn)的方法。
其實也可以說是一件事,就是聲明一些接口。
哦,這下知道Contract是干嗎用的了,就是把V、P的接口寫到同一個文件里面啊,好像也并么有什么高大上的東西???那我把這個文件分成兩個文件寫,應該也可以吧?我認為是可以的。但是又基于Contract的含義即:契約,就是把View和Presenter綁定到一起:
interface Presenter extends BasePresenter {} interface View extends BaseView<Presenter> {}
這樣還是按照官方的來,用一個文件來寫好了。
Presenter找到了,Contract也知道是干嗎用的了。那么View呢,從文件名已經找不到了,那就看繼續(xù)看代碼吧。從TasksActivity看起,首先我們知道TasksPresenter構造函數(shù)里面有一個TasksContract.View的參數(shù),那么就找這個參數(shù)傳的什么。
public class TasksActivity extends AppCompatActivity { .... private TasksPresenter mTasksPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); .... TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); } ..... // Create the presenter //這個傳入的是 tasksFragment mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment); } ....... }
由上面的代碼可知,TasksPresenter 傳入了一個TasksFragment的對象,那這樣的TasksFragment就應該是所謂的View了,跟蹤進入TasksFragment。
public class TasksFragment extends Fragment implements TasksContract.View {private TasksContract.Presenter mPresenter; ......public TasksFragment() {// Requires empty public constructor}public static TasksFragment newInstance() {return new TasksFragment(); } ......@Overridepublic void onResume() {super.onResume(); mPresenter.start(); }@Overridepublic void setPresenter(@NonNull TasksContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } ....@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ..... } ....... }
果然,TasksFragment 實現(xiàn)了TasksContract.View,就是所謂的View。他的核心點在于: 1、實現(xiàn)了TasksContract.View; 2、重寫setPresenter方法,接收傳遞過來的presenter。
這樣之后,就可以通過presenter.xxxxx()的方式來調用presenter里面定義的一些方法,而presenter里面定義的方法主要執(zhí)行耗時操作或者一些數(shù)據(jù)處理等等,等到presenter里面的函數(shù)執(zhí)行完畢之后,在通過mTasksView.xxx()的方式回調給TasksFragment,TasksFragment再進行頁面的改變。
官方給的Demo就看到這里吧,因為關于MVP核心的東西差不多就看完了,或許還有更多的東西我沒有發(fā)掘。
根據(jù)官方Demo,我這里總結了一下實現(xiàn)MVP模式的步驟:
1、定義BaseView、BasePresenter??梢詤⒖脊俜绞纠?2、定義契約類,在里面定義兩個接口,舉個登錄的例子:
public interface LoginContract { interface Presenter extends BasePresenter { /** * 登錄 */ void login(); } interface View extends BaseView<Presenter> { /** * 返回登錄成功 */ void loginSuccess(); void loginFailed(String errorMessage); } }
3、定義一個實現(xiàn)契約類中Presenter接口的類,用于實現(xiàn)邏輯代碼,并把處理結果返回。例如:
public class LoginPresenter implements LoginContract.Presenter { private LoginContract.View view; public LoginPresenter(LoginContract.View view) { this.view = view; view.setPresenter(this); } /** * 登錄 */ @Override public void login() { String useName = view.getUserName(); String pwd = view.getPwd(); Map<String, String> params = new HashMap<String, String>(); params.put("phone", useName); params.put("password", pwd); AuthRequestUtil.doLogin(params, User.class, new ResponseCallBack<User>() { @Override public void onSuccess(User data) { super.onSuccess(data); saveLoginInfo(data); //返回登錄成功 view.loginSuccess(); } @Override public void onFailure(ServiceException e) { super.onFailure(e); //返回登錄失敗 view.loginFailed(e.getMessage()); } }); } }
4、在Activity 或者Fragment中實現(xiàn)契約類中的View接口。
要實現(xiàn)簡單的MVP,差不多就這4步。接觸的時間也不長,中間有可能會出現(xiàn)一些紕漏或者錯誤,如果有這方面的牛人在看到這篇文章的時候,希望能給出寶貴意見。這里先說聲謝謝。
關于MVP,還有很多東西,我看到還有關Presenter生命周期的相關文章,還沒有仔細研究。這里先記一下。等有時間在仔細研究一下。