最近把MVP+Retrofit2.0+RxJava2.0封裝了一下,在項目里用了之后几缭,發(fā)現代碼確實簡潔了很多,看上去簡直不要太爽沃呢!哈哈年栓,大圣寫代碼,自己寫自己夸樟插!
關于MVP的定義之類的內容我就不再贅述了韵洋,無非就是Model竿刁,View黄锤,Presenter。網上千篇文章千篇樣食拜,所以我理解著就是根據自己的風格寫就行了鸵熟,沒有一個統(tǒng)一的定義非要怎么樣怎么樣去寫。Retrofit2.0和RxJava2.0的基礎知識各位看官也就移步別苑自己去看吧负甸。這里就直接說我寫的這個封裝了流强。
首先我先定義了一個Retrofit的管理類:RetrofitManager。
public class RetrofitManager {
private static int DEFAULT_TIMEOUT = 5;
private RetrofitManager() {
}
public static RetrofitManager getInstance() {
return RetrofitManagerHolder.INSTANCE;
}
private static class RetrofitManagerHolder {
private static RetrofitManager INSTANCE = new RetrofitManager();
}
public <S> S create(Class<S> service, String baseUrl) {
Retrofit retrofit = new Retrofit.Builder()
.client(getOkHttpClient())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit.create(service);
}
private OkHttpClient getOkHttpClient() {
return new OkHttpClient.Builder()
.proxy(Proxy.NO_PROXY)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
}
}
這個類里面定義了Retrofit的獲取方法create()呻待,其中涉及到了OkHttp的同步封裝打月。單例模式用的是靜態(tài)內部類的方式(《設計模式》里最推薦的定義單例模式的方式)。
其次是定義了一個接口:RetrofitService蚕捉。
public interface RetrofitService {
@GET(HttpUtil.GET_COUPON_INFO_URL)
Observable<CouponInfoEntity> getCouponInfo(@QueryMap Map<String, String> params);
@FormUrlEncoded
@POST(HttpUtil.CONSUME_COUPON_URL)
Observable<ConsumeCouponEntity> consumeCoupon(@Query("randomseed") String randomSeed,
@Query("token") String token,
@FieldMap Map<String, String> params);
}
這里面放的就是Retrofit請求接口的方式奏篙,注意:返回類型為Observable<T>,這是因為集成了RxJava。
然后就是我們的MVP了秘通,首先是Presenter为严。我這里定義了一個BasePresenter。
public interface BasePresenter {
void add(Disposable disposable);
void clear();
}
兩個方法:add()和clear()肺稀,這兩個方法就是用來管理RxJava的生命周期的第股。
其次是View。我同樣定義了一個BaseView话原。
public interface BaseView {
void showResult(String result);
}
這里面就是根據各位自己的喜好自行定義了夕吻。
最后是Model。同樣的稿静,BaseMode梭冠。
public class BaseModel {
public <T> Observable<T> observable(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
}
這個方法實現的是返回一個切換線程的Observable,這樣就不用在后面每次寫一遍了改备。
然后我還定義了一個RxPresenter控漠,用來實現BasePresenter。
public class RxPresenter implements BasePresenter {
private CompositeDisposable disposables;
@Override
public void add(Disposable disposable) {
if (null == disposables) {
disposables = new CompositeDisposable();
}
disposables.add(disposable);
}
@Override
public void clear() {
if (null != disposables && disposables.isDisposed()) {
disposables.clear();
}
}
}
CompositeDisposable是一個管理Disposable的集合悬钳,具體怎么實現的大家可以去看下源碼盐捷,很好理解。
還有一個BaseObserver默勾,用來簡潔化請求接口時的操作碉渡。
public abstract class BaseObserver<T> implements Observer<T> {
private Context context;
public BaseObserver(Context context) {
this.context = context;
}
@Override
public void onSubscribe(Disposable d) {
DialogFactory.showLoadingDialog(context);
onDisposable(d);
}
@Override
public void onNext(T t) {
onSuccess(t);
}
@Override
public void onError(Throwable e) {
LogUtil.i("onError: " + e.getMessage());
DialogFactory.dismissLoadingDialog();
}
@Override
public void onComplete() {
DialogFactory.dismissLoadingDialog();
}
protected abstract void onSuccess(T t);
protected abstract void onDisposable(Disposable d);
}
這是一個抽象類,實現了Observer母剥,其中我定義了一個構造方法滞诺,用于獲取上下文Context來顯示加載對話框。這個類定義出來之后环疼,后面實現請求接口的時候就只需要執(zhí)行和實現onSuccess()和onDisposable()方法习霹。
最后是BaseActivity類,此處onDestroy()方法里清除所有的disposable炫隶。
public abstract class BaseActivity<P extends RxPresenter> extends Activity {
protected P presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(initContentView());
ButterKnife.bind(this);
UIUtil.add(this);
init();
presenter = createPresenter();
initListener();
showView();
}
protected abstract int initContentView();
protected abstract void init();
protected abstract P createPresenter();
protected abstract void initListener();
protected abstract void showView();
@Override
protected void onDestroy() {
super.onDestroy();
presenter.clear();
UIUtil.remove(this);
}
}
最后看一下具體實現:
比如主界面里我要請求兩個接口淋叶,定義兩個按鈕,具體實現layout的xml就不貼了伪阶。
public class MainActivity extends BaseActivity<HomePresenter> implements HomeView {
@BindView(R.id.edit_input_coupon_code)
EditText inputCouponCode;
private long exitTime;
@Override
protected int initContentView() {
return R.layout.activity_main;
}
@Override
protected void init() {
}
@Override
protected HomePresenter createPresenter() {
return new HomePresenter(this, this);
}
@Override
protected void initListener() {
}
@Override
protected void showView() {
}
@Override
public String getCouponCode() {
return StringUtil.getStringValue(inputCouponCode);
}
@Override
public void onConsumeCouponSuccess(ConsumeCouponEntity consumeCouponEntity) {
}
@Override
public void onGetCouponInfoSuccess(CouponInfoEntity couponInfoEntity) {
}
@Override
public void showResult(String result) {
LogUtil.i(result);
}
@OnClick(R.id.img_setting)
public void onSetting() {
UIUtil.openUI(this, SettingActivity.class);
}
@OnClick(R.id.btn_get_coupon_info)
public void onGetCouponInfo() {
presenter.getCouponInfo();
}
@OnClick(R.id.btn_consume_coupon)
public void onConsumeCoupon() {
presenter.consumeCoupon();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
ToastUtil.toastShort(this, "再按一次退出程序");
exitTime = System.currentTimeMillis();
} else {
UIUtil.exit();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
這里我們用的是HomePresenter和HomeView煞檩。然后我們再來看一下這兩個類是怎么實現的。
public class HomePresenter extends RxPresenter {
private Context context;
private HomeView homeView;
private HomeModel homeModel;
public HomePresenter(Context context, HomeView homeView) {
this.context = context;
this.homeView = homeView;
homeModel = new HomeModel();
}
/**
* 獲取優(yōu)惠券信息
*/
public void getCouponInfo() {
String randomSeed = String.valueOf(System.currentTimeMillis() / 1000);
String token = "token";
homeModel.getCouponInfo(homeView.getCouponCode(), randomSeed, token)
.subscribe(new BaseObserver<CouponInfoEntity>(context) {
@Override
protected void onSuccess(CouponInfoEntity couponInfoEntity) {
if (couponInfoEntity.Status != 1) {
ToastUtil.toastShort(context, couponInfoEntity.Msg);
return;
}
homeView.onGetCouponInfoSuccess(couponInfoEntity);
homeView.showResult(couponInfoEntity.toString());
}
@Override
protected void onDisposable(Disposable d) {
add(d);
}
});
}
/**
* 核銷優(yōu)惠券
*/
public void consumeCoupon() {
String shopId = (String) SPUtil.getInstance().getData(SPUtil.SHOP_ID, "");
if (null == shopId || shopId.equals("")) {
ToastUtil.toastShort(context, "請先設置店鋪Id");
return;
}
String randomSeed = String.valueOf(System.currentTimeMillis() / 1000);
String token = "token";
homeModel.consumeCoupon(homeView.getCouponCode(), shopId, randomSeed, token)
.subscribe(new BaseObserver<ConsumeCouponEntity>(context) {
@Override
protected void onSuccess(ConsumeCouponEntity consumeCouponEntity) {
if (consumeCouponEntity.Status != 1) {
ToastUtil.toastShort(context, consumeCouponEntity.Msg);
return;
}
homeView.onConsumeCouponSuccess(consumeCouponEntity);
homeView.showResult(consumeCouponEntity.toString());
}
@Override
protected void onDisposable(Disposable d) {
add(d);
}
});
}
}
看栅贴,請求接口部分多么簡潔斟湃,數據解析已經通過RetrofitManager里設置的.addCallAdapterFactory(RxJava2CallAdapterFactory.create())實現了,媽媽再也不用擔心我手動解析的麻煩了檐薯。在onSuccess()方法里接收就行了凝赛。漂亮!
再看HomeView。
public interface HomeView extends BaseView {
String getCouponCode();
void onConsumeCouponSuccess(ConsumeCouponEntity consumeCouponEntity);
void onGetCouponInfoSuccess(CouponInfoEntity couponInfoEntity);
}
其中getCouponCode()方法用來獲取界面上輸入框里的值哄酝,用于HomePresenter里請求接口傳參友存,而onConsumeCouponSuccess()和onGetCouponInfoSuccess()方法用來接收返回數據的實體類對象。
別忘了陶衅,還有一個HomeModel呢屡立,那么它是什么樣的呢?到底長的是鞋拔子臉還是豬腰子臉呢搀军?
public class HomeModel extends BaseModel {
public Observable<CouponInfoEntity> getCouponInfo(String couponCode, String randomSeed, String token) {
Map<String, String> params = new HashMap<>();
params.put("couponCode", couponCode);
params.put("randomseed", randomSeed);
params.put("token", token);
return observable(RetrofitManager.getInstance().create(RetrofitService.class, HttpUtil.BASE_URL).getCouponInfo(params));
}
public Observable<ConsumeCouponEntity> consumeCoupon(String couponCode, String storeId, String randomSeed, String token) {
Map<String, String> params = new HashMap<>();
params.put("CouponCode", couponCode);
params.put("StoreId", storeId);
return observable(RetrofitManager.getInstance().create(RetrofitService.class, HttpUtil.BASE_URL).consumeCoupon(randomSeed, token, params));
}
}
哇膨俐!原來長得跟本山大叔一樣,太神奇了罩句。(開個玩笑焚刺,如有侵權,本人概不承認C爬谩)
這里就是實現接口請求了乳愉,用了BaseMode()里的observable()。
大體就是這樣一個風格屯远,這是我自己適合的風格蔓姚,權且拋磚引玉,語言描述上有不完善之處慨丐,代碼上應該也有不盡人意之處坡脐,還請各位大神幫忙指點指點,本人期待進步房揭!
其實之前寫了一個Retrofit初接觸备闲,當時還沒用到MVP和RxJava2.0,就已經覺得自己很牛13了⊥北現在看到這個封裝之后恬砂,感覺代碼果然好簡潔,水平高了很多伶唯。再看看那個Retrofit初接觸觉既,感覺好low熬屙铩乳幸!哈哈,說明我還是進步了钧椰。
行了粹断,就這些吧。記在這兒嫡霞,拋磚引玉瓶埋,敬請指教。也方便自己以后使用的時候查看。