本博客轉自郭霖公衆號:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236866&idx=1&sn=da666831f67303eeb7a57c1591204b43&scene=24&srcid=0910wJAKSLdsEFTBKwKfTNor#wechat_redirectjava
http://blog.csdn.net/qq_27778869/article/details/52121208json
MVP 是基於 MVC 的改進,相信你們都是從 MVC 走過的,可能不少人還會在項目中使用 MVC,咱們傳統的 MVC 中 xml佈局 充當就是 視圖層(View) ,業務層兼模型層 的一些業務處理須要在咱們 Activity 或 Fragment 中進行處理而後更新視圖層。因爲xml定義好的佈局,一旦加載以後,只能經過動態更新,那麼視圖層和Model就創建了聯繫,所以兩者的耦合度就提升了,那麼一旦修改了其中的一個就有可能對另外一產生影響。設計模式
同時全部的操做在咱們的 Controller 中進行處理,Controller 就會顯得十分的臃腫,可讀性就下降了,致使後期的項目維護成本提升了很多。不少時候你須要和團隊一塊兒開發項目,若是你的同事有一天在你請假或者想修改功能的時候發現一個Activity都有上千行甚至更多的時候,他開始慌了,他慌的時候若是還找到你的話,你可能也慌了。有了 MVP 以後,媽媽不再用擔憂我會慌了!api
在 MVP 中 model層 依然不變,只不過以前充當 Controller 的 Activity或者Fragment 就變身爲了 View層,而業務邏輯的實現是在咱們的 Presenter 中。簡單介紹完了 MVP,光說不練,一點效果都沒有的,下面咱們來進行 MVP 的三部曲。服務器
本次案例是在進入程序的時候訪問服務器的指定接口來獲取當前服務器中apk的版本號,經過對比和當前應用程序的版本號,若是有新版本彈出一個土司提示用戶更新,功能就這麼簡單。網絡
視圖層須要哪些更新UI的操做?可能你們會好奇我爲何會問這個,這裏我留下一個懸念,待會給你們細講。這個問題的答案是彈出一個土司提示用戶更新。app
怎麼進行更新UI前的操做?異步
什麼時候告知視圖層進行UI更新?ide
結合上面的三個問題,咱們根據需求設計代碼:函數
1. 定義一個 MvpView 的接口:
1 public Interface MvpView { 2 //提示更新 3 void showMessage(); 4 5 }
2. 定義一個 Model 類:
1 public class MvpModel{ 2 /*從服務器獲取的apk版本*/ 3 private String newApkVersion; 4 //這裏我就隨便給出一個,正確與否我就不得而知 這裏面須要注意的query是跟上請求的參數,經過GET方法來請求的 ,你可能這會問我,不是說不知道正不正確的嗎,那還解釋這麼多?很差意思哈,我這裏說明好是爲了下面講到Retrofit 作鋪墊的 5 public static final String GET_NEW_VER_URL="http://192.168.1:8080/app/index/type?version=query"; 6 public String getNewApkVersion() { 7 return newApkVersion; 8 } 9 10 public void setNewApkVersion(String newApkVersion) { 11 this.newApkVersion = newApkVersion; 12 13 }
3. 定義一個基類 BasePresenter(固然你們也能夠不用這麼作):
1 public abstract class BasePresenter<T> { 2 3 /*這個基類的Presenter 主要的做用就是將當前Presenter持有的view 在合適的時候給清除掉*/ 4 public T mView ; 5 public void attach(T mView){ 6 7 this.mView=mView; 8 9 10 } 11 12 13 public void dettach(){ 14 15 mView=null; 16 17 } 18 19 }
4. 定義一個實際操做的 MvpPresenter:
1 public class MvpPresenter extends BasePresenter<MvpView> { 2 private MvpModel mMvpModel; 3 private Context mContext; 4 private RequestQueue mRequestQueue; 5 //這裏重寫當前的構造函數 由於咱們須要獲取程序的版本,所以須要一個上下文對象 6 public MvpPresenter (Context mContext) { 7 this.mContext = mContext; 8 //當前的Mvp模式的講解我是採用Volley進行網絡請求的因此 就封裝了一個VolleyManager用於獲取惟一的RequestQueue對象 9 mRequestQueue = VolleyManager.getInstance(mContext).getRequestQueue(); 10 //對業務層初始化爲後面獲取的apk版本進行存放 11 mUpdateModel = new MvpModel(); 12 } 13 //因爲邏輯處理放到咱們的Presenter中了,所以咱們須要將在Activity的onResume 時候進行版本的檢查 14 public void onResume(){ 15 16 17 } 18 19 }
5. 開始視圖層的構建了,不過在以前咱們先建立一個 BaseMvpActivity 用於統一化管理咱們的 MVP模式 的Activity的構建:
1 public abstract class BaseMvpActivity<V,T extends BasePresenter<V> > extends AppCompatActivity { 2 3 4 5 /*定義一個Presenter 用於解綁持有的View 6 * 7 * 在onCreate進行初始化Presenter的操做 8 * 在onResume中進行綁定 9 * 在onDestroy 中進行解綁 10 * 11 * */ 12 public T presenter; 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 presenter=initPresenter(); 17 18 } 19 20 @Override 21 protected void onResume() { 22 super.onResume(); 23 presenter.attach((V)this); 24 } 25 26 @Override 27 protected void onDestroy() { 28 super.onDestroy(); 29 presenter.dettach(); 30 } 31 32 /* 這裏是適配爲不一樣的View 裝載不一樣Presenter*/ 33 public abstract T initPresenter(); 34 35 36 }
6. 正式寫咱們的應用Activity了(當前Activity的佈局中僅有一個TextView):
1 public class MvpActivity extends BaseMvpActivity<MvpView,MvpPresenter> implements MvpView{ 2 @Bind(R.id.tvShow) 3 TextView tvMsg; 4 @Override 5 protected void onCreate(Bundle savedInstanceState) { 6 super.onCreate(savedInstanceState); 7 8 setContentView(R.layout.activity_update); 9 ButterKnife.bind(this); 10 } 11 @Override 12 protected void onResume() { 13 super.onResume(); 14 presenter.onResume(); 15 } 16 @Override 17 public MvpPresenter initPresenter() { 18 return new MvpPresenter (getApplicationContext()); 19 } 20 @Override 21 22 public void showMessage(){ 23 24 tvMsg.setText("有新版本需更新!"); 25 26 Toast.makeText(getApplicationContext(),"更新版本啦",Toast.LENGTH_LONG).show(); 27 28 29 } 30 31 32 33 34 35 36 37 38 }
7. 看到上面的 MvpActivity 是否是以爲代碼很清爽,那還不是把業務處理的邏輯丟給了 Presenter了,好了咱們來具體看看 MvpPresenter 怎麼寫:
1 //在前面貼出的代碼中添加 2 pulic void onResume(){ 3 4 getVerFromServer(); 5 6 7 } 8 private void getVerFromServer(){ 9 10 JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.GET, MvpModel.GET_NEW_VERSION ,null, new Response.Listener<JSONObject>() { 11 @Override 12 public void onResponse(JSONObject response) { 13 Log.e("請求版本返回結果爲:",response.toString()); 14 15 try { 16 mUpdateModel.setNewApkVersion(response.getJSONObject("data").getString("version")); 17 //若是從服務器獲取的版本和當前的版本相同,就可提示用戶了 18 if(mUpdateModel.getNewApkVersion.equals(getCurrentVer())){ 19 //這裏就是合適的更新UI時間,經過這裏把更新的實際傳遞出去 20 mView.showMessage(); 21 } catch (JSONException e) { 22 e.printStackTrace(); 23 } 24 25 } 26 }, new Response.ErrorListener() { 27 @Override 28 public void onErrorResponse(VolleyError error) { 29 30 } 31 }); 32 jsonRequest.setTag("getVer"); 33 mRequestQueue.add(jsonRequest); 34 35 36 } 37 private String getCurrentVer(){ 38 String verName=""; 39 try { 40 verName=mContext.getPackageManager().getPackageInfo(context.getPackageName(),0).versionName; 41 } catch (PackageManager.NameNotFoundException e) { 42 e.printStackTrace(); 43 } 44 Log.e("apkVer",verName); 45 return verName; 46 47 }
相信你們在開發過程當中被這個字眼給衝擊過很多次,具體 Retrofit 是什麼這裏我就不詳細介紹了,咱們只針對它的簡單應用來說解:
1. 首先須要建立一個接口如 RetrofitCall,由於 Retrofit 是經過註解的形式構建方法的,下面咱們來寫一下:
1 public interface RetrofitCall{ 2 // 咱們把以前定義好的api接口 拿來 http://192.168.1:8080/app/index/type?version=query 3 //這裏我先簡單給你們介紹一下 Retrofit 請求網絡的接口形式要知足RESTful 形式(有不瞭解的可自填坑) 不夠構成通常是這樣的[協議類型:][//主機地址加端口(或者域名)][具體目錄][請求參數] 4 //因此這個接口的具體目錄(path)爲app/index/type 也就是下面這個GET註解中應該填入的 5 @GET("app/index/type") 6 Call<VersionBean> getVer(@Query(version) String ver); 7 //若是有讀者不知道這個VersionBean怎麼生成的能夠查看個人博客有一篇講Retrofit的 其實就是利用GSONFromat 來實現的 8 9 }
2. 註冊網絡訪問(這裏的代碼是在 Presenter 實現的):
1 Retrofit mRetrofit =new Retrofit.Builder() 2 //這裏爲主機加端口(或域名) 3 .baseUrl("http://192.168.1:8080/") 4 .clent(new OkHttpClient()) 5 .addConverterFactory(GsonConverterFactory.create()) 6 .build();
3. 建立自定義接口實例並執行異步網絡請求:
1 RetrofitCall call=mRetrofit.create(RetrofitCall.class); 2 //這裏須要把請求的參數填入 3 Call<VersionBean> myCall=call.getVer("1"); 4 //異步執行 5 maCall.enqueue(new Callback<VersionBean>(){ 6 @Override 7 public void onResposne(Call<VersionBean>call,retrofit2.Response<VersionBean> response) { 8 9 10 if(response.body().getData().getVersion().equals(getCurrentVe r())){ 11 12 mView.showMessage(); 13 } 14 } 15 @Override 16 public void onFailure(Call<VersionBean> call, Throwable t) { 17 Log.e("callerro+"------------>"+t); 18 } 19 20 });
RxJava 是一個異步操做的庫,主要採用的觀察者模式,在這裏我只是簡單的介紹,須要詳細瞭解能夠參考拋物線這篇:
給Android開發工程師的一篇關於RxJava的詳解(這個已經推薦好幾回了,值得一看吶)
http://gank.io/post/560e15be2dca930e00da1083
在這裏我就不做過多介紹了,咱們來演示 Retrofit 依靠 RxJava 來改進上面的代碼,咱們在 RetrofitCall 中添加一個新的註解:
1 @GET("app/index/type") 2 Observable<VersionBean> getVerByRxJavaWithRetrofit(@Query("version") String ver);
接着在 Presenter 中填入以下的方法:
1 private void requestDataByRxJavaWithRetrofit(){ 2 Retrofit mRxjavaRetrofit= new Retrofit.Builder() 3 .baseUrl("http://192.168.1:8080/") 4 //因爲須要轉爲Observable 須要添加RxJavaCallAdapterFactory 5 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 6 .addConverterFactory(GsonConverterFactory.create()) 7 .build(); 8 RetrofitCall call= mRxjavaRetrofit.create(RetrofitCall.class); 9 call.getVerByRxJavaWithRetrofit(""1) 10 //指定時間消費在主線程 11 .observeOn(AndroidSchedulers.mainThread()) 12 //事件產生在新線程 13 .subscribeOn(Schedulers.newThread()) 14 //指定一個觀察者 15 .subscribe(new Observer<Version>(){ 16 @Override 17 public void onCompleted() { 18 19 mView.showMessage(); 20 } 21 22 @Override 23 public void onError(Throwable e) { 24 25 } 26 /*這個時候是訂閱者須要跟新UI的時間了通知它更新*/ 27 @Override 28 public void onNext(LogoBean logoBean) { 29 /*這裏先不作判斷*/ 30 31 } 32 33 34 } ); 35 36 }
上面對這三者的結合使用有了一個直觀的介紹,其實 MVP模式 能夠理解爲更新UI須要什麼操做,何時開始更新UI,怎麼更新UI;而咱們的 MVP模式 就是把這三種狀態巧妙地分開,所以會讓思路顯得很清晰。
而 RxJava 正式基於這種設計,被觀察者經過被訂閱的形式在本身有新動態的時候告知觀察者我改變了,剩下的就交給觀察者作本身應該作的事情了!這樣的設計模式很符合咱們需求,也很利於團隊開發,換個模式你會以爲效率大大提升,讓咱們一塊兒加入 MVP+RxJava+Retrofit 的隊列之中吧!