前言
之前一直搞不明白Android里面的MVP模式是怎么实现的,今天在导师的讲解下豁然开朗,这里要好好感谢一下导师。本文的重点就是通过代码的讲解来实现一个MVP模式下的天气查询的Demo。
准备环境
天气接口还是采用的和风天气的接口,这里给出和风天气的链接,需要的伙伴可以去官网注册使用:https://www.heweather.com/documents/api/v5/now
导入一下必要的包,这里我们使用RxJava+Retrofit来进行网络请求
compile 'com.jakewharton:butterknife:8.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
apt 'com.jakewharton:butterknife-compiler:8.0.0'
还有一个ButterKnife依赖注入框架,具体用法自行Google,用法比较简单
接下来展示具体包接口
接下来讲解每一层的作用:
- model层:用来存放实体类,数据获取的一些类与接口,获取数据的具体操作放在这一层
- presenter:中间层,用来将model层获取到的数据与view进行交互传输,同时可以通过接口监听model层与view层的状态
- view层:用来存放UI控件,处理UI与数据的简单逻辑,将数据绑定至UI等等
具体内容
先看model层里面的具体内容
Weather是实体类,这个就不用看了
WeatherModel 定义 WeatherModelImpl要执行的内容
public interface WeatherModel {
//OnWeatherListener是Presenter层定义的接口,用来监听model层的状态
void loadWeather(String cityName, OnWeatherListener listener);
}
WeatherModelImpl 这是具体的数据操作类包括了网络请求, 这里采用RxJava +Retrofit实现
public class WeatherModelImpl implements WeatherModel {
private Retrofit retrofit;
private static final String key = "81d241f5110048baa04dba7d1fb48948";
@Override
public void loadWeather(String cityName, OnWeatherListener listener) {
retrofit = InitNetWorkRequest.baseRequset();
NetWorkService netWorkService = retrofit.create(NetWorkService.class);
Observable<Weather> observable = netWorkService.getCity(cityName,key);
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Weather>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Weather weather) {
listener.onSuccess(weather); //请求数据成功则调用OnWeatherListener接口的onSuccess()方法将Weather信息发送到Presenter层
}
@Override
public void onError(@NonNull Throwable e) {
listener.onError(); //失败则调用onError()
}
@Override
public void onComplete() {
}
});
}
}
接下来看presenter层
OnWeatherListener 用来监听model层的状态,包括转发数据
public interface OnWeatherListener {
void onSuccess(Weather weather); //成功
void onError(); //出错
}
WeatherPresenter
public interface WeatherPresenter {
//WeatherPresenterImpl要实现的接口,用来控制数据的流向
interface IPresenter{
void getWeather(String cityName);
}
//要和view层绑定的接口,用来获取数据
interface IView {
void onSuccess(Weather weather);
void onFail();
}
}
WeatherPresenterImpl 数据控制流向的具体操作类
public class WeatherPresenterImpl implements WeatherPresenter.IPresenter {
private WeatherPresenter.IView mView;
private WeatherModelImpl model = null;
public WeatherPresenterImpl(WeatherPresenter.IView view ) {
mView = view; //得到view
model = new WeatherModelImpl(); //得到数据操作的实体类
}
@Override
public void getWeather(String cityName) {
model.loadWeather(cityName, new OnWeatherListener() { //绑定监听器
@Override
public void onSuccess(Weather weather) {
mView.onSuccess(weather); //获取Weather信息成功就将信息发送给View
}
@Override
public void onError() {
mView.onFail(); //失败则处理对应逻辑
}
});
}
}
接下来看View层
这里的view层比较简单,就一个Activity
public class MainActivity extends AppCompatActivity implements WeatherPresenter.IView {
private WeatherPresenterImpl mPresenter = null;
@BindView(R.id.et_cityname)
EditText et_cityName;
@BindView(R.id.tv_info)
TextView tv_info;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mPresenter = new WeatherPresenterImpl(this); //实例化presenter
}
@OnClick(R.id.btn_query)
public void onTest(View view){
String cityName = et_cityName.getText().toString();
if(cityName!=null && !cityName.equals("")){
mPresenter.getWeather(cityName.trim());
}else{
Toast.makeText(this,"城市名输入有误!",Toast.LENGTH_SHORT).show();
}
}
@Override
//请求数据成功,进行TextView显示操作
public void onSuccess(Weather weather) {
try {
List<Weather.HeWeather5Bean> list = weather.getHeWeather5();
Weather.HeWeather5Bean.BasicBean basicBean = list.get(0).getBasic();
Weather.HeWeather5Bean.NowBean nowBean = list.get(0).getNow();
tv_info.setText(basicBean.getCity() + "\n" +
basicBean.getCnty() + "\n" +
basicBean.getId() + "\n" +
basicBean.getProv() + "\n" +
nowBean.getFl() + "\n"
);
}catch (NullPointerException e){
Toast.makeText(this,"城市信息错误,请输入正确的城市",Toast.LENGTH_SHORT).show();
}
}
@Override
//请求数据失败则进行失败逻辑处理
public void onFail() {
Toast.makeText(this,"获取天气信息失败",Toast.LENGTH_SHORT).show();
}
}
这里贴出布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.mvpdemo.view.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<EditText
android:id="@+id/et_cityname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.3"
android:hint="请输入城市名"/>
<Button
android:id="@+id/btn_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="查询"/>
</LinearLayout>
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
/>
</LinearLayout>
补上网络请求的代码
InitNetWorkRequest
ublic class InitNetWorkRequest {
public static Retrofit baseRequset(){
return new Retrofit.Builder()
.baseUrl("https://free-api.heweather.com/v5/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
NetWorkService
public interface NetWorkService {
//实况天气,city与key都是请求中的字段
@GET("now")
Observable<Weather> getCity(@Query("city") String city,
@Query("key") String key);
}
另外不要忘了加上权限
<uses-permission android:name="android.permission.INTERNET"/>
运行Demo,效果如图
这样就完成了一个比较标准的MVP项目的Demo