先列出一些常用的依賴挂签,想必看到下面的依賴大家也能明白接下來要講的是什么?
//所需依賴
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'
沒錯(cuò)盼产,就是老生常談的 MVP 模式饵婆。
retrofit2
作為網(wǎng)絡(luò)請(qǐng)求,gson
作為json解析器戏售。注意這里是用的最新的rxjava2
和 jakewharton 大大開源的retrofit2-rxjava2-adapter
作為橋接器侨核。最新的版本可以前往各自的 Github 上查看草穆。
基礎(chǔ)架構(gòu)
我個(gè)人比較喜歡在項(xiàng)目下新建一個(gè) Android library 的模塊,取名為core芹关,主要作用是負(fù)責(zé)網(wǎng)絡(luò)層和數(shù)據(jù)層续挟。像數(shù)據(jù)實(shí)體類,數(shù)據(jù)庫操作侥衬,SharedPreferences緩存诗祸,網(wǎng)絡(luò)請(qǐng)求都可以放在 core 模塊下,主要目的就是徹底將UI和數(shù)據(jù)層完全分開(物理層面上)轴总。
網(wǎng)絡(luò)模塊
需要一個(gè) RetrofitHelper
單例模塊支持直颅,主要是為 OkHttp 設(shè)置請(qǐng)求參數(shù)屬性和初始化 Api 接口服務(wù)。
OkHttp的參數(shù)設(shè)置
OkHttp上可以設(shè)置的參數(shù)很多怀樟,像緩存功偿,頭部信息,超時(shí)時(shí)間往堡,重連等信息都可以在 OkHttpClient 初始化設(shè)置時(shí)統(tǒng)一設(shè)置械荷。
private void initOkHttp() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//設(shè)置統(tǒng)一的請(qǐng)求頭部參數(shù)
builder.addInterceptor(apikey);
//設(shè)置緩存
builder.addNetworkInterceptor(cacheInterceptor);
builder.addInterceptor(cacheInterceptor);
builder.cache(cache);
//設(shè)置超時(shí)
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
builder.writeTimeout(20, TimeUnit.SECONDS);
//錯(cuò)誤重連
builder.retryOnConnectionFailure(true);
okHttpClient = builder.build();
}
初始化項(xiàng)目 api 接口
一般來說,一個(gè)項(xiàng)目的網(wǎng)絡(luò)返回?cái)?shù)據(jù)都有統(tǒng)一的返回?cái)?shù)據(jù)虑灰,比如有一個(gè)定義好的返回碼 resultCode
吨瞎,數(shù)據(jù)返回信息 resultInfo
,以及最重要的數(shù)據(jù)對(duì)象 returnObject
穆咐。所以我們需要一個(gè)類來進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)套接颤诀。
public class ApiResponse<T> {
private int resultCode;
private T returnObject;
private Object ruturnInfo;
// get 和 set 方法
//...
}
定義好數(shù)據(jù)類型,就輪到 Retrofit
與網(wǎng)絡(luò)接口進(jìn)行聯(lián)動(dòng)对湃,首先需要一個(gè)能夠定義 api 接口的地方 Apis
public interface Apis {
/**
* 獲取啟動(dòng)頁圖片
*
* @return
*/
@FormUrlEncoded
@POST("getSplashImg")
Observable<ApiResponse<SplashImageBean>> getStartImg(@Field("uid") String uid,@Field("size") String size);
//其他的api
...
}
之后回到 RetrofitHelper
中初始化接口服務(wù)崖叫。
// 接口服務(wù)
apis = getApiService(HttpUtils.BASEURL, Apis.class);
// 接口服務(wù)初始化方法
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 架構(gòu)
此方法是從 Google 的開源項(xiàng)目android-architecture 上的 todo?mvp?rxjava 篇演變而來。一樣需要定義 View 和 Presenter 接口以及Presenter的實(shí)現(xiàn)拍柒,只不過加了一層 RxPresenter
對(duì)Rxjava的優(yōu)化心傀,防止內(nèi)存的泄露。
MVP的基礎(chǔ)類
首先要為mvp模式定下基礎(chǔ)接口 BaseView
和 BasePresenter
拆讯。
Presenter 需要綁定 View 才能回調(diào) View 里面的各種方法剧包,所以直接在類聲明的時(shí)候?qū)?View 綁定。
同理往果,View 里面需要一個(gè) Presenter 去處理數(shù)據(jù),故定義一個(gè) setPresenter()
方法來提醒(所以不設(shè)置也行)一铅。
public interface BaseView {
void setPresenter();
}
public interface BasePresenter<T extends BaseView> {
void attachView(T view);
void detachView();
}
Rxjava 在 MVP 上的優(yōu)化
之前在寫 MoeMusic開源項(xiàng)目 的時(shí)候完全沒有考慮到 Rxjava 在與 Retrofit 結(jié)合請(qǐng)求網(wǎng)絡(luò)請(qǐng)求的時(shí)候會(huì)存在內(nèi)存泄露的問題陕贮,所以在這個(gè)模塊上利用 Rxjava 的訂閱和取消訂閱功能消除內(nèi)存泄露的問題。
/**
* @author cpacm
* @date 2017/2/26
* @desciption 可取消訂閱的 rxpresenter,防止rxjava引起的內(nèi)存泄露
*/
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 發(fā)射的事件肮之,之后在 detachView()
解綁的時(shí)候取消訂閱掉缺。
MVP 的簡單使用
在使用前,我們先建一個(gè) BaseActivity
作為所有的Activity的基類戈擒,并將其生命周期與 MVP 模塊關(guān)聯(lián)起來眶明。
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();
}
}
}
使用示例
需求:一個(gè) app 的啟動(dòng)頁,啟動(dòng)頁圖片來自服務(wù)器筐高。
SplashContract
中定義各個(gè)接口要回調(diào)的方法搜囱。
public interface SplashContract {
interface View extends BaseView {
void showSplash(SplashImageBean bean);
}
interface Presenter extends BasePresenter<View> {
void getSplashData();
}
}
SplashPresenter
中實(shí)現(xiàn)網(wǎng)絡(luò)的請(qǐng)求和view的回調(diào)
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
實(shí)現(xiàn)UI完成整個(gè)需求
/**
* @author cpacm
* @date 2017/2/16
* @desciption 啟動(dòng)界面
*/
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();
}
}
項(xiàng)目源碼見下方鏈接
結(jié)論
整個(gè)框架小巧而精致,而且看起來也不怎么復(fù)雜柑土,個(gè)人項(xiàng)目或者小型的團(tuán)隊(duì)項(xiàng)目應(yīng)付起來應(yīng)該是綽綽有余了蜀肘,不過大型的項(xiàng)目應(yīng)該還需要擴(kuò)展或者采用其他的架構(gòu)來應(yīng)付繁瑣的需求。