亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

Android MVP 試水

Original 2016-11-02 16:01:38 714
abstrakt:還記得一年前,在上一家公司的時(shí)候,領(lǐng)導(dǎo)準(zhǔn)備接一個(gè)案子,客戶那邊給了一份開(kāi)發(fā)規(guī)范的文檔,上面明確的寫(xiě)著要采用MVP模式進(jìn)行開(kāi)發(fā)。一開(kāi)始看到這個(gè)模式時(shí)候,一臉懵逼,什么是MVP?不懂,問(wèn)一下同事,也沒(méi)有人能說(shuō)清楚,無(wú)奈那就百度吧。好簡(jiǎn)單粗暴的說(shuō)明啊,還是一臉懵逼。 后來(lái),不知道為什么案子也沒(méi)有接,就這樣不了了之了。最近發(fā)生了很多事,從上一家公司離職,與朋友準(zhǔn)備搞公司,搞了差不多2個(gè)月,到現(xiàn)在

還記得一年前,在上一家公司的時(shí)候,領(lǐng)導(dǎo)準(zhǔn)備接一個(gè)案子,客戶那邊給了一份開(kāi)發(fā)規(guī)范的文檔,上面明確的寫(xiě)著要采用MVP模式進(jìn)行開(kāi)發(fā)。一開(kāi)始看到這個(gè)模式時(shí)候,一臉懵逼,什么是MVP?不懂,問(wèn)一下同事,也沒(méi)有人能說(shuō)清楚,無(wú)奈那就百度吧。

1.png

好簡(jiǎn)單粗暴的說(shuō)明啊,還是一臉懵逼。 后來(lái),不知道為什么案子也沒(méi)有接,就這樣不了了之了。

最近發(fā)生了很多事,從上一家公司離職,與朋友準(zhǔn)備搞公司,搞了差不多2個(gè)月,到現(xiàn)在的從團(tuán)隊(duì)退出。然后準(zhǔn)備找工作。。。。

在這期間搞項(xiàng)目的時(shí)候,就抽空研究了一下MVP模式,試著用它進(jìn)行開(kāi)發(fā)。因?yàn)橹皇且粋€(gè)項(xiàng)目,涉及的還不深,所以叫試水。記錄一下。

網(wǎng)上關(guān)于MVP的介紹、講解、示例以及開(kāi)源的項(xiàng)目很多,我這里就不廢話了,如果現(xiàn)在還有人不了解什么是MVP,那就百度去吧。我這里參考Google的源碼todo-mvp來(lái)說(shuō)。 先看一下目錄結(jié)構(gòu): 

1.png

不要問(wèn)我為什么我截圖的字體顏色是藍(lán)色的,我不會(huì)告訴你我是用的octotree瀏覽器插件。 這里有兩個(gè)Base文件:BaseView、BasePresenter,好像和VP有關(guān),先看一下源碼:

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 ?這是什么鬼??jī)蓚€(gè)接口?干嗎用的?不知道,不明覺(jué)厲。不管了,反正一個(gè)對(duì)應(yīng)的V,一個(gè)對(duì)應(yīng)的是P就是了。好吧,你們估計(jì)再說(shuō)我這不廢話了。這里看不出什么東西,那就從程序的入口看吧,從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>

打開(kāi)tasks包,看到如下幾個(gè)文件

1.png

我的習(xí)慣是先不看里面的內(nèi)容,先看文件名字,大致了解每個(gè)文件是干嘛用的,這樣有助于對(duì)整體進(jìn)行把控,所以這里就體現(xiàn)了命名規(guī)范的重要性,關(guān)于命名規(guī)范,可以百度,也可以參考我的另外一篇文章:Android 開(kāi)發(fā)規(guī)范(個(gè)人版)。哎呀,又扯遠(yuǎn)了,繼續(xù)回來(lái)看代碼。

第一個(gè)文件,應(yīng)該是個(gè)自定義的布局,好像沒(méi)什么太大的關(guān)系。 第二個(gè)主程序的入口,沒(méi)啥說(shuō)的。 第三個(gè)Contract (契約),誰(shuí)和誰(shuí)的,不懂,先不管。 第四個(gè)Filter Type(過(guò)濾器類(lèi)型),應(yīng)該是一些類(lèi)型的定義,好像關(guān)系也不大,先不管。 第五個(gè)Fragment,不說(shuō)了 第六個(gè)Persenter,這個(gè)有關(guān)系,而且還很大,那就先看一下它吧。

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() {
  ...
  }
}

省略了一下不必要的代碼,這里可以看到幾個(gè)關(guān)鍵點(diǎn), 1、TasksPresenter本身實(shí)現(xiàn)了TasksContract.Presenter; 2、構(gòu)造函數(shù)里面需要傳入一個(gè)TasksContract.View; 3、拿到這個(gè)tasksView后賦值給了mTasksView,并把自己通過(guò)mTasksView.setPresenter(this)方法傳遞出去。

到這里算是有點(diǎn)眉目了。知道了P和V是如何綁定在一起的了。 P綁定V:通過(guò)實(shí)例化是傳入V; V綁定P: 通過(guò)v.setPresenter(P);

但如何使用V呢?繼續(xù)往下,這里用到了TasksContract這個(gè)契約,跟蹤一下代碼看一下。

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();
  }
}

看到這里就有點(diǎn)意思了,契約類(lèi)里面主要做了兩件事,

定義了一個(gè)繼承自BaseView的接口(View), 并聲明需要實(shí)現(xiàn)的方法。

定義一個(gè)繼承自BasePresenter的接口并繼承(Presenter),并聲明需要實(shí)現(xiàn)的方法。

其實(shí)也可以說(shuō)是一件事,就是聲明一些接口。

哦,這下知道Contract是干嗎用的了,就是把V、P的接口寫(xiě)到同一個(gè)文件里面啊,好像也并么有什么高大上的東西???那我把這個(gè)文件分成兩個(gè)文件寫(xiě),應(yīng)該也可以吧?我認(rèn)為是可以的。但是又基于Contract的含義即:契約,就是把View和Presenter綁定到一起:

interface Presenter extends BasePresenter {}
interface View extends BaseView<Presenter> {}

這樣還是按照官方的來(lái),用一個(gè)文件來(lái)寫(xiě)好了。

Presenter找到了,Contract也知道是干嗎用的了。那么View呢,從文件名已經(jīng)找不到了,那就看繼續(xù)看代碼吧。從TasksActivity看起,首先我們知道TasksPresenter構(gòu)造函數(shù)里面有一個(gè)TasksContract.View的參數(shù),那么就找這個(gè)參數(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 
    //這個(gè)傳入的是 tasksFragment
    mTasksPresenter = new TasksPresenter(
    Injection.provideTasksRepository(getApplicationContext()), tasksFragment);

   } 
  .......
}

由上面的代碼可知,TasksPresenter 傳入了一個(gè)TasksFragment的對(duì)象,那這樣的TasksFragment就應(yīng)該是所謂的View了,跟蹤進(jìn)入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 實(shí)現(xiàn)了TasksContract.View,就是所謂的View。他的核心點(diǎn)在于: 1、實(shí)現(xiàn)了TasksContract.View; 2、重寫(xiě)setPresenter方法,接收傳遞過(guò)來(lái)的presenter。

這樣之后,就可以通過(guò)presenter.xxxxx()的方式來(lái)調(diào)用presenter里面定義的一些方法,而presenter里面定義的方法主要執(zhí)行耗時(shí)操作或者一些數(shù)據(jù)處理等等,等到presenter里面的函數(shù)執(zhí)行完畢之后,在通過(guò)mTasksView.xxx()的方式回調(diào)給TasksFragment,TasksFragment再進(jìn)行頁(yè)面的改變。

官方給的Demo就看到這里吧,因?yàn)殛P(guān)于MVP核心的東西差不多就看完了,或許還有更多的東西我沒(méi)有發(fā)掘。

根據(jù)官方Demo,我這里總結(jié)了一下實(shí)現(xiàn)MVP模式的步驟:

1、定義BaseView、BasePresenter。可以參考官方示例。 2、定義契約類(lèi),在里面定義兩個(gè)接口,舉個(gè)登錄的例子:

public interface LoginContract {   interface Presenter extends BasePresenter {      /**
       * 登錄
       */
      void login();
   }   interface View extends BaseView<Presenter> {      /** 
        * 返回登錄成功
       */
      void loginSuccess();      void loginFailed(String errorMessage);
   }
}

3、定義一個(gè)實(shí)現(xiàn)契約類(lèi)中Presenter接口的類(lèi),用于實(shí)現(xiàn)邏輯代碼,并把處理結(jié)果返回。例如:

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中實(shí)現(xiàn)契約類(lèi)中的View接口。

要實(shí)現(xiàn)簡(jiǎn)單的MVP,差不多就這4步。接觸的時(shí)間也不長(zhǎng),中間有可能會(huì)出現(xiàn)一些紕漏或者錯(cuò)誤,如果有這方面的牛人在看到這篇文章的時(shí)候,希望能給出寶貴意見(jiàn)。這里先說(shuō)聲謝謝。

關(guān)于MVP,還有很多東西,我看到還有關(guān)Presenter生命周期的相關(guān)文章,還沒(méi)有仔細(xì)研究。這里先記一下。等有時(shí)間在仔細(xì)研究一下。


Versionshinweise

Beliebte Eintr?ge