1. 程式人生 > >Okhttp使用和原始碼分析二(OkHttp3.x用法)

Okhttp使用和原始碼分析二(OkHttp3.x用法)

上一篇介紹了OkHttp2.x的用法,這一篇文章我們來對照OkHttp2.x版本來看看,OkHttp3使用起來有那些變化?

1.使用前準備,Android Studio 配置gradle:

compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okio:okio:1.7.0'

新增網路許可權:

<uses-permission android:name="android.permission.INTERNET"/>

2.非同步GET請求

private void getAsynHttp() {
    mOkHttpClient=new
OkHttpClient(); Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com"); //可以省略,預設是GET請求 requestBuilder.method("GET",null); Request request = requestBuilder.build(); Call mcall= mOkHttpClient.newCall(request); mcall.enqueue(new Callback() { @Override
public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if (null != response.cacheResponse()) { String str = response.cacheResponse().toString(); Log.i("yzw"
, "cache---" + str); } else { response.body().string(); String str = response.networkResponse().toString(); Log.i("yzw", "network---" + str); } runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "請求成功", Toast.LENGTH_SHORT).show(); } }); } }); }

與2.x版本並沒有什麼不同,比較鬱悶的是回撥仍然不在UI執行緒。

2.非同步POST請求
OkHttp3非同步POST請求和OkHttp2.x有一些差別就是沒有FormEncodingBuilder這個類,替代它的是功能更加強大的FormBody:

private void postAsynHttp() {
     mOkHttpClient=new OkHttpClient();
     RequestBody formBody = new FormBody.Builder()
             .add("size", "10")
             .build();
     Request request = new Request.Builder()
             .url("http://api.1-blog.com/biz/bizserver/article/list.do")
             .post(formBody)
             .build();
     Call call = mOkHttpClient.newCall(request);
     call.enqueue(new Callback() {
         @Override
         public void onFailure(Call call, IOException e) {
         }
         @Override
         public void onResponse(Call call, Response response) throws IOException {
             String str = response.body().string();
             Log.i("yzw", str);
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     Toast.makeText(getApplicationContext(), "請求成功", Toast.LENGTH_SHORT).show();
                 }
             });
         }
     });
 }

3.非同步上傳檔案

上傳檔案本身也是一個POST請求,上一篇沒有講,這裡我們補上。首先定義上傳檔案型別:

public static final MediaType MEDIA_TYPE_MARKDOWN
        = MediaType.parse("text/x-markdown; charset=utf-8");

將sdcard根目錄的wangshu.txt檔案上傳到伺服器上:

private void postAsynFile() {
    mOkHttpClient=new OkHttpClient();
    File file = new File("/sdcard/wangshu.txt");
    Request request = new Request.Builder()
            .url("https://api.github.com/markdown/raw")
            .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
            .build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i("yzw",response.body().string());
            }
        });
    }

當然如果想要改為同步的上傳檔案只要呼叫 mOkHttpClient.newCall(request).execute()就可以了。
當然不要忘了新增如下許可權:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

4.非同步下載檔案

下載檔案同樣在上一篇沒有講到,實現起來比較簡單,在這裡下載一張圖片,我們得到Response後將流寫進我們指定的圖片檔案中就可以了。

 private void downAsynFile() {
     mOkHttpClient = new OkHttpClient();
     String url = "http://img.my.csdn.net/uploads/201603/26/1458988468_5804.jpg";
     Request request = new Request.Builder().url(url).build();
     mOkHttpClient.newCall(request).enqueue(new Callback() {
         @Override
         public void onFailure(Call call, IOException e) {
         }
         @Override
         public void onResponse(Call call, Response response) {
             InputStream inputStream = response.body().byteStream();
             FileOutputStream fileOutputStream = null;
             try {
                 fileOutputStream = new FileOutputStream(new File("/sdcard/wangshu.jpg"));
                 byte[] buffer = new byte[2048];
                 int len = 0;
                 while ((len = inputStream.read(buffer)) != -1) {
                     fileOutputStream.write(buffer, 0, len);
                 }
                 fileOutputStream.flush();
             } catch (IOException e) {
                 Log.i("wangshu", "IOException");
                 e.printStackTrace();
            }
            Log.d("wangshu", "檔案下載成功");
        }
    });
}

5.非同步上傳Multipart檔案

這種場景很常用,我們有時會上傳檔案同時還需要傳其他型別的欄位,OkHttp3實現起來很簡單,需要注意的是沒有伺服器接收我這個Multipart檔案,所以這裡只是舉個例子,具體的應用還要結合實際工作中對應的伺服器。
首先定義上傳檔案型別:

private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private void sendMultipart(){
    mOkHttpClient = new OkHttpClient();
    RequestBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("title", "yzw")
            .addFormDataPart("image", "yzw.png",
                    RequestBody.create(MEDIA_TYPE_PNG, new File("/sdcard/yzw.jpg")))
            .build();
    Request request = new Request.Builder()
            .header("Authorization", "Client-ID " + "...")
            .url("https://api.imgur.com/3/image")
            .post(requestBody)
            .build();
   mOkHttpClient.newCall(request).enqueue(new Callback() {
       @Override
       public void onFailure(Call call, IOException e) {
       }
       @Override
       public void onResponse(Call call, Response response) throws IOException {
           Log.i("yzw", response.body().string());
       }
   });
}

6.設定超時時間和快取

和OkHttp2.x有區別的是不能通過OkHttpClient直接設定超時時間和快取了,而是通過OkHttpClient.Builder來設定,通過builder配置好OkHttpClient後用builder.build()來返回OkHttpClient,所以我們通常不會呼叫new OkHttpClient()來得到OkHttpClient,而是通過builder.build():

File sdcache = 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));
OkHttpClient mOkHttpClient=builder.build();

7.關於取消請求和封裝

取消請求仍舊可以呼叫call.cancel(),只是這一版中增加了一個cancelAll()的方法:

  /**
     * 取消某單個請求任務
     */
    public void cancelRequest(CharSequence url) {
        try {
            //任務排程中正在排隊的任務
            for (Call call : okHttpClient.dispatcher().queuedCalls()) {
                if (call.request().tag().equals(url))
                    call.cancel();
            }
            //任務排程中正在執行的任務
            for (Call call : okHttpClient.dispatcher().runningCalls()) {
                if (call.request().tag().equals(url))
                    call.cancel();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 取消全部請求任務
     */
    public void cancelAllRequest() {
        try {
            okHttpClient.dispatcher().cancelAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

8.封裝

public class OkHttp3Engine {

    private static OkHttp3Engine mInstance;

    private OkHttpClient okHttpClient = null;
    private int DEFAULT_HTTP_TIMEOUT = 15_000;
    private int SIZE_OF_CACHE = 5 * 1024 * 1024;
    private CharSequence StringEncode = "utf-8";
    private static final MediaType Json = MediaType.parse("application/json; charset=utf-8");
    private static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");

    private CharSequence POST = "post";
    private CharSequence GET = "get";

    private Handler handler = null;

    private boolean hasCache = true;

    public OkHttp3Engine() {
        super();
        handler = new Handler(Looper.getMainLooper());
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
        builder.readTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
        builder.writeTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
        okHttpClient = builder.build();
    }

    public static OkHttp3Engine getInstance() {
        if (mInstance == null) {
            synchronized (OkHttp3Engine.class) {
                if (mInstance == null) {
                    mInstance = new OkHttp3Engine();
                }
            }
        }
        return mInstance;
    }

    public <T> void fetchStringPost(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
        fetchString(url.toString(), resultBean, map, POST, false, null, fetchDatainterface);
    }

    public <T> void fetchStringGet(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
        fetchString(url.toString(), resultBean, map, GET, false, null, fetchDatainterface);
    }

    public <T> void fetchStringAsyncPost(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
        AsyncfetchString(url.toString(), resultBean, map, POST, false, null, fetchDatainterface);
    }

    public <T> void fetchStringAsyncGet(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
        AsyncfetchString(url.toString(), resultBean, map, GET, false, null, fetchDatainterface);
    }

    public <T> void fetchStringPostJson(CharSequence url, T resultBean, CharSequence json, FetchDataInterface fetchDatainterface) {
        fetchString(url.toString(), resultBean, null, POST, true, json, fetchDatainterface);
    }

    public <T> void fetchStringAsyncPostJson(CharSequence url, T resultBean, CharSequence json, FetchDataInterface fetchDatainterface) {
        AsyncfetchString(url.toString(), resultBean, null, POST, true, json, fetchDatainterface);
    }

    public void setOkHttpCache(boolean hasCache, Context mContext) {
        this.hasCache = hasCache;
        File cacheDirectory = new File(mContext.getExternalCacheDir(), "HttpCache");
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
        builder.readTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
        builder.writeTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
        if (okHttpClient != null) {
            okHttpClient.dispatcher().cancelAll();
        }
        if (hasCache) {
            Cache cache = new Cache(cacheDirectory, SIZE_OF_CACHE);
            okHttpClient = builder.cache(cache).build();
        } else {
            okHttpClient = builder.build();
        }
    }

    public boolean isHasCache() {
        return hasCache;
    }

    /**
     * 取消某單個請求任務
     */
    public void cancelRequest(CharSequence url) {
        try {
            //任務排程中正在排隊的任務
            for (Call call : okHttpClient.dispatcher().queuedCalls()) {
                if (call.request().tag().equals(url))
                    call.cancel();
            }
            //任務排程中正在執行的任務
            for (Call call : okHttpClient.dispatcher().runningCalls()) {
                if (call.request().tag().equals(url))
                    call.cancel();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 取消全部請求任務
     */
    public void cancelAllRequest() {
        try {
            okHttpClient.dispatcher().cancelAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 回撥介面
     */
    public interface FetchDataInterface {
        <T> void successful(T result);

        void failed(CharSequence message);
    }

    /**
     * 同步請求
     */
    private <T> void fetchString(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, CharSequence method, boolean isSendJson, CharSequence json, FetchDataInterface fetchDatainterface) {
        Request request = null;
        try {
            if (method.equals(POST)) {
                RequestBody body = null;
                if (isSendJson) {
                    body = RequestBody.create(Json, json.toString());
                } else {
                    //pMap.put("param", JSONObject.toJSONString(map));
                    body = mapToRequestBody(map);
                }
                if (isHasCache())
                    request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).post(body).build();
                else {
                    request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).post(body).build();
                }
            } else {
                //CharSequence param = setMapToParam(map).toString();
                if (isHasCache())
                    request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).get().build();
                else {
                    request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).get().build();
                }
            }

            Response response = okHttpClient.newCall(request).execute();
            if (null == response || !response.isSuccessful()) {
                sendCallBack("respponse isnull", false, fetchDatainterface);
                return;
            }
            byte[] bytes = response.body().bytes();
            if (null == bytes) {
                sendCallBack("responsebody isnull", false, fetchDatainterface);
                return;
            }
            String result = new String(bytes, StringEncode.toString());
            if (TextUtils.isEmpty(result)) {
                sendCallBack("strcode isnull", false, fetchDatainterface);
                return;
            }

            /**
             * 判斷返回的是否是json格式的資料
             */
            JSONObject jsonString = JSON.parseObject(result);

            T t = (T) JSON.parseObject(jsonString.toString(), resultBean.getClass());
            sendCallBack(t, true, fetchDatainterface);
        } catch (Exception e) {
            e.printStackTrace();
            sendCallBack(e.getMessage(), false, fetchDatainterface);
        }
    }

    /**
     * 非同步請求
     */
    private <T> void AsyncfetchString(final CharSequence url, final T resultBean, Map<CharSequence, CharSequence> map, CharSequence method, boolean isSendJson, final CharSequence json, final FetchDataInterface fetchDatainterface) {
        Request request = null;
        try {
            if (method.equals(POST)) {
                RequestBody body = null;
                if (isSendJson) {
                    body = RequestBody.create(Json, json.toString());
                } else {
                    //pMap.put("param", JSONObject.toJSONString(map));
                    body = mapToRequestBody(map);
                }
                if (isHasCache())
                    request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).post(body).build();
                else
                    request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).post(body).build();
            } else {
                //CharSequence param = setMapToParam(map).toString();
                if (isHasCache())
                    request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).get().build();
                else
                    request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).get().build();
            }
            okHttpClient.newCall(request).enqueue(new Callback() {

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (null == response || !response.isSuccessful()) {
                        sendCallBack("response isnull", false, fetchDatainterface);
                        return;
                    }
                    byte[] bytes = response.body().bytes();
                    if (null == bytes) {
                        sendCallBack("responsebody isnull", false, fetchDatainterface);
                        return;
                    }
                    String result = new String(bytes, StringEncode.toString());
                    if (TextUtils.isEmpty(result)) {
                        sendCallBack("strcode isnull", false, fetchDatainterface);
                        return;
                    }
                    /**
                     * 判斷返回的是否是json格式的資料
                     */
                    try {
                        JSONObject jsonString = JSON.parseObject(result);

                        T t = (T) JSON.parseObject(jsonString.toString(), resultBean.getClass());
                        sendCallBack(t, true, fetchDatainterface);
                    } catch (Exception e) {
                        e.printStackTrace();
                        sendCallBack(e.getMessage(), false, fetchDatainterface);
                    }
                }

                @Override
                public void onFailure(Call call, IOException e) {
                    sendCallBack(e.getMessage(), false, fetchDatainterface);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            sendCallBack(e.getMessage(), false, fetchDatainterface);
        }
    }

    private RequestBody mapToRequestBody(Map<CharSequence, CharSequence> kv) throws IOException {
        FormBody.Builder formBody = new FormBody.Builder();
        for (CharSequence key : kv.keySet()) {
            formBody.add(key.toString(), kv.get(key).toString());
        }
        return formBody.build();
    }

    private CharSequence setMapToParam(Map<CharSequence, CharSequence> hashMap) {
        StringBuilder param = new StringBuilder();
        if (hashMap == null || hashMap.size() == 0) {
            return "";
        }
        for (Map.Entry<CharSequence, CharSequence> entry : hashMap.entrySet()) {
            // 如果請求引數中有中文,需要進行URLEncoder編碼
            if (entry.getValue() != null) {
                try {
                    param.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), StringEncode.toString()));
                    param.append("&");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        if (param.length() > 0) {
            param.deleteCharAt(param.length() - 1);
        }
        return param.toString();
    }

    private CharSequence builderGetUri(CharSequence uri, Map<CharSequence, CharSequence> map) {
        StringBuilder sb = new StringBuilder();
        try {
            while (uri.toString().endsWith("/")) {
                try {
                    uri = uri.toString().substring(0, uri.length() - 1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            sb.append(uri).append("?");
            if (map != null && map.size() != 0) {
                for (Map.Entry<CharSequence, CharSequence> entry : map.entrySet()) {
                    // 如果請求引數中有中文,需要進行URLEncoder編碼
                    sb.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), StringEncode.toString()));
                    sb.append("&");
                }
                sb.deleteCharAt(sb.length() - 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.d("builderGetUri", sb.toString());
        return sb.toString();
    }

    /**
     * 檔案上傳
     *
     * @param url                上傳的伺服器地址
     * @param file               上傳的本地檔案
     * @param params             上傳檔案時帶的引數
     * @param fetchDatainterface 上傳完成後的回撥介面
     */
    public void uploadFile(final CharSequence url, File file, Map<String, String> params, final FetchDataInterface fetchDatainterface) {
        Request request = new Request.Builder().headers(Headers.of(params)).url(url.toString()).post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendCallBack(e.getMessage(), false, fetchDatainterface);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                sendCallBack(response.body().string(), true, fetchDatainterface);
            }
        });

    }

    /**
     * 檔案下載
     *
     * @param url                下載的伺服器地址
     * @param localSavePath      下載後儲存的本地地址
     * @param fetchDatainterface 下載完成後的回撥介面
     */
    public void downloadFile(final String url, final String localSavePath, final FetchDataInterface fetchDatainterface) throws Exception {
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendCallBack(e.getMessage(), false, fetchDatainterface);
            }

            @Override
            public void onResponse(Call call, Response response) {
                try {
                    InputStream inputStream = response.body().byteStream();
                    FileOutputStream fileOutputStream = new FileOutputStream(new File(localSavePath));
                    byte[] buffer = new byte[2048];
                    int len = 0;
                    while ((len = inputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, len);
                    }
                    inputStream.close();
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    sendCallBack(localSavePath, true, fetchDatainterface);
                } catch (IOException e) {
                    Log.i("downloadFile", "IOException");
                    e.printStackTrace();
                }
                Log.d("downloadFile", "檔案下載成功");
            }
        });
    }

    /**
     * 圖片下載並展示
     *
     * @param image 展示的控制元件
     * @param url   下載的地址
     */
    public void downloadImgShowImageView(final ImageView image, final String url) throws Exception {
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                image.setImageResource(0);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream is = response.body().byteStream();
                ImageUtils.ImageSize actualImageSize = ImageUtils.getImageSize(is);
                ImageUtils.ImageSize imageViewSize = ImageUtils.getImageViewSize(image);
                int inSampleSize = ImageUtils.calculateInSampleSize(actualImageSize, imageViewSize);
                try {
                    is.reset();
                } catch (Exception e) {
                    Request request = new Request.Builder().url(url).build();
                    response = okHttpClient.newCall(request).execute();
                    is = response.body().byteStream();
                }
                BitmapFactory.Options ops = new BitmapFactory.Options();
                ops.inJustDecodeBounds = false;
                ops.inSampleSize = inSampleSize;
                final Bitmap bm = BitmapFactory.decodeStream(is, null, ops);
                image.post(new Runnable() {
                    @Override
                    public void run() {
                        image.setImageBitmap(bm);
                    }
                });
                is.close();
            }
        });
    }

    private <T> void sendCallBack(final T t, final boolean isSccessful, final FetchDataInterface fetchDatainterface) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (isSccessful)
                    fetchDatainterface.successful(t);
                else
                    fetchDatainterface.failed(t.toString());
            }
        });
    }

}

我們以一個get請求為例使用:

OkHttp3Engine.getInstance().fetchStringAsyncGet("http://www.baidu.com", new String(), null, new OkHttp3Engine.FetchDataInterface() {
            @Override
            public <T> void successful(T result) {
                Log.d("yzw", result.toString());
            }

            @Override
            public void failed(CharSequence message) {

            }
        });