现状很多的公司在面试的时候都会要求使用MVP模式来开发,像这种模式开发有很多好处,比如代码结构清晰,代码维护非常方便等等,但是也有不好的地方,比如,逻辑比较复杂,如果太多的代码使用MVP模式,可能会导致项目庞大臃肿,但是由于趋势在那里,所以我们必须要习惯去使用这些东西,包括retrofit+rxjava的网络框架,作为一名安卓开发和即将进入ios开发的工程师来说,我给自己定的标准是,以后所有的开发都必须用MVP模式来写。
.mvp模式的介绍:
这张图在熟悉不过了,以前我们的开发模式都是把所有的内容全部放在activity里面,这样导致了部分activity几千行代码,这样逻辑很混乱,修改代码也比较麻烦。
MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。
这就是MVP模式,现在这样的话,Activity的工作的简单了,只用来响应生命周期,其他工作都丢到Presenter中去完成。从上图可以看出,Presenter是Model和View之间的桥梁,为了让结构变得更加简单,View并不能直接对Model进行操作。
。。。。。
太多的文字描述不是我擅长的,网上多的是,我也不多说了,我写这篇文章的主要目的就是教大家如何去写一个mvp模式的完整的界面及逻辑。
编写过程
功能分析:
第一步:建好文件夹,分好类
第二步:创建一个model、view、presenter类,主要用来处理逻辑,至于view其实就是activity,不过为了方便抽取,我们先弄一个view的接口,一个model接口,一个presenter接口,分别对应一个实现类
好了,基本的模板完成,现在正是进入功能分析阶段(其实就是把逻辑分开放到这些类中,便于管理及后续优化)
第一:view中只写页面的处理,我们看到有三个状态。
1.显示天气数据。
2.button点击处理。
3.progressbar的显示与隐藏。
局部完成后的view代码:
package com.huhai.mvp_retrofit_rxjva_demo.view; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import com.huhai.mvp_retrofit_rxjva_demo.R; import com.huhai.mvp_retrofit_rxjva_demo.presenter.WeatherPresenterImpl; public class MainActivity extends AppCompatActivity implements IWeatherView,View.OnClickListener { private TextView mShow; private Button mButton; private ProgressBar mProgress; private WeatherPresenterImpl mPresenter; //气象局天气接口 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /*找下id*/ //显示天气的textview mShow =(TextView) findViewById(R.id.show); //button的id mButton =(Button) findViewById(R.id.getwhether); //progress的id mProgress =(ProgressBar)findViewById(R.id.progress); //初始化persenter对象。 mPresenter = new WeatherPresenterImpl();//点击事件
mButton.setOnClickListener(this);} //显示天气信息 @Override public void setTextShow(String weatherString) { mShow.setText(weatherString); } //显示进度 @Override public void showProgress() { mButton.setVisibility(View.VISIBLE); } //隐藏进度 @Override public void dismissProgress() { mButton.setVisibility(View.GONE); } //实现button的点击事件,记住这里的逻辑不能再这里写, // 必须放到model中,但是model不与view直接相关,所以我们必须要把这个方法放到presenter中,、 // 所以我们需要presenter对象,那么初始化的时候就需要获取presenter对象 @Override public void onClick(View v) { mPresenter.getWheather(); }}
第二:讲view与presenter连接上。
//这个时候,注意观察,view与presenter已经连接上了,我们点击button获取天气的方法已经不在view中了,已经到了presenter中。
mPresenter.getWheather();
这个方法就是获取天气的方法,是我们自己写的,注意 ,这个方法是没有的 ,我们创建一下
package com.huhai.mvp_retrofit_rxjva_demo.presenter; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.presenter * @文件名: IWeatherPresenter * @创建者: Administrator * @创建时间: 2017/3/4 9:54 * @描述: TODO */ public interface IWeatherPresenter { public static final String TAG = "IWeatherPresenter"; void getWheather(); }
这个时候presenter的接口中已经有了获取天气的方法了,看到没,(这里直接使用androidStudio生成的),哇,andridStudio实在是太智能了,如果你体验过ios,你才会知道ios开发这么难用。
再看看实现类:
package com.huhai.mvp_retrofit_rxjva_demo.presenter; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.presenter * @文件名: WeatherPresenterImpl * @创建者: Administrator * @创建时间: 2017/3/4 9:51 * @描述: TODO */ public class WeatherPresenterImpl implements IWeatherPresenter{ private static final String TAG = "WeatherPresenterImpl"; @Override public void getWheather() { } }
第三:将presenter与model连接上。
写到这里,我们已经将获取天气的方法转移到presenter中,但是还不行啊,我们的mvp模式需要将网络请求数据这些东西放到model中,所以继续吧。
看model接口中的代码:
package com.huhai.mvp_retrofit_rxjva_demo.model; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.model * @文件名: IWeatherModel * @创建者: Administrator * @创建时间: 2017/3/4 9:53 * @描述: TODO */ public interface IWeatherModel { public static final String TAG = "IWeatherModel"; void getWhether(); }
实现类的代码
package com.huhai.mvp_retrofit_rxjva_demo.model; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.model * @文件名: WeatherRxJavaModelImpl * @创建者: Administrator * @创建时间: 2017/3/4 9:52 * @描述: TODO */ public class WeatherRxJavaModelImpl implements IWeatherModel { private static final String TAG = "WeatherRxJavaModelImpl"; @Override public void getWhether() { } }
到了这里,真正的网络请求逻辑就到了他自己合适的地方。接下来就进行网络请求吧!!!!!!!!!(我们先不要管数据后面怎么放回去,车到山前必有路,几个啥子)
第四:网络请求阶段,获取数据
这里我们使用的rxjava+retrofit封装网络请求,关于这部分,我也会在代码中详细的进行介绍。更多的就不讲这方面的
数据是网上随便找的一个天气的数据
//http://www.weather.com.cn/data/sk/101010100.html
1.先创建javabean存储数据(bean我就不贴了)
2.创建一个网络请求工具类。
添加依赖
compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-jackson:2.1.0' compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' compile 'io.reactivex:rxandroid:1.2.1'
上面两个工具类就是封装了retrofit与rjava的网络框架,代码如下:
package com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet * @文件名: IWeatherProtocol * @创建者: Administrator * @创建时间: 2017/3/4 10:44 * @描述: TODO */ import com.huhai.mvp_retrofit_rxjva_demo.bean.WhetherBean; import retrofit2.http.GET; import rx.Observable; public interface IWeatherProtocol { /** * Rxjava * @return */ @GET("data/sk/101010100.html") Observable<WhetherBean> getWeatherRxJava(); /*@POST("data/cityinfo/101010100.html") Observable<WeatherBean> getWeather();*/ }
package com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet; import com.huhai.mvp_retrofit_rxjva_demo.bean.WhetherBean; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.jackson.JacksonConverterFactory; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.retrofit * @文件名: RetrofitManager * @创建者: Administrator * @创建时间: 2017/3/4 10:41 * @描述: TODO */ public class RetrofitManager { private volatile static RetrofitManager mInstance; private OkHttpClient okHttpClient; private Retrofit retrofit; private RetrofitManager() { if (okHttpClient == null) { okHttpClient = new OkHttpClient.Builder() .readTimeout(10000, TimeUnit.MILLISECONDS) .writeTimeout(10000, TimeUnit.MILLISECONDS) .connectTimeout(10000, TimeUnit.MILLISECONDS) .build(); } if (retrofit == null) retrofit = new Retrofit.Builder() .baseUrl("http://www.weather.com.cn/") .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//用于返回Rxjava调用,非必须 .addConverterFactory(JacksonConverterFactory.create()) .client(okHttpClient) .build(); IWeatherProtocol service = retrofit.create(IWeatherProtocol.class); service.getWeatherRxJava().subscribeOn(Schedulers.io())//这连续几个方法都是RxJava里面的 .observeOn(AndroidSchedulers.mainThread())//AndroidSchedulers是RxAndroid里面的类 .subscribe(new Subscriber<WhetherBean>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(WhetherBean weather) { } }); } public static RetrofitManager getInstance() { if (mInstance == null) { synchronized (RetrofitManager.class) { if (mInstance == null) mInstance = new RetrofitManager(); } } return mInstance; } public <T> T createService(Class<T> clz) { return retrofit.create(clz); } }
接下来我们在model中请求网络。获取数据:
package com.huhai.mvp_retrofit_rxjva_demo.model; import com.huhai.mvp_retrofit_rxjva_demo.bean.WhetherBean; import com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet.IWeatherProtocol; import com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet.RetrofitManager; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.model * @文件名: WeatherRxJavaModelImpl * @创建者: Administrator * @创建时间: 2017/3/4 9:52 * @描述: TODO */ public class WeatherRxJavaModelImpl implements IWeatherModel { private static final String TAG = "WeatherRxJavaModelImpl"; @Override public void getWhether() { RetrofitManager.getInstance().createService(IWeatherProtocol.class).getWeatherRxJava() .subscribeOn(Schedulers.io())//这连续几个方法都是RxJava里面的 .observeOn(AndroidSchedulers.mainThread())//AndroidSchedulers是RxAndroid里面的类 .subscribe(new Subscriber<WhetherBean>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(WhetherBean weather) { } }); } }
到了这里我们其实就已经获取了网络数据了,可以看到,在完成网络请求后有三个方法,请求完成,请求失败,请求成功后的onnext。
也就是说我们在这里就可以将值返回去了,那么怎么将值返回到我们的view中呢?
接口回调?enenbus? 监听器?
这些大家都能想到,我们知道,model不与view直接打交道,所以,我们先不管其他的,只要把数据返回给presenter就行。
我们可以写一个回调函数,让我们的presenter去实现这个接口,然后在model中创建对象去调用接口的方法。(这里其实就是回调函数,很简单)
回调函数:
然后我们在presenter中奖对象传过去
/*加一个构造方法,便于初始化model*/ public WeatherPresenterImpl(){ //将对象传过去 mModel = new WeatherRxJavaModelImpl(this); }
回调方法中有两种结果,网络请求成功,或者失败
package com.huhai.mvp_retrofit_rxjva_demo.callback; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.callback * @文件名: OnWeatherCallback * @创建者: Administrator * @创建时间: 2017/3/4 11:10 * @描述: TODO */ import com.huhai.mvp_retrofit_rxjva_demo.bean.WhetherBean; public interface OnWeatherCallback { //在回调中需要有两个方法,请求成功或者请求失败 public void onfailure(); public void onsuccess(WhetherBean whetherString); }
presenter实现回调函数后的方法:
package com.huhai.mvp_retrofit_rxjva_demo.presenter; import com.huhai.mvp_retrofit_rxjva_demo.callback.OnWeatherCallback; import com.huhai.mvp_retrofit_rxjva_demo.model.WeatherRxJavaModelImpl; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.presenter * @文件名: WeatherPresenterImpl * @创建者: Administrator * @创建时间: 2017/3/4 9:51 * @描述: TODO */ public class WeatherPresenterImpl implements IWeatherPresenter,OnWeatherCallback { private static final String TAG = "WeatherPresenterImpl"; private final WeatherRxJavaModelImpl mModel; /*加一个构造方法,便于初始化model*/ public WeatherPresenterImpl(){ //将对象传过去 mModel = new WeatherRxJavaModelImpl(this); } @Override public void getWheather() { //继续讲网络请求的方法转移到model中 mModel.getWhether(); } //回调的方法 @Override public void onfailure() { } @Override public void onsuccess(String whetherString) { } }
好了基本的路子已经完成了。现在可以传数据了。
package com.huhai.mvp_retrofit_rxjva_demo.model; import com.huhai.mvp_retrofit_rxjva_demo.bean.WhetherBean; import com.huhai.mvp_retrofit_rxjva_demo.callback.OnWeatherCallback; import com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet.IWeatherProtocol; import com.huhai.mvp_retrofit_rxjva_demo.retrofitInternet.RetrofitManager; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.model * @文件名: WeatherRxJavaModelImpl * @创建者: Administrator * @创建时间: 2017/3/4 9:52 * @描述: TODO */ public class WeatherRxJavaModelImpl implements IWeatherModel{ private static final String TAG = "WeatherRxJavaModelImpl"; private OnWeatherCallback listener; public WeatherRxJavaModelImpl(OnWeatherCallback callback){ this.listener =callback; } @Override public void getWhether() { RetrofitManager.getInstance().createService(IWeatherProtocol.class).getWeatherRxJava() .subscribeOn(Schedulers.io())//这连续几个方法都是RxJava里面的 .observeOn(AndroidSchedulers.mainThread())//AndroidSchedulers是RxAndroid里面的类 .subscribe(new Subscriber<WhetherBean>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { listener.onfailure(); } @Override public void onNext(WhetherBean weather) { listener.onsuccess(weather); } }); } }
数据已经传到了presenter层。到了这里就简单了。接下来就是由model层的数据教给view层去处理
我们记得view里面有哪几个方法啊,就是处理界面的方法,比如显示progress,隐藏progress,显示text等等。
那些方法都是在接口中定义的,需要去调用它们必须要有activity的对象,也就是view的对象,所以我们现在需要view的对象。
那还不简单,我们在new presenter的时候直接传过来不就行了。对了就是这样的。
在oncreat方法中
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /*找下id*/ //显示天气的textview mShow =(TextView) findViewById(R.id.show); //button的id mButton =(Button) findViewById(R.id.getwhether); //progress的id mProgress =(ProgressBar)findViewById(R.id.progress); //初始化persenter对象。 mPresenter = new WeatherPresenterImpl(this); }
在presenter中获取
/*加一个构造方法,便于初始化model*/ public WeatherPresenterImpl(MainActivity mainActivity){ //将对象传过去 this.mActivity =mainActivity; mModel = new WeatherRxJavaModelImpl(this); }
这里有了view了。接下里,使用view去调用activity的函数吧!
package com.huhai.mvp_retrofit_rxjva_demo.presenter; import com.huhai.mvp_retrofit_rxjva_demo.bean.WhetherBean; import com.huhai.mvp_retrofit_rxjva_demo.callback.OnWeatherCallback; import com.huhai.mvp_retrofit_rxjva_demo.model.WeatherRxJavaModelImpl; import com.huhai.mvp_retrofit_rxjva_demo.view.MainActivity; /* * @项目名: MVP_retrofit_rxjva_demo * @包名: com.huhai.mvp_retrofit_rxjva_demo.presenter * @文件名: WeatherPresenterImpl * @创建者: Administrator * @创建时间: 2017/3/4 9:51 * @描述: TODO */ public class WeatherPresenterImpl implements IWeatherPresenter,OnWeatherCallback { private static final String TAG = "WeatherPresenterImpl"; private WeatherRxJavaModelImpl mModel; private MainActivity mActivity; /*加一个构造方法,便于初始化model*/ public WeatherPresenterImpl(MainActivity mainActivity){ //将对象传过去 this.mActivity =mainActivity; mModel = new WeatherRxJavaModelImpl(this); } @Override public void getWheather() { //继续讲网络请求的方法转移到model中 mModel.getWhether(); } //回调的方法 @Override public void onfailure() { mActivity.dismissProgress(); } @Override public void onsuccess(WhetherBean whetherString) { mActivity.setTextShow(whetherString.getWeatherinfo().getWD()); } }
最后不要忘记了要加上网络权限
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
最后再看下项目的结构:
如果项目有出错的话,例如:
Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/LICENSE
File1: E:\androidstudio\.gradle\caches\modules-2\files-2.1\com.fasterxml.jackson.core\jackson-annotations\2.7.0\19f42c154ffc689f40a77613bc32caeb17d744e3\jackson-annotations-2.7.0.jar
File2: E:\androidstudio\.gradle\caches\modules-2\files-2.1\com.fasterxml.jackson.core\jackson-databind\2.7.2\84ffa765dd258dbab8695963c41308b054f3a1cb\jackson-databind-2.7.2.jar
File3: E:\androidstudio\.gradle\caches\modules-2\files-2.1\com.fasterxml.jackson.core\jackson-core\2.7.2\8b8310381b690e317f5f0574e9b2dd7034778b4c\jackson-core-2.7.2.jar
请在build.gradle添加
android { compileSdkVersion 24 buildToolsVersion "24.0.3" aaptOptions.cruncherEnabled = false aaptOptions.useNewCruncher = false packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' } defaultConfig { applicationId "com.eben.zhukeyunfu" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
好了,整个项目完成了,我会把我的代码放上去。大家可以去我的资源里面下载,以后有不明白的的就这么去套,都是套路,套路多了就不是套路了
代码放在我的个人资源里面http://download.csdn.net/detail/qq_16177199/9770216