Android MVP 学习
我们都听说过MVC,但是MVP是什么鬼呢?
MVP概念
M对应Model,V对应View,P对应Presenter。
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
MVC与MVP的示意图如下,
我们可以从图中,直观的看出,MVP与MVC的区别。
为什么使用MVP
那么为什么要使用MVP呢?
你是否把所有的代码都写在Activity中,但是,随着代码的维护,你会发现,Activity的代码会变得越来越臃肿,变的越来越难以维护。
这是因为,你把所有的代码都放在了View中,我们就可以把其中的代码分离开来,变成MVP模型。
使用MVP的好处有很多。其中的一个好处是可以使代码分离,变得更加清晰,我们修改界面(View)的时候,其他代码不需要改动(Model和Presenter)。当然好处不止这些。
MVP结构
MVP一般的结构如下,
-IModel,Model的接口,定义数据操作的方法
-Model,实现了Imodel接口
-IView,View的接口,定义页面显示的方法
-View,实现IView接口,一般为Activity
-Presenter
在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model–这就是与MVC很大的不同之处。
接下来,举一个例子,该例子中,从网上获取内容,并在页面上显示。
IView代码
public interface IView {
void setText(String content);//更新界面
}
View的具体实现,
public class MainActivity extends ActionBarActivity implements IView {
private Button btn;
private TextView tv;
private MyPresenter mPresenter;
final String strurl = "http://fabowang.sinaapp.com/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new MyPresenter(this);
btn = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.textView1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mPresenter.printWeb(strurl);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void setText(String content) {//更新页面
// TODO Auto-generated method stub
tv.setText(content);
}
}
IModel代码,
public interface IModel {
void getWeb(String url,IListener listen);//获取网络数据
}
其中IListener是自定义的接口,用来回调,代码如下,
public interface IListener {
void callBack(String result);
}
Model的具体实现,
public class MyModel implements IModel {
private String result = "";
@Override
public void getWeb(final String strurl, final IListener listener) {
result = "";
new Thread() {
public void run() {
URL url = null;
try {
url = new URL(strurl);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
URLConnection conn = url.openConnection();
BufferedReader rd = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line = "";
while ((line = rd.readLine()) != null) {
System.out.println(line);
result = result + line;
}
listener.callBack(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
}
Presenter代码如下,
public class MyPresenter {
private IModel model;
private IView view;
private Handler mHandler = new Handler();
public MyPresenter(IView view) {
super();
this.view = view;
this.model = new MyModel();
}
public void printWeb(String url) {
model.getWeb(url, new IListener() {
@Override
public void callBack(final String result) {
// TODO Auto-generated method stub
mHandler.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//update view
view.setText(result);
}
});
}
});
}
}
Ok,让我们来梳理一下,
Iview定义了更新view的接口,而MainActivity实现了该接口,另外MyPresenter含有Iview的引用,其会调用Iview.setText方法,进而调用MainActivity的具体接口实现,所以实现了更新界面。这是P调用V。
View(即MainActivity)包含一个MyPersenter实例,其可以调用printWeb来得到网络内容,并更新界面。这是V调用P
Model负责数据的获取,MyPresenter包含IModel的引用,其会调用Model的具体实现,得到数据。
由于多线程的问题,我们引入回调接口(IListener),在Model得到网络内容后,回调该接口,并更新UI(调用setText方法)。
不知读者有没有发现,虽然这样,代码感觉复杂了很多,但是结构很清晰,如果我们要修改获取网络数据的代码,我们直接修改Model的实现就好了,其他的代码,我们不需要修改。
本文的代码可以从github上获得,https://github.com/KingPaul/MVPDemo