首先,展現一下封裝好以後的項目的層級結構。
一、先建立一個RetrofitApiService.javaphp
package com.xdw.retrofitrxmvpdemo.http; import com.xdw.retrofitrxmvpdemo.model.UserInfo; import retrofit2.http.GET; import retrofit2.http.Query; import rx.Observable; /** * Created by 夏德旺 on 2017/12/8. */ public interface RetrofitApiService { @GET("userinfo") Observable<UserInfo> getUserInfo(@Query("uid") int uid); }
這裏就是把原生的retrofit中的Call換成了RxJava中的Observable。java
二、封裝RetrofitUtilandroid
package com.xdw.retrofitrxmvpdemo.http; import android.content.Context; import com.google.gson.GsonBuilder; import com.xdw.retrofitrxmvpdemo.constant.UrlConstant; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; /** * Created by 夏德旺 on 2017/12/8. */ public class RetrofitUtil { private Context mCntext; //聲明Retrofit對象 private Retrofit mRetrofit; //聲明RetrofitApiService對象 private RetrofitApiService retrofitApiService; OkHttpClient client = new OkHttpClient(); GsonConverterFactory factory = GsonConverterFactory.create(new GsonBuilder().create()); //因爲該對象會被頻繁調用,採用單例模式,下面是一種線程安全模式的單例寫法 private volatile static RetrofitUtil instance; public static RetrofitUtil getInstance(Context context){ if (instance == null) { synchronized (RetrofitUtil.class) { if (instance == null) { instance = new RetrofitUtil(context); } } } return instance; } private RetrofitUtil(Context mContext){ mCntext = mContext; init(); } //初始化Retrofit private void init() { mRetrofit = new Retrofit.Builder() .baseUrl(UrlConstant.BASE_URL) .client(client) .addConverterFactory(factory) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); retrofitApiService = mRetrofit.create(RetrofitApiService.class); } public RetrofitApiService getRetrofitApiService(){ return retrofitApiService; } }
三、封裝DataManager,這裏和原生的retrofit的封裝同樣該類用來管理RetrofitApiService中對應的各類API接口,當作Retrofit和presenter中的橋樑,Activity就不用直接和retrofit打交道了json
package com.xdw.retrofitrxmvpdemo.manager; import android.content.Context; import com.xdw.retrofitrxmvpdemo.http.RetrofitApiService; import com.xdw.retrofitrxmvpdemo.http.RetrofitUtil; import com.xdw.retrofitrxmvpdemo.model.UserInfo; import rx.Observable; /** * Created by 夏德旺 on 2017/12/8. */ //該類用來管理RetrofitApiService中對應的各類API接口, // 當作Retrofit和presenter中的橋樑,Activity就不用直接和retrofit打交道了 public class DataManager { private RetrofitApiService mRetrofitService; private volatile static DataManager instance; private DataManager(Context context){ this.mRetrofitService = RetrofitUtil.getInstance(context).getRetrofitApiService(); } //因爲該對象會被頻繁調用,採用單例模式,下面是一種線程安全模式的單例寫法 public static DataManager getInstance(Context context) { if (instance == null) { synchronized (DataManager.class) { if (instance == null) { instance = new DataManager(context); } } } return instance; } //將retrofit的業務方法映射到DataManager中,之後統一用該類來調用業務方法 //之後再retrofit中增長業務方法的時候,相應的這裏也要添加,好比添加一個getOrder public Observable<UserInfo> getUserInfo(int uid){ return mRetrofitService.getUserInfo(uid); } }
四、建立一個Presenter接口,這裏就引入了Presenter這個東西了,後面咱們根據具體的業務建立對應的實例去實現該接口。該接口主要面向數據,後面還會建立一個PresentView接口(面向View的),而後經過設計模式
BindPresentView方法將這2個接口關聯起來,從而實現對數據和Activity的管理。api
package com.xdw.retrofitrxmvpdemo.presenter; import com.xdw.retrofitrxmvpdemo.pv.PresentView; /** * Created by 夏德旺 on 2017/12/8. */ public interface Presenter { //Presenter初始化 void onCreate(); //銷燬 void onDestroy(); //綁定視圖 void BindPresentView(PresentView presentView); }
五、定義一個實現Presenter的基礎類BasePresenter,後續具體功能類繼承於它,主要是爲了在該類中寫一些共用方法,好比
CompositeSubscription的建立與銷燬。(CompositeSubscription幹嗎用的能夠查閱RxJava的資料)安全
package com.xdw.retrofitrxmvpdemo.presenter; import com.xdw.retrofitrxmvpdemo.pv.PresentView; import rx.subscriptions.CompositeSubscription; /** * Created by 夏德旺 on 2017/12/8. */ //定義一個Presenter的基礎類,後續具體功能類繼承於它 public class BasePresenter implements Presenter { //聲明一個CompositeSubscription對象,注意是protected修飾符,便於子類進行調用 protected CompositeSubscription mCompositeSubscription; @Override public void onCreate() { //在基礎類中對CompositeSubscription進行初始化,子類中就不用再寫一次 //子類若是須要對onCreate進行重寫,記得先調用super.onCreate(); mCompositeSubscription = new CompositeSubscription(); } @Override public void onDestroy() { //釋放CompositeSubscription,不然會形成內存泄漏 if (mCompositeSubscription.hasSubscriptions()){ mCompositeSubscription.unsubscribe(); } } @Override public void BindPresentView(PresentView presentView) { //與具體視圖進行綁定,留個子類進行擴展 } }
六、建立一個接口PresentView,面向視圖View的接口,和前面的Prenseter配合使用網絡
package com.xdw.retrofitrxmvpdemo.pv; /** * Created by 夏德旺 on 2017/12/8. */ //面向視圖View的接口,和前面的Prenseter配合使用 public interface PresentView { //定義一個最基礎的接口,裏面就包含一個出錯信息的回調 //由於大多數時候報錯的時候都是採用一條信息提示 //若是須要負責的報錯接口,請重載onError,是重載不是重寫 void onError(String result); }
七、以前還已經建立了2個類,這裏補充下,一個是UrlConstant(用來存放常量的,這裏用來存放Base Url),另一個是數據模型類UserInfoapp
UrlConstant.java:框架
package com.xdw.retrofitrxmvpdemo.constant; /** * Created by 夏德旺 on 2017/12/8. */ public class UrlConstant { /** * 域名: * 調試:http://10.1.1.192 */ public static final String BASE_URL="http://10.1.1.192"; }
UserInfo.java:
package com.xdw.retrofitrxmvpdemo.model; /** * Created by 夏德旺 on 2017/12/8. */ public class UserInfo { private String username; private int age; public UserInfo(String username, int age) { this.username = username; this.age = age; } public String getUsername() { return username; } public int getAge() { return age; } @Override public String toString() { return "UserInfo{" + "username='" + username + '\'' + ", age=" + age + '}'; } }
八、下面就須要將具體的業務加入到Presenter中了,先寫一個UserInfoPv繼承自PresentView接口,這個是根據具體業務添加。
package com.xdw.retrofitrxmvpdemo.pv; import com.xdw.retrofitrxmvpdemo.model.UserInfo; /** * Created by 夏德旺 on 2017/12/8. */ public interface UserInfoPv extends PresentView { //對基礎接口PresentView進行擴展,添加onSuccess回調 //由於該回調與具體的業務對應,因此不能寫到基礎接口裏面 //好比UserInfo的回調就建立一個UserInfoPv的接口,若是新增一個Order的業務, //則新增一個OrderPv的接口 void onSuccess(UserInfo userInfo); }
九、寫一個UserInfoPresenter繼承BasePresenter類,在該類中實現數據的請求,而且將該類與業務對應的PresentView進行綁定,這裏是和UserInfoPv綁定
package com.xdw.retrofitrxmvpdemo.presenter; import android.content.Context; import com.xdw.retrofitrxmvpdemo.manager.DataManager; import com.xdw.retrofitrxmvpdemo.model.UserInfo; import com.xdw.retrofitrxmvpdemo.pv.PresentView; import com.xdw.retrofitrxmvpdemo.pv.UserInfoPv; import rx.Observer; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; /** * Created by 夏德旺 on 2017/12/8. */ //該類是具體業務presenter,如需增長另外一個業務,好比Order //則能夠再建立一個OrderPresenter public class UserInfoPresenter extends BasePresenter { private Context mContext; private UserInfoPv mUserInfoPv; private UserInfo mUserInfo; public UserInfoPresenter (Context context){ this.mContext = context; } @Override public void BindPresentView(PresentView presentView) { mUserInfoPv = (UserInfoPv)presentView; } //在presenter中實現業務邏輯,此處會調用前面封裝好的retrofit的東西 //將處理結果綁定到對應的PresentView實例,這樣Activity和PresentView實例綁定好以後, //Activity->PresentView->Presenter->retrofit的關係就打通了 public void getUserInfo(int uid){ super.mCompositeSubscription.add(DataManager.getInstance(mContext).getUserInfo(uid) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<UserInfo>() { @Override public void onCompleted() { if (mUserInfo != null){ mUserInfoPv.onSuccess(mUserInfo); } } @Override public void onError(Throwable e) { e.printStackTrace(); mUserInfoPv.onError("請求失敗!!"); } @Override public void onNext(UserInfo userInfo) { mUserInfo = userInfo; } }) ); } }
十、好了,retrofit與rxjava,presenter相關的東西都封裝好了,接下來輪到以前苦逼的Activity出場了,在Activity中展現presenter傳遞過來的數據。
package com.xdw.retrofitrxmvpdemo.activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.xdw.retrofitrxmvpdemo.R; import com.xdw.retrofitrxmvpdemo.model.UserInfo; import com.xdw.retrofitrxmvpdemo.presenter.UserInfoPresenter; import com.xdw.retrofitrxmvpdemo.pv.UserInfoPv; public class MainActivity extends AppCompatActivity { private TextView text; private Button button; //定義須要調用的presenter對象 private UserInfoPresenter mUserInfoPresenter =new UserInfoPresenter(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView)findViewById(R.id.text); button = (Button)findViewById(R.id.button); //在Activity建立的時候同時初始化presenter,這裏onCreater不是指的建立presenter對象, // 而是作一些presenter的初始化操做,名字應該取名init更好理解點,我這裏就不重命名了 mUserInfoPresenter.onCreate(); //將presenter和PresentView進行綁定,實際上就是將presenter和Activity視圖綁定, //這個是MVP模式中V與P交互的關鍵 mUserInfoPresenter.BindPresentView(mUserInfoPv); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //點擊按鈕觸發presenter裏面的方法 mUserInfoPresenter.getUserInfo(1); } }); } //採用內部類方法定義presentView對象,該對象用來將Activity和presenter進行綁定 //綁定了之後主線程中就能夠經過回調來獲取網絡請求的數據 private UserInfoPv mUserInfoPv = new UserInfoPv(){ @Override public void onSuccess(UserInfo userInfo) { text.setText(userInfo.toString()); } @Override public void onError(String result) { Toast.makeText(MainActivity.this,result, Toast.LENGTH_SHORT).show(); } }; //在Activity銷燬的時候,必定要對CompositeSubscription進行釋放,不然會形成內存泄漏 //釋放操做封裝到了presenter的ondestroy方法中 @Override protected void onDestroy(){ super.onDestroy(); mUserInfoPresenter.onDestroy(); } }
補充下:截圖中的Result這個類是我實際項目中用到的,這裏簡化了下業務邏輯,就沒用到它。
Result.java:
package com.xdw.retrofitrxmvpdemo.model; /** * Created by 夏德旺 on 2017/12/8. */ public class Result<T> { private int code; private String msg; private T data; public Result(int code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public int getCode() { return code; } public String getMsg() { return msg; } public T getData() { return data; } @Override public String toString() { return "Result{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; } }
最後說明下,可能有的人以爲原本以前直接在Activity中寫retrofit很方便,如今卻加了這麼多代碼,視乎變得更麻煩了。可是本身仔細想一想設計模式,當業務變得愈來愈複雜的時候,這種封裝就會變得愈來愈有意義。
列舉下以後像該項目中擴展業務的步驟,好比加一個訂單功能。
操做步驟:
一、添加對應的model類Order
二、RetrofitApiService中添加對應的網絡請求api
三、將新添加的api映射到DataManager中
四、添加業務對應的PrensentView實例OrderPv
五、添加業務對應的Presenter實例OrderPresenter
六、在須要該業務的UI線程(Activity或Fragment)中調用具體業務對應的Presenter
看着新添加一個業務的過程是否是不少?可是邏輯很清晰,擴展很容易,寫代碼不是說代碼量越少,表明代碼寫的越好越有效率。
以後還會對該框架進一步封裝,加入攔截器,網絡請求等待框等功能的封裝。
補充下我這裏測試用的服務端源碼,就一個index.php文件:
<?php header("Content-Type: application/json; charset=utf-8"); $uid=$_GET['uid']; if($uid==1){ $arr['code']=200; $arr['msg']='ok'; $data['username']='xiadewang'; $data['age']=30; $arr['userinfo']=$data; echo json_encode($data,JSON_UNESCAPED_UNICODE); }
android代碼連接:http://download.csdn.net/download/aaaabbbb1019/10151399