去年因爲項目重構寫了個 MVP 模式的框架 Demo,採用 RxJava + Retrofit 作爲網路請求框架,一直想整理一下,但上半年確實浪得一(*),現在難得有點空閒,馬上整理一下,作爲回顧,查漏補缺。【聽着怎麼這麼耳熟 ~ 】
相關文章:《 MVP(Model-View-Presenter)框架整理 》
網路請求框架基於以下版本:
// RXJava
compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
// Retrofit
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
// JSON Parsing
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
// Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* @author 小僑
* @time 2017/7/21 14:52
* @desc RetrofitHelper 類
* 主要用來構造 retrofit 對象以及得到接口類對象
*/
public class RetrofitHelper {
private static final RetrofitHelper mRetrofitHelper = new RetrofitHelper();
private Retrofit mRetrofit;
private RetrofitHelper() {
setRetrofit();
}
public static RetrofitHelper getInstance(){
return mRetrofitHelper;
}
private void setRetrofit() {
mRetrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/")
.addConverterFactory(GsonConverterFactory.create()) // 添加 gson 關聯
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 添加 Rxjava 關聯
.build();
}
public ApiService getApiService() {
ApiService apiService = mRetrofit.create(ApiService.class);
return apiService;
}
}
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Path;
/**
* @author 小僑
* @time 2017/7/21 14:44
* @desc 數據接口類
*/
public interface ApiService {
// 豆瓣250:http://api.douban.com/v2/movie/top250
@GET("v2/movie/top250")
Observable<Movies> getMovies250();
// 條目信息:http://api.douban.com/v2/movie/subject/1764789
@GET("v2/movie/subject/{id}")
Observable<Movie> getMoviesById(@Path("id") int id);
}
import io.reactivex.disposables.CompositeDisposable;
/**
* @author 小僑
* @time 2017/7/21 10:11
* @desc Presenter 基類
*/
public abstract class BasePresenter<V extends IView, M extends IModel> implements IPresenter {
protected V mView;
protected M mModel;
// 用於取消訂閱:http://blog.csdn.net/tyrantu1989/article/details/69053816
// RxJava 2.0 CompositeDisposable 取代 CompositeSubscription
protected CompositeDisposable mCompositeDisposable;
public BasePresenter() {
mModel = initModel();
mCompositeDisposable = new CompositeDisposable();
}
protected abstract M initModel();
@Override
public void attachView(IView view) {
mView = (V) view;
}
@Override
public void detachView() {
// activity 銷燬的時候,切斷所有訂閱關係
mCompositeDisposable.clear();
mView = null;
}
}
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* @author 小僑
* @time 2017/7/21 10:25
* @desc movie 頁面 Presenter 層
*/
public class MoviePresenter extends BasePresenter<MovieActivity, MovieModel> implements MovieContract.IPresenter {
// MoviePresenter<MovieActivity> 已經在 MovieActivity 中 onCreate 時綁定了
// MoviePresenter<MovieModel> 已經在 MoviePresenter 新建時調用 initModel()方法綁定了
@Override
protected MovieModel initModel() {
return new MovieModel();
}
@Override
public void getTop250() {
mModel.downloadTop250()
.subscribeOn(Schedulers.io()) // 指定發射事件的線程
.observeOn(AndroidSchedulers.mainThread()) // 指定消費事件的線程
.subscribe(new Observer<Movies>() {
@Override
public void onSubscribe(Disposable d) {
// 添加訂閱事件到管理器,以便統一管理
mCompositeDisposable.add(d); // TODO: 2017/8/28 思考看能不能抽取???除非重寫 Observer?
mView.showLoading();
// TODO: 這裏不用做 mView != null 判斷,是因爲在 BaseActivity 已經做了 onDestroy()時解綁了
}
@Override
public void onNext(Movies movies) {
mView.showTop250(movies);
}
@Override
public void onError(Throwable e) {
mView.hideLoading();
mView.showToast(e.getMessage());
}
@Override
public void onComplete() {
mView.hideLoading();
}
});
}
@Override
public void getMovieInfo(int id) {
mModel.downloadMovieInfo(id)
.subscribeOn(Schedulers.io()) // 另開一個線程來下載(指定一個觀察者在哪個調度器上觀察這個Observable)
.observeOn(AndroidSchedulers.mainThread()) // 下載完回到主線程(指定 Observable 自身在哪個調度器上執行)
.subscribe(new Observer<Movie>() {
@Override
public void onSubscribe(Disposable d) {
mCompositeDisposable.add(d);
mView.showLoading();
}
@Override
public void onNext(Movie movie) {
mView.showMovieInfo(movie);
}
@Override
public void onError(Throwable e) {
mView.hideLoading();
mView.showToast(e.getMessage());
}
@Override
public void onComplete() {
mView.hideLoading();
}
});
}
}
import io.reactivex.Observable;
/**
* @author 小僑
* @time 2017/7/21 10:30
* @desc movie 頁面 Model 層
*/
public class MovieModel implements IModel, MovieContract.IModel {
@Override
public Observable<Movies> downloadTop250() {
ApiService apiService = RetrofitHelper.getInstance().getApiService();
return apiService.getMovies250();
}
@Override
public Observable<Movie> downloadMovieInfo(int id) {
ApiService apiService = RetrofitHelper.getInstance().getApiService();
return apiService.getMoviesById(id);
}
}
import io.reactivex.Observable;
/**
* @author 小僑
* @time 2017/7/21 10:23
* @desc movie 頁面合約類:用於約束/定義 movie 頁面的特有方法
*/
public class MovieContract {
/**
* View
*/
interface IView {
void showTop250(Movies movies);
void showMovieInfo(Movie movie);
}
/**
* Prezenter
*/
interface IPresenter {
void getTop250();
void getMovieInfo(int id);
}
/**
* Model
*/
interface IModel {
Observable<Movies> downloadTop250();
Observable<Movie> downloadMovieInfo(int id);
}
}
Demo 的 Github地址:
https://github.com/ZhangZeQiao/GeneralFramework.git