先列出一些常用的依賴,想必看到下面的依賴大家也能明白接下來要講的是什么?
//所需依賴
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.5'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
沒錯,就是老生常談的 MVP 模式。
retrofit2
作為網絡請求,gson
作為json解析器。注意這里是用的最新的rxjava2
和 jakewharton 大大開源的retrofit2-rxjava2-adapter
作為橋接器。最新的版本可以前往各自的 Github 上查看。
基礎架構
我個人比較喜歡在項目下新建一個 Android library 的模塊,取名為core,主要作用是負責網絡層和數據層。像數據實體類,數據庫操作,SharedPreferences緩存,網絡請求都可以放在 core 模塊下,主要目的就是徹底將UI和數據層完全分開(物理層面上)。
網絡模塊
需要一個 RetrofitHelper
單例模塊支持,主要是為 OkHttp 設置請求參數屬性和初始化 Api 接口服務。
OkHttp的參數設置
OkHttp上可以設置的參數很多,像緩存,頭部信息,超時時間,重連等信息都可以在 OkHttpClient 初始化設置時統一設置。
private void initOkHttp() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//設置統一的請求頭部參數
builder.addInterceptor(apikey);
//設置緩存
builder.addNetworkInterceptor(cacheInterceptor);
builder.addInterceptor(cacheInterceptor);
builder.cache(cache);
//設置超時
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
builder.writeTimeout(20, TimeUnit.SECONDS);
//錯誤重連
builder.retryOnConnectionFailure(true);
okHttpClient = builder.build();
}
初始化項目 api 接口
一般來說,一個項目的網絡返回數據都有統一的返回數據,比如有一個定義好的返回碼 resultCode
,數據返回信息 resultInfo
,以及最重要的數據對象 returnObject
。所以我們需要一個類來進行網絡數據套接。
public class ApiResponse<T> {
private int resultCode;
private T returnObject;
private Object ruturnInfo;
// get 和 set 方法
//...
}
定義好數據類型,就輪到 Retrofit
與網絡接口進行聯動,首先需要一個能夠定義 api 接口的地方 Apis
public interface Apis {
/**
* 獲取啟動頁圖片
*
* @return
*/
@FormUrlEncoded
@POST("getSplashImg")
Observable<ApiResponse<SplashImageBean>> getStartImg(@Field("uid") String uid,@Field("size") String size);
//其他的api
...
}
之后回到 RetrofitHelper
中初始化接口服務。
// 接口服務
apis = getApiService(HttpUtils.BASEURL, Apis.class);
// 接口服務初始化方法
private <T> T getApiService(String baseUrl, Class<T> clz) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit.create(clz);
}
搭建 MVP 架構
此方法是從 Google 的開源項目android-architecture 上的 todo?mvp?rxjava 篇演變而來。一樣需要定義 View 和 Presenter 接口以及Presenter的實現,只不過加了一層 RxPresenter
對Rxjava的優化,防止內存的泄露。
MVP的基礎類
首先要為mvp模式定下基礎接口 BaseView
和 BasePresenter
。
Presenter 需要綁定 View 才能回調 View 里面的各種方法,所以直接在類聲明的時候將 View 綁定。
同理,View 里面需要一個 Presenter 去處理數據,故定義一個 setPresenter()
方法來提醒(所以不設置也行)。
public interface BaseView {
void setPresenter();
}
public interface BasePresenter<T extends BaseView> {
void attachView(T view);
void detachView();
}
Rxjava 在 MVP 上的優化
之前在寫 MoeMusic開源項目 的時候完全沒有考慮到 Rxjava 在與 Retrofit 結合請求網絡請求的時候會存在內存泄露的問題,所以在這個模塊上利用 Rxjava 的訂閱和取消訂閱功能消除內存泄露的問題。
/**
* @author cpacm
* @date 2017/2/26
* @desciption 可取消訂閱的 rxpresenter,防止rxjava引起的內存泄露
*/
public abstract class RxPresenter<T extends BaseView> implements BasePresenter<T> {
protected T view;
protected CompositeDisposable compositeDisposable;
protected void unDisposable() {
if (compositeDisposable != null) {
compositeDisposable.clear();
}
}
protected void addDisposable(Disposable disposable) {
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
}
compositeDisposable.add(disposable);
}
@Override
public void attachView(T view) {
this.view = view;
}
@Override
public void detachView() {
this.view = null;
unDisposable();
}
}
原理很簡單,就是使用 CompositeDisposable 來訂閱 rxjava 發射的事件,之后在 detachView()
解綁的時候取消訂閱。
MVP 的簡單使用
在使用前,我們先建一個 BaseActivity
作為所有的Activity的基類,并將其生命周期與 MVP 模塊關聯起來。
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView {
protected T presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setPresenter();
if (presenter != null) {
presenter.attachView(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.detachView();
}
}
}
使用示例
需求:一個 app 的啟動頁,啟動頁圖片來自服務器。
SplashContract
中定義各個接口要回調的方法。
public interface SplashContract {
interface View extends BaseView {
void showSplash(SplashImageBean bean);
}
interface Presenter extends BasePresenter<View> {
void getSplashData();
}
}
SplashPresenter
中實現網絡的請求和view的回調
public class SplashPresenter extends RxPresenter<SplashContract.View> implements SplashContract.Presenter {
private Apis zqswApis;
public SplashPresenter() {
zqswApis = RetrofitHelper.getInstance().getApis();
}
@Override
public void getSplashData() {
Disposable disposable = zqswApis.getStartImg("","")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Consumer<ApiResponse<SplashImageBean>>() {
@Override
public void accept(ApiResponse<SplashImageBean> splashImageBean) throws Exception {
view.showSplash(splashImageBean.getReturnObject());
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e("cpacm", throwable.toString());
}
});
addDisposable(disposable);
}
}
SplashActivity
實現UI完成整個需求
/**
* @author cpacm
* @date 2017/2/16
* @desciption 啟動界面
*/
public class SplashActivity extends BaseActivity<SplashPresenter> implements SplashContract.View {
private ImageView bgView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
bgView = (ImageView) findViewById(R.id.background);
presenter.getSplashData();
}
@Override
public void setPresenter() {
presenter = new SplashPresenter();
}
@Override
public void showSplash(SplashImageBean bean) {
Glide.with(this)
.load(bean.getImageUrl())
.into(bgView);
Toast.makeText(this, bean.toString(), Toast.LENGTH_SHORT).show();
}
}
項目源碼見下方鏈接
結論
整個框架小巧而精致,而且看起來也不怎么復雜,個人項目或者小型的團隊項目應付起來應該是綽綽有余了,不過大型的項目應該還需要擴展或者采用其他的架構來應付繁瑣的需求。