兩行代碼搞定發送 Retrofit GET/POST 請求

目前Android開發幾乎都離不開網絡請求,而很多Android App網絡框架都使用Retrofit來發送網絡請求和響應交互,其優點是一底層依賴了強大靈活的Okhttp,二是其符合標準的RESTFUL和後端交互更爽。

本身Retrofit已經封裝得很好了,其使用也很簡單:

//定以接口
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

//獲取實例
Retrofit retrofit = new Retrofit.Builder()
    //設置OKHttpClient,如果不設置會提供一個默認的
    .client(new OkHttpClient())
    //設置baseUrl
    .baseUrl("https://api.github.com/")
    //添加Gson轉換器
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

Call<List<Repo>> call = service.listRepos("octocat");

//異步請求
clone.enqueue(new Callback<List<Repo>>() {
        @Override
        public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
            // Get result bean from response.body()
            List<Repo> repos = response.body();
            ...
        }

        @Override
        public void onFailure(Call<List<Repo>> call, Throwable t) {

        }
    });

所以,即便Retrofit已經做好了大量封裝工作,但是如果在app層使用的時候,如果直接使用將會在代碼中到處充斥了這樣的冗餘代碼,作爲一名資深碼農,肯定不會讓這樣的事情發生,爲了偷懶,先要動腦筋。因此,如何更加方便的使用網絡請求,讓代碼看起來更加優雅,這裏奉上自己的勞動成果,不敢獨享,兩行代碼搞定請求。

先來看看我最後的使用效果,用起來是像下面這樣的:

/**
  * GET 請求 示例:
  */
    private void clickGetButton(){
        String [] strArray = {"Android", "1"};
        RetrofitClient.get("newAppVerInfo", strArray, new BaseCallback<NewAppVerInfo>() {
            @Override
            protected void on200Resp(NewAppVerInfo newAppVerInfo){
                textView.setText("收到GET結果: newAppVerInfo = " + new Gson().toJson(newAppVerInfo));
            }
        });
    }

    /**
     * POST 請求 示例:
     */
    private void clickPostButton(){
        LoginReq loginReq = new LoginReq("13051892977", FormatUtil.md5("123456"), "phoneID");

        RetrofitClient.post("loginInfo", loginReq, new BaseCallback<LoginInfo>() {
            @Override
            protected void on200Resp(LoginInfo loginInfo){
                textView.setText("收到POST結果: loginInfo = " + new Gson().toJson(loginInfo));
            }
        });
    }

說明:
1. 上面 GET 和 POST 請求的第一行都是在組裝發送請求所需要的參數,第二行即發送請求,請求回來的callback在UI線程,可以直接操作view,默認只需要override 200 response時的操作,如果想對Non-200的response override當然也可以。
2. 上例中的newAppVerInfo和loginInfo都是定義在EHService中的接口函數名(見下)。
3. callback中的NewAppVerInfo和LoginInfo是定義在EHService中的接口函數中Call中的類型,也是你自己最終想得到的。

其中EHService如下:

public interface EHService {
    @GET("product/getNewVersionInfo.json")
    Call<NewAppVerInfo> newAppVerInfo(@Query("OSType") String OSType, @Query("DevType") String DevType);

    @Headers({"Content-Type: application/json","Accept: application/json"})//需要添加頭
    @POST("login/login.json")
    Call<LoginInfo> loginInfo(@Body RequestBody loginReq);
}

有沒有覺得很方便,很爽?!好的,接下來,我們來看看是如何實現的!這裏最核心的代碼都封裝在RetrofitClient中,首先對於Retrofit的獲取採用了單例:

public class RetrofitClient {
private static Retrofit retrofit;
    private static EHService service;

    private RetrofitClient(){}

    private static void initRetrofitClient(){
        if(retrofit == null || service == null){
            synchronized (RetrofitClient.class){
                if(retrofit == null){
                    retrofit = new Retrofit.Builder()                           .baseUrl("https://your.base.url/")                            .addConverterFactory(GsonConverterFactory.create()) 
.build();
                    service = retrofit.create(EHService.class);
                }
            }
        }
    }

    public static EHService requestHttp(){
        initRetrofitClient();
        return service;
    }

    public static <E extends BaseModel> Call<E> get(String getFuncName, String[] paramArray, BaseCallback<E> baseCallback){
        Call<E> typeCall = null;

        try {
            typeCall = (Call<E>) invokeMethod(requestHttp(), getFuncName, paramArray);
            typeCall.enqueue(baseCallback);
        }catch (NoSuchMethodException e){
            Log.e("TAG", "NoSuchMethodException e = " + e.getMessage());
        }catch (IllegalAccessException e){
            Log.e("TAG", "IllegalAccessException e = " + e.getMessage());
        }catch (InvocationTargetException e){
            Log.e("TAG", "InvocationTargetException e = " + e.getMessage());
        }catch (Exception e){
            Log.e("TAG", "Exception e = " + e.getMessage());
        }

        return typeCall;
    }

    /**
     * 反射調用方法
     * @param newObj 實例化的一個對象
     * @param methodName 對象的方法名
     * @param args 參數數組
     * @return 返回值
     * @throws Exception
     */
    public static Object invokeMethod(Object newObj, String methodName, Object[] args)throws Exception {
        Class ownerClass = newObj.getClass();
        Class[] argsClass = new Class[args.length];
        for (int i = 0, j = args.length; i < j; i++) {
            argsClass[i] = args[i].getClass();
        }
        Method method = ownerClass.getMethod(methodName, argsClass);
        return method.invoke(newObj, args);
    }

    public static <T, E extends BaseModel> Call<E> post(String postFuncName, T postBean, BaseCallback<E> baseCallback){
        Call<E> typeCall = null;
        RequestBody requestBody = getRequestBody(postBean);
        try {
            Method EHServiceMethod = EHService.class.getDeclaredMethod(postFuncName, RequestBody.class);
            typeCall = (Call<E>) EHServiceMethod.invoke(requestHttp(), requestBody);
            typeCall.enqueue(baseCallback);
        }catch (NoSuchMethodException e){
            Log.e("TAG", "NoSuchMethodException e = " + e.getMessage());
        }catch (IllegalAccessException e){
            Log.e("TAG", "IllegalAccessException e = " + e.getMessage());
        }catch (InvocationTargetException e){
            Log.e("TAG", "InvocationTargetException e = " + e.getMessage());
        }catch (Exception e){
            Log.e("TAG", "Exception e = " + e.getMessage());
        }

        return typeCall;
    }

    public static <T> RequestBody getRequestBody(T postBean){
        //通過Gson將Bean轉化爲Json字符串形式
        String reqBeanJson = new Gson().toJson(postBean);
        return RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), reqBeanJson);
    }
}

首先通過單例函數initRetrofitClient(),保證了後面無論執行多少次都只產生一次 retrofit 和 service, 這正是requestHttp()在後期所做的。

下面我們再看get函數,其參數分別爲getFuncName, String[] paramArray, BaseCallback baseCallback。通過 invokeMethod(requestHttp(), getFuncName, paramArray);反射的方式間接執行了原本定義在EHService中的接口函數,從而實現了發送請求。主要藉助了invokeMethod執行了變參函數。

首先通過反射在EHService Class中找到參數中傳過來的對應的Method,然後賦給其paramArray參數作爲接口中對應的參數執行即可。

get請求相對比較容易理解,下面主要看看post相對來說比較複雜一點的是,其發送請求時需要在post請求有一個json的body帶參數過去,而且這些參數的格式都是不確定的。這裏我使用了泛型,開發者自行定義json對應的bean entity即可在內部自動解析爲json格式進行發送。同理這裏也是使用了反射,通過postFuncName找到EHService Class中找到參數中傳過來的對應的Method,然後執行即可。

這裏有一個BaseCallback callback,那麼BaseCallback裏又做了什麼呢?我們來看:

public abstract class BaseCallback<T extends BaseModel> implements Callback<T> {
    @Override
    public void onResponse(Call<T> call, Response<T> response) {
        int networkRespCode = response.raw().code();
        Log.i("TAG", "BaseCallback onResponse! networkRespCode = " + networkRespCode);
        T bean = response.body();

        if (networkRespCode == 200) { // 200是服務器有合理響應, IP層的HTTP迴應
            if(bean.getCode() == 200) on200Resp(bean); // Response 裏 json 層 code: 200
            else onNon200Resp(bean);
        }else
            onFailure(call, new RuntimeException("response error,detail = " + response.raw().toString()));
    }

    @Override
    public void onFailure(Call<T> call, Throwable t) {
        Log.e("TAG", "BaseCallback onFailure! call = " + call.request().url().toString());
        onFailure(t);
    }

    /**
     * on200Resp() onNon200Resp() onFailure() 無論如何都將是在UI線程中執行!即使是把 retrofitClient 放入子線程中仍是如此!
     * 當需要處理 onNon200Resp,onFailure 的場景時,則 override 此方法
     *
     */
    protected abstract void on200Resp(T bean);
    void onNon200Resp(T bean){
        Log.e("TAG", "receive non-200 response, code is " + bean.getCode());
    }
    void onFailure(Throwable t){
        Log.e("TAG", "on Failure! Throwable message = " + t.getMessage() + ", stack trace = " + t.getStackTrace());
    }
}

這裏實際上是對http response在應用層(即返回的json)中做了進一步的封裝處理,把onResponse json中的 return code 分成了 200 OK 和 non-200 response兩種情況,只把200 response作爲abstract留給開發者來override,當然如果想對non-200做處理也可以在callback中override。

好的,基本上基本原理就是如此簡單。如果有什麼問題,可以留言。另外,你可以去github中看看具體的代碼和實例
https://github.com/AndrewXiao/SimpleRetrofit

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