1 MVC架构
MVC架构介绍
现在的Android程序大多采用MVC的架构,MVC的架构软件可以分为三个部分:
- 视图(View): 用户界面
- 控制器(Controller) : 业务逻辑
- 模型(Model) : 数据保存
MVC架构的核心如下图所示:
View传送指令到Controller, Controller完成业务逻辑后,要求Model改变状态,Model将新的数据发送到View,用户得到反馈。
MVC架构存在的问题
在MVC架构中,一般XML布局文件和Activity的setContentView等充当了View角色,Activity的其他代码充当Controller角色,其他数据来源充当了Model角色。Activity担当的责任非常之重,而且违背了单一职责原则。
MVP架构
MVP架构的软件可以分为三个部分:
- 模型(Model) 用来做实际数据的操作,又是也需要创建一个Model的抽象接口Model Interface来降低耦合。
- 视图(View) 也就是Android的Activity,同事需要创建一个View的抽象接口View Interface,需要View实现接口,View通过View Interface与Presenter进行交互,降低耦合。
- 表示层(Presenter): 作为View与Model交互的中间纽带,处理与用户交互的交互逻辑。
下图展示了MVC架构的核心:
MVC和MVP对比
MVP架构:
- View不直接与Model交互,而是通过与中间层Presenter交互来与Model间接交互
- Presenter与View的交互是通过接口来进行的
- 通常View与Presenter与一对一的
MVC架构:
- View可以与Model直接交互
- Controller是基于行为的,可以被多个View共享
MVP是从经典的MVC演变而来, 它们的基本思想是有想通的地方:Controller/Presenter负责逻辑的处理,MOdel提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不能直接使用Model,它们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过Controller。
MVP架构的例子
接下来,我们将通过一个使用MVP网络请求天气预报的demo来讲解MVP架构。我们的网络请求使用OKHttp框架来做。
项目github地址: https://github.com/chengshuyuan/MVPTest.git
程序运行图
工程总共包括四个包
bean: 包含一个WeatherInfo实体类
model:项目的model层,包含一个网络请求天气预报信息的接口与实现类。
presenter: 表示层
view: 项目的view层,包括一个activity和一个view接口。
如图
1 实体类
实体类很简单,不解释
package com.secondclass.mvptest.bean;
public class WeatherInfo {
private String city;
private String cityid;
private String temp1;
private String temp2;
private String weather;
private String ptime;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCityId() {
return cityid;
}
public void setCityId(String cityId) {
this.cityid = cityId;
}
public String getTemp1() {
return temp1;
}
public void setTemp1(String temp1) {
this.temp1 = temp1;
}
public String getTemp2() {
return temp2;
}
public void setTemp2(String temp2) {
this.temp2 = temp2;
}
public String getPtime() {
return ptime;
}
public void setPtime(String ptime) {
this.ptime = ptime;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
public String toString(){
return city + temp1 + temp2 + weather + ptime;
}
}
2 Model层
在业务层,我们抽取了一个接口,并在实现类中采用OKHttp进行网络的请求和解析,网络请求是一个耗时操作,而OKHttp框架新开了线程来进行网络的操作。
接口
package com.secondclass.mvptest.model;
public interface IWeatherModel {
public void getWeather(String cityName, GetWeatherListener listener);
}
实现类
package com.secondclass.mvptest.model;
import com.google.gson.Gson;
import com.secondclass.mvptest.bean.WeatherInfo;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class WeatherModel implements IWeatherModel {
OkHttpClient client = new OkHttpClient();
private WeatherInfo weatherInfo;
@Override
public void getWeather(String cityName, final GetWeatherListener listener) {
final Request request = new Request.Builder()
//这里只做一个模拟
.url("http://www.weather.com.cn/data/cityinfo/101010100.html")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
listener.getFailed();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseStr = response.body().string();
try {
JSONObject jsonObject = new JSONObject(responseStr);
JSONObject weatherJson = jsonObject.optJSONObject("weatherinfo");
Gson gson = new Gson();
weatherInfo = gson.fromJson(weatherJson.toString(), WeatherInfo.class);
listener.getSuccess(weatherInfo);
}catch (Exception e){
e.printStackTrace();
listener.getFailed();
}
}
});
}
}
3 Presenter
package com.secondclass.mvptest.presenter;
import android.os.Handler;
import android.os.Looper;
import com.secondclass.mvptest.View.IWeatherView;
import com.secondclass.mvptest.bean.WeatherInfo;
import com.secondclass.mvptest.model.GetWeatherListener;
import com.secondclass.mvptest.model.IWeatherModel;
import com.secondclass.mvptest.model.WeatherModel;
public class WeatherPresenter {
private IWeatherView weatherView;
private IWeatherModel weatherModel;
private Handler handler = new Handler(Looper.getMainLooper());
public WeatherPresenter(IWeatherView weatherView){
this.weatherModel = new WeatherModel();
this.weatherView = weatherView;
}
public void getWeather(String cityName){
weatherModel.getWeather(cityName, new GetWeatherListener() {
@Override
public void getSuccess(final WeatherInfo weatherInfo) {
handler.post(new Runnable() {
@Override
public void run() {
weatherView.setWeather(weatherInfo);
}
});
}
@Override
public void getFailed() {
}
});
}
}
这里需要注意的是OKhttp进行网络请求是在其他的线程,所以想要更新UI的时候,需要调用绑定ui线程的Handler类的post方法。
4 View
View的接口:
package com.secondclass.mvptest.View;
import com.secondclass.mvptest.bean.WeatherInfo;
public interface IWeatherView {
public void setWeather(WeatherInfo weatherInfo);
}
View的实现类, 也就是activity
package com.secondclass.mvptest.View;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.secondclass.mvptest.R;
import com.secondclass.mvptest.bean.WeatherInfo;
import com.secondclass.mvptest.presenter.WeatherPresenter;
public class WeatherActivity extends AppCompatActivity implements IWeatherView{
private EditText et_cityName;
private Button btn_submit;
TextView tv_temp1, tv_temp2, tv_weather, tv_time;
private String cityName;
private WeatherPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
presenter = new WeatherPresenter(this);
initView();
}
private void initView(){
et_cityName = (EditText)this.findViewById(R.id.et_cityname);
btn_submit = (Button)this.findViewById(R.id.btn_submit);
tv_temp1 = (TextView)this.findViewById(R.id.tv_temp1);
tv_temp2 = (TextView)this.findViewById(R.id.tv_temp2);
tv_weather = (TextView)this.findViewById(R.id.tv_weather);
tv_time = (TextView)this.findViewById(R.id.tv_time);
btn_submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
cityName = et_cityName.getText().toString().trim();
if(!TextUtils.isEmpty(cityName)) {
presenter.getWeather(cityName);
}
}
});
}
@Override
public void setWeather(final WeatherInfo weatherInfo) {
tv_temp1.setText(weatherInfo.getTemp1());
tv_temp2.setText(weatherInfo.getTemp2());
tv_weather.setText(weatherInfo.getWeather());
tv_time.setText(weatherInfo.getPtime());
}
}