RxJava+Retrofit+ViewModel+LiveData的封裝
前言
我們可以在github上看到當今比較流行的架構方式有:todo-mvp,todo-mvp-clean,todo-mvvm,todo-mvp-rxjava等等(可以參考:ofollow,noindex">https://github.com/googlesamples/android-architecture ),而當今比較流行的技術莫過於:
1.響應式程式設計+非同步任務
2.網路框架
Retrofit,Okhttp
3.Android Jetpack系列
LiveData ,ViewModel,Room等(可以在Android官網看到https://developer.android.google.cn/jetpack/ )
因此我做了一個demo對RxJava+Retrofit+ViewModel+LiveData的封裝;
原始碼傳送門:https://github.com/daxiaaaaaaaaaaaaa/SmartArchitecture
1.RxJava+Retrofit網路框架的封裝
package com.architecturedemo.http; import com.architecturedemo.base.BaseDto; import com.architecturedemo.dto.LoginDto; import com.architecturedemo.utils.HttpUtil; import com.mujin.keji.architecturedemo.vo.LoginVo; import io.reactivex.Flowable; public class Api { /** * 登入介面 * * @param vo * @return */ public static Flowable<BaseDto<LoginDto>> login(LoginVo vo) { return RequetRetrofit.getInstance().login(HttpUtil.convertVo2Params(vo)); } }
package com.architecturedemo.http; import com.architecturedemo.base.BaseDto; import com.architecturedemo.dto.LoginDto; import java.util.Map; import io.reactivex.Flowable; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.POST; /** * api介面 * * @author weishixiong * @Time 2018-04-2 */ public interface ApiService { /** * 登入 * post * 表單提交 * * @param map * @return */ @FormUrlEncoded @POST("login") Flowable<BaseDto<LoginDto>> login(@FieldMap Map<String, String> map); }
package com.architecturedemo.http; import android.util.Log; import com.architecturedemo.CcsApplication; import com.architecturedemo.Constant; import com.architecturedemo.interceptor.AddCookiesInterceptor; import com.architecturedemo.interceptor.ReceivedCookiesInterceptor; import com.facebook.stetho.okhttp3.StethoInterceptor; import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import com.mujin.keji.architecturedemo.ssl.SafeHostnameVerifier; import com.mujin.keji.architecturedemo.ssl.SafeTrustManager; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * 普通的網路請求用到的Retrofit */ public class RequetRetrofit { private static final String TAG = "RequetRetrofit"; /** * 建立okhttp相關物件 */ private static OkHttpClient okHttpClient; /** * 建立Retrofit相關物件 */ private static Retrofit retrofit; public static ApiService getInstance() { if (okHttpClient == null) { /** * 建立okhttp相關物件 */ okHttpClient = new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) {//訪問網路請求,和服務端響應請求時。將資料攔截並輸出 Log.e(TAG, "log: " + message); } }).setLevel(HttpLoggingInterceptor.Level.BODY))//Log等級 .connectTimeout(Constant.Server.TIME_OUT, TimeUnit.SECONDS)//超時時間 .readTimeout(Constant.Server.TIME_OUT, TimeUnit.SECONDS) .writeTimeout(Constant.Server.TIME_OUT, TimeUnit.SECONDS) .addNetworkInterceptor(new StethoInterceptor()) .addInterceptor(new AddCookiesInterceptor()) //這部分 .addInterceptor(new ReceivedCookiesInterceptor()) //這部分 .hostnameVerifier(new SafeHostnameVerifier()) .sslSocketFactory(CcsApplication.getSslSocket(),new SafeTrustManager()) // .build(); } if (retrofit == null) { retrofit = new Retrofit.Builder() .baseUrl(Constant.Server.ROOT_URL)//BaseUrl .client(okHttpClient)//請求的網路框架 .addConverterFactory(GsonConverterFactory.create())//解析資料格式 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 使用RxJava作為回調適配器 .build(); } return retrofit.create(ApiService.class); } }
2.自定義攔截器 實現登入校驗,判斷sessionId是否過期
package com.architecturedemo.interceptor; import com.alibaba.fastjson.JSONObject; import com.architecturedemo.Constant; import com.architecturedemo.utils.SPUtil; import java.io.IOException; import java.util.ArrayList; import java.util.List; import okhttp3.Interceptor; import okhttp3.Response; /** * 首次請求的處理 * @author : weishixiong * @create : 18/05/03 */ public class ReceivedCookiesInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); if (!originalResponse.headers("Set-Cookie").isEmpty()) { List<String> cookies = new ArrayList<>(); for (String header : originalResponse.headers("Set-Cookie")) { cookies.add(header); } String cookieStr = JSONObject.toJSONString(cookies); SPUtil.putData(Constant.SP.SP, Constant.SP.SESSION_ID, cookieStr); } return originalResponse; } }
package com.architecturedemo.interceptor; import android.util.Log; import com.alibaba.fastjson.JSONObject; import com.architecturedemo.Constant; import com.architecturedemo.utils.SPUtil; import java.io.IOException; import java.util.List; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; /** * 非首次請求的處理 * @author : weishixiong * @create : 18/05/03 */ public class AddCookiesInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); String cookieStr = SPUtil.getData(Constant.SP.SP, Constant.SP.SESSION_ID, String.class, null); List<String> cookies = JSONObject.parseArray(cookieStr, String.class); if (cookies != null) { for (String cookie : cookies) { builder.addHeader("Cookie", cookie); Log.v("OkHttp", "Adding Header: " + cookie); // This is done so I know which headers are being added; this interceptor is used after the normal logging of OkHttp } } return chain.proceed(builder.build()); } }
3.RxJava Subscriber的封裝
package com.architecturedemo.base; import android.arch.lifecycle.MutableLiveData; import android.os.Build; import android.support.annotation.RequiresApi; import com.architecturedemo.Constant; import com.architecturedemo.exception.ApiException; import com.architecturedemo.exception.ExceptionEngine; import com.architecturedemo.exception.ServerException; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; /** * 自定義請伺服器被觀察者 * * @author weishixiong * @Time 2018-04-12 */ public class BaseHttpSubscriber<T> implements Subscriber<BaseDto<T>> { //異常類 private ApiException ex; public BaseHttpSubscriber() { data = new MutableLiveData(); } private MutableLiveData<BaseDto<T>> data; public MutableLiveData<BaseDto<T>> get() { return data; } public void set(BaseDto<T> t) { this.data.setValue(t); } public void onFinish(BaseDto<T> t) { set(t); } @Override public void onSubscribe(Subscription s) { // 觀察者接收事件 = 1個 s.request(1); } @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) @Override public void onNext(BaseDto<T> t) { if (t.getCode() == Constant.Server.SUCCESS_CODE) { onFinish(t); }else { ex = ExceptionEngine.handleException(new ServerException(t.getCode(), t.getMsg())); getErrorDto(ex); } } @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) @Override public void onError(Throwable t) { ex = ExceptionEngine.handleException(t); getErrorDto(ex); } /** * 初始化錯誤的dto * * @param ex */ private void getErrorDto(ApiException ex) { BaseDto dto = new BaseDto(); dto.setCode(ex.getCode()); dto.setMsg(ex.getMsg()); onFinish((BaseDto<T>) dto); } @Override public void onComplete() { } }
4.ViewModel+LiveData對資料的封裝
package com.architecturedemo.viewmodel; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.ViewModel; import com.architecturedemo.Constant; import com.architecturedemo.base.BaseDto; import com.architecturedemo.dto.LoginDto; import com.architecturedemo.repository.ILoginRepository; import com.architecturedemo.repository.LoginRepository; import com.architecturedemo.utils.SPUtil; import com.mujin.keji.architecturedemo.vo.LoginVo; /** * 登入viewmodel * * @author weishixiong * @Time 2018-04-2 */ public class LoginViewModel extends ViewModel { private LiveData<BaseDto<LoginDto>> loginDtoLiveData; private ILoginRepository loginRepository; public LiveData<BaseDto<LoginDto>> getLoginDtoLiveData() { return loginDtoLiveData; } /** * 登入 * @param username * @param password */ public voidlogin(String username,String password){ SPUtil.clearData(Constant.SP.SP); loginRepository = new LoginRepository(); LoginVo vo = new LoginVo(); vo.setPassword(password); vo.setUsername(username); this.loginDtoLiveData = loginRepository.login(vo); } }
因為程式碼比較多就不一一貼出來了,有興趣的同學可以下載demo看看