1.前言
Rrtrofit的擴展性很強,如果對retrofit不熟悉的話,是很難應對各種各樣的需求的。因此,在這裏,做一下簡單的封裝。主要爲了下面三點需求:
- 使用簡單
- 加密處理
- 錯誤處理
2.怎樣才能簡單使用
爲了簡單粗暴,我做了以下工作。
- 使用單例Retrofit
- 引入RxJava
在這裏,如何Retrofit單例化,就不多說了,大致代碼如下:
Retrofit.Builder builder = new Retrofit.Builder() .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(SecurityGsonConverterFactory.create())
.baseUrl("xxx");
client = new OkHttpClient().newBuilder()
.addNetworkInterceptor(new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY))
.addNetworkInterceptor(new StethoInterceptor())
.addInterceptor(new SecurityInterceptor(context))
.retryOnConnectionFailure(true)
// TODO: 2016/8/24 緩存、cookie等
.connectTimeout(5_000, TimeUnit.MILLISECONDS)
.readTimeout(5_000, TimeUnit.MILLISECONDS)
.build();
retrofit = builder.client(client).build();
其中的SecurityInterceptor和SecurityGsonConverterFactory,在稍後會說。
我們來看下引入RxJava之後代碼如何編寫。以一個獲取用戶信息爲例。
Api
@FormUrlEncoded
@POST("v1/api.user.profile.get")
Observable<UserResponse> get(
@Field("uid") String uid
);
首先,我們需要ServiceApi對象,在這裏進行一次簡單封裝,如下:
public static <T> T createApi(Class<T> clz) {
return (T) retrofit.create(clz);
}
在需要的地方,一般是model裏,在構造函數中,我們初始化。
this.userApi = RetrofitClient.createApi(UserApi.class);
接下來就是->Observable
Observable<UserResponse> observable = userApi.get(params[0]);
接着便是,Subscriber,因爲這裏我對Subscriber也進行了封裝,但是我們這裏先暫時不管。先看下代碼
NormalSubscriber<UserResponse> subscriber = new NormalSubscriber<UserResponse>(context) {
@Override
public void onNext(UserResponse userResponse) {
response.onSuccess(userResponse, UserModel.class, ApiHelper.userApi.GET);
}
};
,需要額外說明的是,HttpResponse,是封裝的一個接口。
public interface HttpResponse {
<T>void onSuccess(T response, Class<?> clz,String methodName);
void onError(Throwable t);
}
,最後,進行訂閱操作
CoreUtil.subscribe(observable, subscriber);
這個對應的詳細代碼爲:
public static <T> void subscribe(Observable<T> observable, Subscriber<T> subscriber) {
observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
這麼依賴,過程就非常簡單了。
- 首先在model的構造函數中create serviceApi,
- 進行網絡請求
而網絡請求的代碼,經過稍微的封裝就變爲以下僞代碼。
Observable<FriendApplyListResponse> observable = friendApi.applylist();
NormalSubscriber<FriendApplyListResponse> subscriber = new NormalSubscriber<FriendApplyListResponse>(context) {
@Override
public void onNext(FriendApplyListResponse friendListResponse) {
response.onSuccess(friendListResponse,FriendModel.class,ApiHelper.friendApi.APPLYLIST);
}
};
CoreUtil.subscribe(observable,subscriber);
因爲,我這裏還是傳統mvc,因此,在onSuccess裏,需要以下內容,Response,Model.class,接口名稱。這樣,在Activity裏纔好區分。
3.如何進行加密解密
說道這裏,上面提到過的SecurityGsonConverterFactory,SecurityInterceptor就祈禱作用了。先說加密:
3.1 加密
在SecurityInterceptor的intercept方法當中,相關代碼如下:
Request request = chain.request();
RequestBody oldBody = request.body();
Buffer buffer = new Buffer();
oldBody.writeTo(buffer);
StringBuffer stringBuffer = new StringBuffer();
String s;
while ((s = buffer.readUtf8Line()) != null) {
stringBuffer.append(s);
}
StringBuilder newString = encrypt(stringBuffer.toString().trim());
上訴encrypt 方法就是進行參數加密的,各位可以根據自己公司的需求進行編寫。
然後。進行重組即可
RequestBody body = RequestBody.create(mediaType, newString);
request = request.newBuilder()
.header("Content-Type", body.contentType().toString())
.header("Content-Length", String.valueOf(body.contentLength()))
.header("Authorization", SESSION.getInstance().getToken())
.header("UserAgent", "Platform/Android, Device/" + model + ", Lang/" + UserAgent.getInstance().lang + ", ScreenWidth/" + UserAgent.getInstance().width + ", ScreenHeight/" + UserAgent.getInstance().height)
.header("UDID", UserAgent.getInstance().UDID)
.header("Ver", UserAgent.getInstance().ver)
.header("Sign", signString)
.method(request.method(), body)
.build();
Response response = chain.proceed(request);
大致過程就是這樣,這樣,我們就完成了參數加密的過程。
3.1 解密
這涉及到了SecurityGsonConverterFactory,這個和GsonConverterFactory的區別之處就在於SecurityGsonResponseBodyCoverter,在這個類的convert方法中,進行解密。
String encryptString = value.string();
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(encryptString.trim());
String decrypt_data = XXTEA.decryptBase64StringToString(jsonObject.optString("data"), UserAppConst.AppDataKey);
jsonObject = new JSONObject(decrypt_data);
} catch (JSONException e) {
e.printStackTrace();
}
JsonReader jsonReader = gson.newJsonReader(new StringReader(jsonObject.toString()));
try {
return adapter.read(jsonReader);
}finally {
value.close();
}
當然,上面對應的服務器返回數據應該是這樣的:
//僞數據啊 json格式的
{
success:xxx
error_code:xxx
data:{
xxx
}
}
在這裏,我有一點建議。就是我們前面對應的UserResponse應該是data裏的數據,當然,data裏也可以放error數據,比如說什麼Token失效了等等,但是,這個時候要求返回http code 400,爲什麼呢?這樣方便我們做錯誤處理。
4.錯誤處理
我們這裏的錯誤處理就在上面提到過得NormalSubscriber裏。我們來看看。我們在onError裏做處理,需要說明的是,onError裏,有兩種情況會調用onError
- 出現異常
- 服務器不是200-299相應嗎(ps:猜測,知道的朋友告訴下,因爲業務小的關係,沒遇到過)
public void onError(Throwable e) {
// TODO: 2016/9/2 添加其他異常判斷
if (e instanceof ConnectException) {
ToastUtil.toastShow(mContext, e.getLocalizedMessage());
return;
}
//當然這裏還有其他異常,比如說SocketTimeoutException,MalformedJsonException,IllegalStateException等等。這些異常是代碼引起的異常,我們需要了解另一種異常。HttpException,這個異常很重要,這是我們錯誤處理的關鍵,比如說我們token過期了,在服務器返回的數據裏面,data裏應該是tiken無效,並且 http code應該是400,bad request。
String errorJson = ((HttpException) e).response().errorBody().string();
JSONObject jsonObject = new JSONObject(errorJson);
String errorMessage = jsonObject.optString("data");
//這裏的errorMessage應該就是錯誤原因了。我們可以根據這個或者,error_code進行對應的處理
}
5.總結
上面的這種方法,解決了我們公司 目前項目的絕大部分問題。相信對你們應該也是有用的,瞭解一下吧。