你可能听过MVC设计模式,甚至你在各种框架(例:.Net)下都使用了该模式。然而当我尝试用一种新的设计模式去实现我的Android程序的时候,我发现了MVP模式。MVP和MVC最本质的区别是,MVP在present实现UI上的业务逻辑然后通过接口和外部通讯。
下面我将通过例子来展现如何在Android程序里通过MVP模式来提高代码的可测试性(这里主要是单元测试)。我们的例子是这样的,在App要启动的时候,我们需要有个欢迎界面,这个欢迎界面后台运行着初始化数据的方法,在进入app主界面前,我们需要在欢迎界面提供一个进度条以此来检测是否网络环境正常,如果网络环境不ok,那我们显示一个错误页面。如果网络环境ok那就直接进入app主界面。
建启动页的同时,我们同时创建一个presenter来负责model和view的通信。在本例中presenter应该具备两个功能:判断是否网络正常和view的处理,以下是我的工程的结构:
presenter模块会引用model模块,这里的model模块是ConnectStatus,他实现了IConnectStatus接口。
1 package com.sample.mvp.model; 2 3 /** 4 * Created by wuruize on 2015/1/27. 5 */ 6 public interface IConnectStatus { 7 8 public abstract boolean isOnline(); 9 }
和你想象的一样,管理view模块的是实现了ISpalshView接口的Activity。实现了这个接口类将会被presenter控制,如下:
package com.sample.mvp.view; /** * Created by wuruize on 2015/1/27. */ public interface ISpalshView { public abstract void hideProgress(); public abstract void showProgress(); public abstract void showNoInetErroMsg(); public abstract void moveToMain(); }
在编写Android程序的时候,view模块会首先被创建,然后会被交给presenter:
package com.sample.mvp.view.impl; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import com.sample.mvp.R; import com.sample.mvp.presenter.SpalshPresenter; import com.sample.mvp.view.ISpalshView; public class SpalshActivity extends ActionBarActivity implements ISpalshView { private SpalshPresenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPresenter = new SpalshPresenter(this); } @Override protected void onResume() { super.onResume(); mPresenter.didFinishLoading(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void hideProgress() { } @Override public void showProgress() { } @Override public void showNoInetErroMsg() { } @Override public void moveToMain() { } }
首先我们初始化Activity,然后我们把Activity实例给presenter构造presenter实例,重写 onResume() 让presenter模块接管view模块,present代码如下:
package com.sample.mvp.presenter; import com.sample.mvp.model.impl.ConnectStatus; import com.sample.mvp.view.ISpalshView; /** * Created by wuruize on 2015/1/27. */ public class SpalshPresenter { private ISpalshView mSpalshView; private ConnectStatus mStatus; public SpalshPresenter(ISpalshView view) { this.mSpalshView = view; mStatus = new ConnectStatus(); } public void didFinishLoading() { if(mStatus.isOnline()) { } else { mSpalshView.hideProgress(); mSpalshView.showNoInetErroMsg(); } } }
presenter模块会持有实现了ISpalshView接口的引用,实现IConnectStatus的model用于判断网络是否ok。基于此,我们可以通过view模块做不同的事情,和我们看到的一样,我们不需要知道view的具体实现(是被Activity实现还是其他)。如此,降低解耦。能够轻易作修改,进行单元测试。
在单元测试的时候,你是不是有那样一个痛点,由于Activity功能元素功能都较为复杂,想要测试某个具体功能块的话,很浪费时间,降低了开发效率。而如果你使用了MVP模式,不需要依赖于View的具体实现,做到真正的test-driven development.
声明:文章里面的东西基本来自于《50 Android Hacks》理解和翻译,特此感谢此书。