Android应用的MVC架构,Activity往往充当了View和Control双重角色,造成代码耦合性较强。怎样将View和Control解耦呢,可以使用MVP架构(Model、Control、Prestener)将Activity的View和Control彻底分离,不说废话了直接上代码吧!
github:
以Activity为例,Fragment的原理相同:
View:我们将Activity看作一个单纯的View
package com.allin.android.activity;import com.allin.android.presenter.BasePrestener;import android.app.Activity;import android.os.Bundle;/** * MVP架构中Activity的基类,主要用于与Prestener建立关联并管理生命周期 * @author Allin * 2015.11.24 * @paramBaseActivity子类需继承的View接口,该接口声明了Activity的各种视图操作逻辑, MVP中的V * @param BasePrestener子类, MVP中的P */public abstract class BaseActivity
> extends Activity { protected P mPrestener; //持有一个Prestener对象的引用,用来调用Prestener中的数据逻辑 @SuppressWarnings("unchecked") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPrestener = createPrestener(); mPrestener.attachView((V)this); } @Override protected void onDestroy() { super.onDestroy(); mPrestener.detachView(); } protected abstract P createPrestener(); //mPrestener对象延迟到子类生产 }
Prestener:中介者用来处理数据逻辑和View中视图逻辑的流程控制
package com.allin.android.presenter;import java.lang.ref.Reference;import java.lang.ref.WeakReference;/** * MVP架构中Prestener的基类,主要用于与View建立关联并管理生命周期 * @author Allin * 2015.11.24 * @paramView接口 */public abstract class BasePrestener { protected Reference mViewRef; //持有一个V的引用,用来调用Activity或Fragment中的视图逻辑 public void attachView(V view){ mViewRef = new WeakReference (view); //此处使用弱引用,防止因Activity或Fragment异常关闭造成内存泄露 } protected V getView(){ return mViewRef.get(); } public boolean isViewAttached(){ return mViewRef != null && mViewRef.get() != null; } public void detachView(){ if(mViewRef != null){ mViewRef.clear(); mViewRef = null; } } }
至此,V和C已经完全解耦。
为了避免Prestener臃肿,我们可以将具体逻辑进一步分离,以联网Api为例:
package com.allin.android.api;import java.util.Map;/**Api基类 * @author Allin * 2015.11.24 */class BaseApi { /** * http get请求 * @param url * @param params * @param callback 将网络获取的数据回调给调用者 */ public void httpGet(final String url, final Map params, final ApiCallback callback){ // TODO Auto-generated method stub } /** * http post请求 * @param url * @param params * @param callback 将网络获取的数据回调给调用者 */ public void httpPost(final String url, final Map params, final ApiCallback callback){ // TODO Auto-generated method stub }}
Api回调接口:
package com.allin.android.api;/** * ApiCallback * @author Allin * 2015.11.24 */public interface ApiCallback { /** * 网络请求成功的回调 * @param result */void onSucceed(T result); /** * 网络请求失败的回调 * @param msg */ void onFailed(String msg);}
用法举栗,写一个Activity包含三个功能:创建长度为length的无序数组用ListView展示出来、排序并刷新ListView、统计有多少项的值为value
首先写一个ViewListener声明Activity里面的方法:
package com.allin.android.listener;/** * MVPViewListener * @author Allin * 2015.11.24 */public interface MVPViewListener extends BaseViewListener{ /** * 创建无序数组并在列表显示 * @param array */ void showList(int[] array); /** * 排序然后刷新列表 */ void sort(); /** * 统计有多少项的值为value * @param value */ void count(int value);}
然后是Activity和Prestener的交互,先看Activity
package com.allin.android.activity;import com.allin.android.adapter.MVPAdapter;import com.allin.android.allin.R;import com.allin.android.listener.MVPViewListener;import com.allin.android.presenter.MVPPrestener;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.EditText;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;/** * MVPActivity * @author Allin * 2015.11.24 */public class MVPActivity extends BaseActivityimplements MVPViewListener, OnClickListener{ private ListView mListView; private EditText mEditText; private TextView mCountText; private TextView mSortText; private MVPAdapter mAdapter; /** * 父类BasePrestener的抽象函数,必须在子类中实现,用来创建具体的Prestener对象 * Prestener对象及其生命周期全部交给父类管理,子类只需要创建实例就可以了 */ @Override protected MVPPrestener createPrestener() { return new MVPPrestener(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mvp); initView(savedInstanceState); initData(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.count: String edit = mEditText.getText().toString(); int value = Integer.parseInt(edit); count(value); break; case R.id.sort: sort(); break; default: break; } } @Override public void initView(Bundle savedInstanceState) { mListView = (ListView) findViewById(R.id.list); mEditText = (EditText) findViewById(R.id.edit); mCountText = (TextView) findViewById(R.id.count); mSortText = (TextView) findViewById(R.id.sort); mCountText.setOnClickListener(this); mSortText.setOnClickListener(this); } @Override public void initData() { mPrestener.init(); //数据逻辑交给Prestener去处理 } @Override public void showList(int[] array) { mAdapter = new MVPAdapter(array); mListView.setAdapter(mAdapter); } @Override public void sort() { mPrestener.sort(); //数据逻辑交给Prestener去处理 } @Override public void count(int value) { int count = mPrestener.count(value); //数据逻辑交给Prestener去处理 Toast.makeText(this, String.valueOf(count), Toast.LENGTH_SHORT).show(); }}
Prestener
package com.allin.android.presenter;import com.allin.android.algorithms.Sort;import com.allin.android.listener.MVPViewListener;import com.allin.android.util.Factory;/** * MVPPrestener * @author Allin * 2015.11.24 */public class MVPPrestener extends BasePrestener{ private int[] array; public void init(){ array = Factory.createArray(1000); getView().showList(array); } public void sort(){ Sort.sort(array); getView().showList(array); } public int count(int value){ int count = 0; for (int i = 0; i < array.length; i++) { if(value == array[i]){ count++; } } return count; }}
视图与数据逻辑分离之后,两边的代码都十分干净清爽,而且方便维护。缺点在于对开发人员的接口设计能力要求比较高