最近看了不少M(fèi)VP的文章和項(xiàng)目,整合了一個(gè)關(guān)于MVP+retrofit2+RxJava的項(xiàng)目,話不多說(shuō)直接上代碼
前言
首先摘昌,閱讀本篇文章前,建議你先去了解一下MVP這個(gè)設(shè)計(jì)模式高蜂。當(dāng)然聪黎,也可以先看看我前面的文章_ 傳送門(mén)
另外,還用到了RxJava备恤、Retrofit稿饰、Okhttp。如果你已經(jīng)了解了他們的基本用法露泊,請(qǐng)直接忽略這段喉镰,接著往下看~
不想看長(zhǎng)長(zhǎng)的文章的,可以直接看代碼惭笑。代碼地址已經(jīng)上傳到Github,https://github.com/jjxs/MvpPackage
工程結(jié)構(gòu)
按照慣例侣姆,先看看工程的主要結(jié)構(gòu):
簡(jiǎn)單說(shuō)一下幾個(gè)主要包下的功能。首先是api包沉噩,這是存放對(duì)Retrofit進(jìn)行包裝的類(lèi)捺宗。Base包當(dāng)然是放各種Base類(lèi)啦~ mvp包是將契約類(lèi)Contract、Model的實(shí)現(xiàn)類(lèi)和Presenter的實(shí)現(xiàn)類(lèi)放一起川蒙,方便管理蚜厉。其實(shí)你也可以按功能分包,個(gè)人喜好吧畜眨。ui包放一些界面的類(lèi)昼牛,如Activity和Fragment。
下面正式開(kāi)始~
契約類(lèi)
同樣也是從Contract契約類(lèi)開(kāi)始說(shuō)起:
public interface MainContract {
interface View extends BaseView {
void showDialog();
void onSucceed(Gank data);
void onFail(String err);
void hideDialog();
}
interface Model extends BaseModel {
Observable<Gank> getGank();
}
abstract class Presenter extends BasePresenter<View, Model> {
public abstract void getGank();
}
}
我們可以看到胶果,整體上和Google的Demo差不多匾嘱,都是把View和Presenter放到Contract類(lèi)里面統(tǒng)一管理,我這里多加了個(gè)Model接口早抠,我不推薦在Presenter進(jìn)行Model操作霎烙,本來(lái)很優(yōu)雅的一件事,在Presenter進(jìn)行Model操作的話蕊连,感覺(jué)就差了很多悬垃,要做一個(gè)優(yōu)雅的程序員。不同的地方是Model和View接口繼承了BaseModel接口和BaseView接口甘苍,Presenter變成了一個(gè)抽象類(lèi)尝蠕,繼承于BasePresenter抽象類(lèi),傳入兩個(gè)泛型View载庭、Model看彼。為啥呢廊佩?我們接著看Base包下的三個(gè)Base類(lèi)。
Base類(lèi)
BaseView:
public interface BaseView {
}
BaseModel;
public interface BaseModel {
}
BasePresenter:
public class BasePresenter<V extends BaseView,M extends BaseModel> {
protected V mView;
protected M mModel;
private CompositeSubscription mCompositeSubscription;
protected void addSubscribe(Subscription subscription) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscription);
}
public void unSubscribe() {
if (mView != null) {
mView = null;
}
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.clear();
}
}
BaseModel和BaseView接口里面是空的靖榕,在這里我只是為了在BasePresenter中提供一個(gè)約束标锄。當(dāng)然,如果你有其它全局的需求茁计,可以在里面添加一些方法料皇。重點(diǎn)是BasePresenter這個(gè)抽象類(lèi),傳入一個(gè)View和Model星压,并將其用protected關(guān)鍵字修飾践剂,這樣,在它的子類(lèi)中就可以直接對(duì)其賦值和使用了娜膘。加入CompositeSubscription變量逊脯,是為了對(duì)RxJava進(jìn)行管理。unSubscribe方法對(duì)View進(jìn)行null賦值和清除Rx的Subscription(訂閱)竣贪,防止內(nèi)存泄漏男窟。
Presnter橋梁:
接下來(lái)看看這個(gè)很重要的類(lèi),作為連接Model和View的橋梁贾富,這里又是怎么做的呢?
public class MainPresenter extends MainContract.Presenter {
public MainPresenter(MainContract.View view) {
mView = view;
mModel = new MainModel();
}
@Override
public void getGank() {
Subscription subscribe = mModel.getGank()
.subscribe(new Subscriber<Gank>() {
@Override
public void onStart() {
mView.showDialog();
}
@Override
public void onCompleted() {
mView.hideDialog();
}
@Override
public void onError(Throwable e) {
mView.onFail(e.getMessage());
onCompleted();
}
@Override
public void onNext(Gank gank) {
mView.onSucceed(gank);
}
});
addSubscribe(subscribe);
}
}
構(gòu)造方法傳進(jìn)一個(gè)View牺六,并且new了一個(gè)Model對(duì)象颤枪,直接賦值給父類(lèi)中的View和Model。然后下面復(fù)寫(xiě)的方法中調(diào)用Model中的方法淑际,再將結(jié)果通過(guò)View中的方法傳出去畏纲,這是很原始的MVP方式。最后addSubscribe添加到訂閱隊(duì)列中春缕。
Model處理數(shù)據(jù)
Model分出來(lái)盗胀,而不在Presenter處理,其實(shí)也是為了簡(jiǎn)潔锄贼,當(dāng)你要處理很多數(shù)據(jù)的時(shí)候票灰,Presenter就會(huì)變得很亂了。
public class MainModel implements MainContract.Model {
@Override
public Observable<Gank> getGank() {
return ApiEngine.getInstance().getApiService()
.getGank("1")
.compose(RxSchedulers.<Gank>switchThread());
}
}
我這里很簡(jiǎn)單宅荤,就獲取ApiService對(duì)象屑迂,然后調(diào)用API。最后compose傳進(jìn)我自己定義的線程切換器:
public class RxSchedulers {
public static <T> Observable.Transformer<T, T> switchThread() {
return new Observable.Transformer<T, T>() {
@Override
public Observable<T> call(Observable<T> tObservable) {
return tObservable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
將網(wǎng)絡(luò)請(qǐng)求的IO線程切換回Android的UI主線程冯键,才能繼續(xù)進(jìn)行在Presenter中的操作惹盼。
BaseActivity中的封裝
在前面我們可以看到BasePresenter中有兩個(gè)方法,一個(gè)是添加訂閱addSubscribe惫确,另一個(gè)是unSubscribe解除訂閱手报。我們只看到了在Presenter中使用了addSubscribe蚯舱,而沒(méi)有看到unSubscribe在哪使用了。因?yàn)橐乐箖?nèi)存泄漏掩蛤,所以當(dāng)然要在和生命周期相關(guān)的地方進(jìn)行釋放資源枉昏,這個(gè)地方只有我們所說(shuō)的View了,也就是Activity和Fragment中盏档。我們先開(kāi)看一下相關(guān)代碼:
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity {
protected P mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (onCreatePresenter() != null) {
mPresenter = onCreatePresenter();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.unSubscribe();
}
}
protected abstract P onCreatePresenter();
}
通過(guò)泛型確定一個(gè)Presenter的類(lèi)型凶掰,然后使用抽象方法onCreatePresenter對(duì)其進(jìn)行賦值,最后在onDestroy方法中進(jìn)行資源的釋放蜈亩。繼承這個(gè)BaseActivity類(lèi)的Activity懦窘,就不用每次都在onDestroy進(jìn)行同樣的操作啦~達(dá)到簡(jiǎn)潔的目的。同理稚配,F(xiàn)ragment中也是同樣的畅涂,只是在生命周期的onResume和onPause中分別進(jìn)行Presenter的賦值和資源的釋放。這里我就不貼代碼道川,可以上我的Github看午衰。\
Retrofit引擎封裝
/**
* Created by Fangzheng on 2017/9/21.
*/
public class ApiEngine {
private volatile static ApiEngine apiEngine;
private Retrofit retrofit;
private ApiEngine() {
//日志攔截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//緩存
int size = 1024 * 1024 * 100;
File cacheFile = new File(App.getContext().getCacheDir(), "OkHttpCache");
Cache cache = new Cache(cacheFile, size);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(12, TimeUnit.SECONDS)
.writeTimeout(12, TimeUnit.SECONDS)
.writeTimeout(12, TimeUnit.SECONDS)
.addNetworkInterceptor(new NetworkInterceptor())
.addInterceptor(loggingInterceptor)
.cache(cache)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(ApiService.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public static ApiEngine getInstance() {
if (apiEngine == null) {
synchronized (ApiEngine.class) {
if (apiEngine == null) {
apiEngine = new ApiEngine();
}
}
}
return apiEngine;
}
public ApiService getApiService() {
return retrofit.create(ApiService.class);
}
}
用了單例模式,在構(gòu)造方法中只初始化一次Retrofit和Okhttp冒萄。雙重鎖的方式獲取單例臊岸,然后再根據(jù)需要獲取ApiService,如果你有很多個(gè)不同源的API尊流,那就可以創(chuàng)建多個(gè)getXXXXApiService帅戒。
結(jié)語(yǔ)
看下運(yùn)行效果