使用OkHttp後,遇到的那些“坑”

首先說明下:本文碰到的一些問題,都是在 okhttp-3.10.0,okio-1.14.0 這個版本碰到的。

1,OkHttpClient 默認的 connectTimeout,readTimeout,writeTimeout 都是10秒,實際在應用體驗上來說,有點短的。項目中最好 connectTimeout = 10s,readTimeout= 60s,readTimeout= 60s,如果涉及到下載上傳,實際應該修改成更長的。

2,Request,默認頭信息 User-Agent 是“okhttp3.0”,建議修改成系統的。

下面代碼是兼容的方式獲取系統默認UA的方法:

    /**
     * 獲取默認UserAgent(讀取系統WebView的UserAgent設置)
     */
    public String getDefaultUserAgent(Context context) {
        String result;
        // http://androidxref.com/4.1.1/xref/frameworks/base/core/java/android/webkit/WebSettingsClassic.java#getCurrentUserAgent
        try {
            Locale locale = context.getResources().getConfiguration().locale;
            StringBuffer buffer = new StringBuffer();
            // Add version
            final String version = Build.VERSION.RELEASE;
            if (version.length() > 0) {
                buffer.append(version);
            } else {
                // default to "1.0"
                buffer.append("1.0");
            }
            buffer.append("; ");
            final String language = locale.getLanguage();
            if (language != null) {
                buffer.append(language.toLowerCase());
                final String country = locale.getCountry();
                if (country != null) {
                    buffer.append("-");
                    buffer.append(country.toLowerCase());
                }
            } else {
                // default to "en"
                buffer.append("en");
            }
            // add the model for the release build
            if ("REL".equals(Build.VERSION.CODENAME)) {
                final String model = Build.MODEL;
                if (model.length() > 0) {
                    buffer.append("; ");
                    // ok3, header不支持中文, encode一下
                    buffer.append(URLEncoder.encode(model, "utf-8"));
                }
            }
            final String id = Build.ID;
            if (id.length() > 0) {
                buffer.append(" Build/");
                buffer.append(id);
            }
            final String base = context.getResources().getText(
                    Resources.getSystem().getIdentifier("web_user_agent", android.R.string.class.getSimpleName(), "android")).toString();
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                result = String.format(base, buffer);
            } else {
                String mobile = context.getResources().getText(
                        Resources.getSystem().getIdentifier("web_user_agent_target_content", android.R.string.class.getSimpleName(), "android")).toString();
                result = String.format(base, buffer, mobile);
            }
        } catch (Exception | Error e) {
            if (LogUtils.isDebug()) {
                LogUtils.d("OkhttpImp", "getDefaultUserAgent");
            }
            result = System.getProperty("http.agent");
        }
        if (LogUtils.isDebug()) {
            LogUtils.d("OkhttpImp", "getDefaultUserAgent.result = " + result);
        }
        return result;
    }

3,Request 添加 header 的時候,需要注意 okhttp3 中 header 是不支持中文的,所以注意 URLEncoder。

4,ok3 支持異步獲取數據: Call.enqueue(Callback cb),但是要注意 Callback 回調裏面的方法全部是在子線程的。 

5,http1.1 支持 TCP 通道複用機制,http2.0 還支持了多路複用機制,所以有時候明明有網絡,但是接口卻返回 SocketTimeoutException,UnknownHostException,一般都是後臺接口沒有嚴格按照http1.1協議和http2.0協議來,導致服務器Socket關了,但是沒有通知客戶端,客戶端下次請求,複用鏈路導致 SocketTimeoutException。三種思路解決:

第一種:服務器端修改。

第二種:客戶端關閉連接池 OkHttpClient.connectionPool().evictAll()。

第三種:客戶端加重試機制,失敗重新請求一次。推薦這種方式,失敗重試可以解決很多其他網絡偶然問題,比如快速切網的時候。



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