OkHttp的簡單封裝

       我們都知道,很多app的應用都離不開對網絡的請求,OkHttp是一個很好的移動網絡請求框架。使用起來非常方便,但是如果每使用一次網絡請求就要去new 一個client,不僅代碼冗長多餘,而且沒有必要浪費資源,所以我們今天要講的就是將OkHttp進行簡單的封裝,作爲一個工具類來調用,也體現了java三要素之一:封裝。

      在上一篇裏,已經說過如何添加依賴和網絡權限,這裏就不贅述。Android目前不允許在主線程裏對網絡進行請求,因爲會阻塞UI線程,用戶體驗非常不好,所以,所有的網絡請求都應該在子線程裏進行,這裏也就涉及到多線程,我們將以單例的形式來做網絡請求,讓整個應用只有一個OkhttpClient。節省資源開銷。

      我們寫的是一個工具類OkHttpUtil,所以誰使用這個工具類,就將獲取到的網絡數據回調給誰。所以,先寫一個回調接口,包含兩個方法java代碼

 //創建接口,回調給調用者
    interface ResultCallback{
        void onError(Request request,Exception e);
        void onResponse(Response response) throws IOException;
    }

       接着我們創建OkHttpUtil的實例,我們使用單例模式,網絡請求最好使用單例模式。將其構造方法私有化,然後提供一個靜態的方法返回OkHttpUtil對象。這裏提一下,單例模式最好寫兩個判定語句,這裏就不細說了,屬於java設計模式範疇。

private volatile static OkHttpUtil okHttpUtil;//會被多線程使用,所以使用關鍵字volatile
    private OkHttpClient client;
    private Handler mHandler;
    //私有化構造方法
    private OkHttpUtil(Context context){
        File sdcache = context.getExternalCacheDir();
        int cacheSize = 10 * 1024 *1024;//設置緩存大小
        OkHttpClient.Builder builder= new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20,TimeUnit.SECONDS)
                .readTimeout(20,TimeUnit.SECONDS)
                .cache(new Cache(sdcache.getAbsoluteFile(),cacheSize));//設置緩存的路徑
                 client = builder.build();
                mHandler = new Handler();
    }
    //單例模式,全局得到一個OkHttpUtil對象
    public static OkHttpUtil getInstance(Context context){
        if (okHttpUtil == null){
            synchronized (OkHttpUtil.class){
                if (okHttpUtil == null){
                    okHttpUtil = new OkHttpUtil(context);
                }
            }
        }
        return okHttpUtil;
    }

       在其私有的構造方法中,我們得到了一個OkHttpClient 和handler,他們分別是用來發起網絡請求和在線程中傳遞數據。handler是一個很神奇的東東,傳遞數據就靠它了。我們剛剛說網絡請求在子線程中進行的,而Android是不允許在子線程中更新UI的,那我們如何將數據更新到UI線程呢?其實就是handler幫我們完成的。

       我們再寫兩個私有的方法,來處理如何將得到的網絡數據傳遞出去,一個是當請求成功後的方法,一個是請求失敗後的方法

/**當請求失敗時,都會調用這個方法
     * @param call
     * @param e
     * @param callback
     */
    private void sendFailedCallback(final Call call, final IOException e, final ResultCallback callback){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("main","當前線程:"+Thread.currentThread().getName());
                if (callback != null){
                    callback.onError(call.request(),e);
                }
            }
        });
    }

    /**請求成功調用該方法
     * @param response  返回的數據
     * @param callback 回調的接口
     */
    private void sendSuccessCallback(final Response response, final ResultCallback callback){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("main","當前線程:"+Thread.currentThread().getName());
                if (callback != null){
                    try {
                        callback.onResponse(response);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

       這裏我們使用handler.post()來傳遞數據,這裏看起來好像開啓了一個新的線程,但是,我通過控制檯獲取當前線程的名稱,竟然發現這個handler工作在主線程,也就是說,我們可以直接在回調裏面更新UI了。

       前期工作都準備就緒,我們可以寫請求方法了,一個是get請求,一個是post請求(提交表單)

/**get異步請求
     * @param url
     * @param callback
     */
    public void getAsynHttp(String url, final ResultCallback callback){
         Request request = new Request.Builder()
                .url(url)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendFailedCallback(call,e,callback);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                sendSuccessCallback(response,callback);
            }
        });
    }

    /**提交表單數據
     * @param url
     * @param map
     * @param callback
     */
    public void postForm(String url, Map<String,String > map, final ResultCallback callback){
        FormBody.Builder form = new FormBody.Builder();//表單對象,包含以input開始的對象,以html表單爲主
        if (map != null && !map.isEmpty()){
            //遍歷Map集合
            for(Map.Entry<String ,String> entry : map.entrySet()){
                form.add(entry.getKey(),entry.getValue());
            }
            RequestBody body = form.build();
            Request request = new Request.Builder().url(url).post(body).build();//採用post提交數據
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                        sendFailedCallback(call,e,callback);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (response.isSuccessful()&&response != null){
                        sendSuccessCallback(response,callback);
                    }
                }
            });
        }

    }

       其實,封裝使用的知識點不是很多,弄清楚單例模式,接口回調,也就差不多了,當然,我們還可以添加很多方法,讓這個工具類更充實,大家可以根據自己的實際情況添加。在最後我會貼上工具類的源碼,以及具體使用例子

       OkHttpUtil源碼

public class OkHttpUtil {
    private volatile static OkHttpUtil okHttpUtil;//會被多線程使用,所以使用關鍵字volatile
    private OkHttpClient client;
    private Handler mHandler;
    //私有化構造方法
    private OkHttpUtil(Context context){
        File sdcache = context.getExternalCacheDir();
        int cacheSize = 10 * 1024 *1024;//設置緩存大小
        OkHttpClient.Builder builder= new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20,TimeUnit.SECONDS)
                .readTimeout(20,TimeUnit.SECONDS)
                .cache(new Cache(sdcache.getAbsoluteFile(),cacheSize));//設置緩存的路徑
                 client = builder.build();
                mHandler = new Handler();
    }
    //單例模式,全局得到一個OkHttpUtil對象
    public static OkHttpUtil getInstance(Context context){
        if (okHttpUtil == null){
            synchronized (OkHttpUtil.class){
                if (okHttpUtil == null){
                    okHttpUtil = new OkHttpUtil(context);
                }
            }
        }
        return okHttpUtil;
    }

    /**get異步請求
     * @param url
     * @param callback
     */
    public void getAsynHttp(String url, final ResultCallback callback){
         Request request = new Request.Builder()
                .url(url)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendFailedCallback(call,e,callback);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                sendSuccessCallback(response,callback);
            }
        });
    }

    /**提交表單數據
     * @param url
     * @param map
     * @param callback
     */
    public void postForm(String url, Map<String,String > map, final ResultCallback callback){
        FormBody.Builder form = new FormBody.Builder();//表單對象,包含以input開始的對象,以html表單爲主
        if (map != null && !map.isEmpty()){
            //遍歷Map集合
            for(Map.Entry<String ,String> entry : map.entrySet()){
                form.add(entry.getKey(),entry.getValue());
            }
            RequestBody body = form.build();
            Request request = new Request.Builder().url(url).post(body).build();//採用post提交數據
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                        sendFailedCallback(call,e,callback);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (response.isSuccessful()&&response != null){
                        sendSuccessCallback(response,callback);
                    }
                }
            });
        }

    }

    /**當請求失敗時,都會調用這個方法
     * @param call
     * @param e
     * @param callback
     */
    private void sendFailedCallback(final Call call, final IOException e, final ResultCallback callback){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("main","當前線程:"+Thread.currentThread().getName());
                if (callback != null){
                    callback.onError(call.request(),e);
                }
            }
        });
    }

    /**請求成功調用該方法
     * @param response  返回的數據
     * @param callback 回調的接口
     */
    private void sendSuccessCallback(final Response response, final ResultCallback callback){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("main","當前線程:"+Thread.currentThread().getName());
                if (callback != null){
                    try {
                        callback.onResponse(response);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    //創建接口,回調給調用者
    interface ResultCallback{
        void onError(Request request,Exception e);
        void onResponse(Response response) throws IOException;
    }

}

       在MainActivity中,我們添加一個按鈕,這些代碼就不一一講解了,然後再按鈕裏發送請求

public class MainActivity extends AppCompatActivity {
    private utl_btn;
    private OkHttpUtil httpUtil;
    private String path = "xxxxxxxxxxx";
    private String path_url = "xxxxxxxxxxx";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        utl_btn = (Button) findViewById(R.id.btn3);
        httpUtil = OkHttpUtil.getInstance(MainActivity.this);
        utl_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                HashMap<String,String> map = new HashMap();
                map.put("xxx","yyy");
                map.put("zzz","aaa");
                httpUtil.postForm(path_url, map, new OkHttpUtil.ResultCallback() {
                    @Override
                    public void onError(Request request, Exception e) {

                    }

                    @Override
                    public void onResponse(Response response) throws IOException {
                        Log.i("main","response:"+response.body().string());
                    }
                });
            }

        });
    }

}

       好了,其實如果網絡請求不是很複雜,我們完全可以自己封裝一個,沒有必要使用開源庫,當然,GitHub上有很多優秀的開源庫,值得我們學習和借鑑,如果比較懶的話,可是直接添加依賴,這裏推薦一個OkHttpFinal

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