Google官方架构MVP解析与实战

wbqdavid 8年前
   <h3>1 前言</h3>    <p>当然对于MVP的解说也是使用也是层出不穷,我也网络上也能看到各种版本的解说,之前博客也有文章的更新,里面有MVP的详细说明和项目代码---><a href="http://www.open-open.com/lib/view/open1462626318543.html">Android中的MVP模式,带实例</a>。</p>    <p>本篇文章将参考 google官方android MVP架构项目的实现,来实现自己的项目。或许看了这篇文章之后,你再去梳理一下google官方架构项目,会让你收获更多。官方的实例肯定具有更好的<strong>权威性</strong>。</p>    <p><strong>推荐关注安卓各种架构相关文章合集github地址:<a href="/misc/goto?guid=4959672635985726459">AndroidArchitectureCollection</a></strong></p>    <h3>2 google官方MVP架构解析</h3>    <p>1 项目目录</p>    <p>打开github,展开项目目录,会发现项目结构的组织方式是按照功能进行分模块的,当然根据个人情况,也可以按照ui,model,view,presenter这种情况进行划分组织目录。</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/67a998178e8774ad43f9a6d4cc601d1a.png"></p>    <p>google官方MVP架构目录视图</p>    <p><br> <strong>2 具体实现流程</strong></p>    <p>我们将关注度放到具体的一个taskdetail模块当中来解析实现MVP的流程。</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/b36e24b4b90ed5e8d4eb54dbcab9ebd5.png"></p>    <p>taskdetail模块</p>    <p><br> <strong>2.1 TaskDetailContract</strong><br> 可以看到这里是通过一个协议类XXXContract来对View和Presenter的接口进行继承。这样做的好处也就是,我们可以将基础的View层的操作放在BaseView里面,对基础的Presenter层的操作放在BasePresenter里面。减少后续代码的重复。一个协议类也将View和Presenter管理起来,方便操作。</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/502d82495487ea8cbc26bb62daed8cc4.png"></p>    <p><br> <strong>2.2 BaseView</strong><br> 那么来看看BaseView,主要是有一个setPresenter的操作,MVP中Presenter和View层是需要交互的,这里通过setPresenter操作,我们也就可以获得Presenter层的实例进行交互了。所以在我们自己的代码中,我们也可以将加载的loading,以及加载错误页面,加载失败页面等操作放在BaseView里面,这是每个View都会有的:</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/ed82069554720b1334bf4344d2c771b0.png"></p>    <p><strong>2.3 BasePresenter</strong><br> BasePresenter中只有一个start方法,表示“开始”,我们可以在这里进行数据加载初始化等。</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/44c7f36bef36eb2fe5485f0b59d6c97e.png"></p>    <p><br> <strong>2.3 TaskDetailActivity</strong><br> 可以看到这里这里一个初始化了fragment的activity,主要操作当让是new了一个XXXPresenter。activity在项目中是一个全局的控制者,负责创建view以及presenter实例,并将二者联系起来,</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/39216c15c0366df6112ce06c26e5b884.png"></p>    <p><strong>2.4 TaskDetailFragment</strong><br> Fragment是MVP中View的实现类,它不与Model 层进行交互,只和presenter的实例进行交互。</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/719eb5fb49866160032312dfe1e8964f.png"></p>    <p><br> <strong>2.5 TaskDetailPresenter</strong><br> Presenter的真正实现类,在这里进行model层和view层的交互。</p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/8d1262b34ab7b70fdf85e0ba8fe159b0.png"></p>    <blockquote>     <p>通过上面的分析,在来梳理一下整个步骤:<br> <strong>1 </strong>官方MVP实例,通过协议类XXXContract来对View和Presenter的接口进行内部继承。是对BaseView和BasePresenter的进一步封装,所以我们实现的View和Presenter也只需要继承XXXContract中的对应内部接口就行。</p>    </blockquote>    <p><strong>2 </strong>activity的作用主要是创建View(这里是相应的fragment),以及创建presenter,并把view传递给presenter</p>    <p><strong>3 </strong>在presenter的实现类的构造函数中,通过view的setPresenter,让view获得了presenter实例。这样view中就可以对Presenter中的方法进行操作了。</p>    <p><strong>4 </strong>在presenter的实现类中,可以对Model数据进行操作。实例中,数据的获取、存储、数据状态变化都是model层的任务,presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样model、presenter、view都只处理各自的任务,此种实现确实是单一职责最好的诠释。</p>    <h3>3 实战应用:</h3>    <p>说了这么多,通过一个主页面的搭建,来完整的使用MVP吧。<br> <strong>3.1 BaseView</strong><br> 我在这里没有添加setPresenter方法,而是将loading,以及加载错误,网络加载错误等页面都放在了这里面。</p>    <pre>  <code class="language-java">public interface BaseView {    //    void setPresenter(P presenter);        void showLoading(String msg);        void hideLoading();        void showError(String msg, View.OnClickListener onClickListener);        void showEmpty(String msg, View.OnClickListener onClickListener);        void showEmpty(String msg, View.OnClickListener onClickListener, int imageId);        void showNetError(View.OnClickListener onClickListener);    }</code></pre>    <p><strong>3.2 BasePresenter</strong><br> 只有一个start方法。将在view界面初始化后调用(onResume方法中)</p>    <pre>  <code class="language-java">public interface BasePresenter {      void start();  }</code></pre>    <p><strong>3.3 DrawerMainContract </strong><br> 可以看到相比官方的实例我在其中添加了一个onGetDrawerListListener,主要是用于获取model层数据之后,进行数据的监听。</p>    <pre>  <code class="language-java">public interface DrawerMainContract {        interface Presenter extends BasePresenter{  //获取数据          void getDrawerList();  //头像点击          void onDrawerIconClicked();  //获取fragment          void getSelectFragment(int position);      }        interface View extends BaseView{  //获取数据后,更新界面          void onDrawerListGet(ArrayList<DrawerItemsData.DrawerItem> list);  //设置头像          void setDrawerIcon(int resId);  //fragment返回后          void onSelectFragmentGet(Fragment fragment);        }  //数据监听      interface onGetDrawerListListener {          void onSuccess();          void onError();      }      }</code></pre>    <p><strong>3.4 DrawerMainActivity </strong><br> 在开发中Fragment,Activity以及自定义view都可以作为MVP中View的实现。这里也在创建presenter实例的时候传入了当前view。并调用了 mPresenter.getDrawerList()进行获取数据,mPresenter.getSelectFragment(0);设置当前为第一个fragment。</p>    <pre>  <code class="language-java">/**   * Created by Anthony on 2016/5/3.   * Class Note:   * 1 use{@link DrawerLayout} to   * acts as a top-level container for window content that allows for   * interactive "drawer" views to be pulled out from the edge of the window.   * 2 View in MVP   * see {@link DrawerMainContract}------Manager role of MVP   * {@link DrawerMainPresenter}---------Presenter   * &{@link DrawerMainActivity}---------View   * &{@link DrawerItemsData}------------Model   */  public class DrawerMainActivity extends AbsBaseActivity implements DrawerMainContract.View, View.OnClickListener {      private Toolbar actionBarToolbar;      public static DrawerLayout drawerLayout;      private ListView mDrawerMenu;      private CircleImageView mUserImg;      private NavDrawerListAdapter mAdapter;     private DrawerMainPresenter mPresenter;        @Override      protected int getContentViewID() {          return R.layout.activity_drawer;      }        @Override      protected void onResume() {          super.onResume();          mPresenter.start();      }        @Override      protected void initViewsAndEvents() {          super.initViewsAndEvents();//一定要调用super,进行父类中的一些初始化操作          initDrawerLayout();          setupToolBar();            //hide toolBar          ActionBar actionBar = getSupportActionBar();          if (actionBar != null) {              actionBar.hide();          }            mUserImg = (CircleImageView) findViewById(R.id.user_img);          mUserImg.setOnClickListener(this);          mDrawerMenu = (ListView) findViewById(R.id.left_menu);      //        mPresenter = new DrawerMainPresenter(this, mContext);          //create and initialize presenter,         mPresenter= new DrawerMainPresenter(this,mContext);            //use presenter to load data          mPresenter.getDrawerList();          //default select first fragment          mPresenter.getSelectFragment(0);      }              protected void initDrawerLayout() {          drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);          if (drawerLayout == null) {              // current activity does not have a drawer.              return;          }          if (getDrawerLayoutId() != 0) {              FrameLayout leftLayout = (FrameLayout) findViewById(R.id.left_drawer_layout);              View nav_drawer_layout = getLayoutInflater().inflate(getDrawerLayoutId(), null);              leftLayout.addView(nav_drawer_layout);          }        }          protected void setupToolBar() {          if (actionBarToolbar == null) {              actionBarToolbar = (Toolbar) findViewById(R.id.toolbar);              if (actionBarToolbar != null) {                  setSupportActionBar(actionBarToolbar);              }          }          final ActionBar ab = getSupportActionBar();          if (ab == null)              return;          ab.setHomeAsUpIndicator(R.drawable.ic_menu);          ab.setDisplayHomeAsUpEnabled(true);      }          public static void openDrawer() {          if (drawerLayout == null)              return;          drawerLayout.openDrawer(GravityCompat.START);      }        public static void closeDrawer() {          if (drawerLayout == null)              return;          drawerLayout.closeDrawer(GravityCompat.START);      }        /**       * close drawer if drawer is opening       */      @Override      public void onBackPressed() {          if (drawerLayout.isDrawerOpen(GravityCompat.START)) {              closeDrawer();          } else {              super.onBackPressed();          }      }          protected int getDrawerLayoutId() {          return R.layout.nav_drawer_layout;      }        @Override      public void onClick(View v) {          if (v.getId() == R.id.user_img) {              mPresenter.onDrawerIconClicked();          }      }        @Override      public void onDrawerListGet(final ArrayList<DrawerItemsData.DrawerItem> mDrawerItems) {          mAdapter = new NavDrawerListAdapter(this,                  mDrawerItems);          mDrawerMenu.setAdapter(mAdapter);          mDrawerMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() {              @Override              public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {                  if (!BaseUtil.isEmpty(mDrawerItems, i)) {                      DrawerItemsData.DrawerItem drawerItem = mDrawerItems.get(i);                      if (drawerItem != null) {                          mPresenter.getSelectFragment(i);                      }                  }              }          });      }        @Override      public void setDrawerIcon(int resId) {          mUserImg.setImageResource(resId);      }        @Override      public void onSelectFragmentGet(Fragment fragment) {          closeDrawer();          FragmentManager fragmentManager = getSupportFragmentManager();          fragmentManager.beginTransaction()                  .replace(R.id.content, fragment).commit();        }        @Override      protected void onNetworkConnected(NetUtils.NetType type) {          ToastUtils.getInstance().showToast("this type is"+type);      }        @Override      protected void onNetworkDisConnected() {          ToastUtils.getInstance().showToast("no network disconnected");      }        @Override      protected View getLoadingTargetView() {          return null;      }      //    @Override  //    public void setPresenter(DrawerMainContract.Presenter presenter) {  //        mPresenter= (DrawerMainPresenter) presenter;  //    }        @Override      public void showLoading(String msg) {          toggleShowLoading(true, msg);      }        @Override      public void hideLoading() {          toggleShowLoading(false, "");      }        @Override      public void showError(String msg, View.OnClickListener onClickListener) {        }        @Override      public void showEmpty(String msg, View.OnClickListener onClickListener) {          toggleShowEmpty(true, msg, onClickListener);      }        @Override      public void showEmpty(String msg, View.OnClickListener onClickListener, int imageId) {          toggleShowEmpty(true, msg, onClickListener, imageId);      }        @Override      public void showNetError(View.OnClickListener onClickListener) {          ToastUtils.getInstance().showToast("oops ,no network now!");      }  }</code></pre>    <blockquote>     <p>这里也实现了DrawerMainContract.View以及BaseView中的所有方法。</p>    </blockquote>    <p><strong>3.5 DrawerMainPresenter</strong><br> 对DrawerMainContract.Presenter和DrawerMainContract.<br> onGetDrawerListListener的实现。<br> 可以看到DrawerItemsData就是model层的对象。主要封装的是本地的字符串和图片数据。</p>    <pre>  <code class="language-java">/**   * Created by Anthony on 2016/5/3.   * Class Note: Presenter in MVP   * see {@link DrawerMainContract}--------Manager role of MVP   * &{@link DrawerMainPresenter}------Presenter   * &{@link DrawerMainActivity}-------------View   * &{@link DrawerItemsData}-----------Model   */  public class DrawerMainPresenter implements DrawerMainContract.Presenter, DrawerMainContract.onGetDrawerListListener {        private DrawerMainContract.View mView;      private Context mContext;      private DrawerItemsData mData;          public DrawerMainPresenter(DrawerMainContract.View mView, Context mContext) {          this.mContext = mContext;            this.mView = mView;  //        mView.setPresenter(this);//bind presenter for View            mData = new DrawerItemsData(mContext, this);//bind data listener for Model        }      @Override      public void onDrawerIconClicked() {          //已经登录,跳到个人详情页          ToastUtils.getInstance().showToast("icon clicked");          //没有登录 ,则跳到登录页面。。。      }        @Override      public void getDrawerList() {          mData.initItemsData();      }        @Override      public void getSelectFragment(int position) {          if (position == 3)              mView.onSelectFragmentGet(new NewsChannelFragment());          else mView.onSelectFragmentGet(new TestFragment());      }        @Override      public void onSuccess() {          mView.onDrawerListGet(mData.mDrawerItems);          mView.setDrawerIcon(R.drawable.icon_head);      }        @Override      public void onError() {          // show error view          mView.showNetError(new View.OnClickListener() {              @Override              public void onClick(View v) {              }          });      }      @Override      public void start() {      }  }</code></pre>    <p><strong>3.6 DrawerItemsData</strong><br> 这就是我主页面的model层的数据,当然后面可能会对这些数据进行修改,或者添加上网络访问。但是你会发现再也不用像以前那样要去修改整个activity了,只需要的是修改这个类。这也是MVP的作用,单一职责。</p>    <pre>  <code class="language-java">/**   * Created by Anthony on 2016/5/3.   * Class Note:   * drawer list item data.   *   */  public class DrawerItemsData {      private Context mContext;      public ArrayList<DrawerItem> mDrawerItems;      private String[] mMenuTitles;      private TypedArray mMenuIconsTypeArray;      private TypedArray mMenuIconTintTypeArray;      private DrawerMainContract.onGetDrawerListListener mListener;          public DrawerItemsData(Context context, DrawerMainContract.onGetDrawerListListener listener) {          this.mContext = context;          this.mListener =listener;            mDrawerItems=new ArrayList<>();      }        public void initItemsData() {          mMenuTitles = mContext.getResources().getStringArray(R.array.nav_drawer_items);          // nav drawer icons from resources          mMenuIconsTypeArray = mContext.getResources()                  .obtainTypedArray(R.array.nav_drawer_icons);          mMenuIconTintTypeArray = mContext.getResources()                  .obtainTypedArray(R.array.nav_drawer_tint);            for (int i = 0; i < mMenuTitles.length; i++) {              mDrawerItems.add(new DrawerItem(mMenuTitles[i], mMenuIconsTypeArray                      .getResourceId(i, -1), mMenuIconTintTypeArray.getResourceId(i, -1)));          }          mMenuIconsTypeArray.recycle();            if(mDrawerItems.size()==mMenuTitles.length){              mListener.onSuccess();          }else{              mListener.onError();          }      }        public class DrawerItem {            private String title;          private int icon;          //图片颜色          private int tint;          private String count = "0";          // boolean to set visiblity of the counter          private boolean isCounterVisible = false;            public DrawerItem(){}            public DrawerItem(String title, int icon, int tint){              this.title = title;              this.icon = icon;              this.tint = tint;          }            public DrawerItem(String title, int icon, boolean isCounterVisible, String count){              this.title = title;              this.icon = icon;              this.isCounterVisible = isCounterVisible;              this.count = count;          }            public String getTitle(){              return this.title;          }            public int getIcon(){              return this.icon;          }            public String getCount(){              return this.count;          }            public boolean getCounterVisibility(){              return this.isCounterVisible;          }            public void setTitle(String title){              this.title = title;          }            public void setIcon(int icon){              this.icon = icon;          }            public void setCount(String count){              this.count = count;          }            public int getTint() {              return tint;          }            public void setTint(int tint) {              this.tint = tint;          }            public void setCounterVisibility(boolean isCounterVisible){              this.isCounterVisible = isCounterVisible;          }      }  }</code></pre>    <p>具体请参看<strong>本篇文章项目github地址:<a href="/misc/goto?guid=4959672636079178786">MVPCommon</a></strong></p>    <p><img alt="google官方架构MVP解析与实战" src="https://simg.open-open.com/show/513fd8bf4713b1f597387108a2653552.gif"></p>    <p>项目效果</p>    <h3>4 参考资料:</h3>    <p><a href="http://www.open-open.com/lib/view/open1460535364435.html">Android官方MVP架构示例项目解析</a><br> <strong><a href="/misc/goto?guid=4959672635985726459">AndroidArchitectureCollection</a></strong></p>    <p> </p>    <p><strong>本篇文章项目github地址:<a href="/misc/goto?guid=4959672636079178786">MVPCommon</a></strong></p>    <p>文/<a href="/misc/goto?guid=4959672636187032718">CameloeAnthony</a>(简书)<br>  </p>