Android網絡開源庫-Retrofit(五)簡易封裝

1.前言

Rrtrofit的擴展性很強,如果對retrofit不熟悉的話,是很難應對各種各樣的需求的。因此,在這裏,做一下簡單的封裝。主要爲了下面三點需求:

  1. 使用簡單
  2. 加密處理
  3. 錯誤處理

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);
    }

這麼依賴,過程就非常簡單了。

  1. 首先在model的構造函數中create serviceApi,
  2. 進行網絡請求

而網絡請求的代碼,經過稍微的封裝就變爲以下僞代碼。

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.總結

上面的這種方法,解決了我們公司 目前項目的絕大部分問題。相信對你們應該也是有用的,瞭解一下吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章