編輯:關於Android編程
上一次帶大家分析了NoHttp源碼,知道我們可以替換NoHttp的底層為其他任何庫,例如OkHttp、HttpURLConnection、HttpClient,那今天就帶領大家一步步來實現替換NoHttp的底層為OkHttp。
先要告訴大家的是,NoHttp和OkHttp的完美結合庫我已經封裝好了,並且做了開源,起名叫NoHttp4OkHtp。
NoHttp4OkHtp開源地址:https://github.com/yanzhenjie/NoHttp4OkHttp。
使用Jar,下載項目後導入jar文件夾中的所有jar包。不要忘記了要去NoHttp主頁下載NoHttp的jar,版本為1.0.6及以上。com.yanzhenjie.nohttp nohttp-okhttp1.0.0 pom
基本用法NoHttp的所有用法不變,需要注意的兩個地方:
1. 創建隊列NoHttp的用法是NoHttp.newRequestQueue…,使用本庫改為NoOkHttp.newRequestQueue…
2. 原來的同步請求是NoHttp.startSyncRequest…,使用本庫改為NoOkHttp.startSyncRequest…
如果還有不明白的可以看demo或者加QQ技術群來討論:547839514。
其他創建請求等用法均不變,更多用法請參考NoHttp:https://github.com/yanzhenjie/NoHttp。
其實NoHttp源碼分析那篇文章也說了IRestConnection接口,但是由於篇幅關系,但是只說了原理,沒有具體分析實現類的源碼,為了不讓大家思緒斷開,這裡我們還是復習一下。
上次說到IRestConnection被RestProtocol調用,負責具體的網絡請求、發送數據、拿到服務器的Stream等,我們看IRestConnection是怎麼出現的,NoHttp在創建隊列時需要一個IRestConnection:
public static RequestQueue newRequestQueue(int threadPoolSize) { // 調用下一個方法:這裡傳入RestConnection.getInstance()生成IRestConnection單例。 return newRequestQueue(DiskCacheStore.INSTANCE, RestConnection.getInstance(), threadPoolSize); } // 這裡需要一個IRestConnection參數。 public static RequestQueue newRequestQueue(Cachecache, IRestConnection connection, int threadPoolSize) { return newRequestQueue(RestProtocol.getInstance(cache, connection), threadPoolSize); } public static RequestQueue newRequestQueue(IRestProtocol iRestProtocol, int threadPoolSize) { return newRequestQueue(RestParser.getInstance(iRestProtocol), threadPoolSize); }
這裡看到IRestConnection最終被傳入RestProtocol中生成單例,而在RestProtocol中看到:
Connection connection = iRestConnection.getConnection(request);
我們看到在RestProtocol中確實是調用了IRestConnection的getConnection()方法去拿到Connection。這裡很明了,我們先看IRestConnection的源碼:
public interface IRestConnection { /** * 拿到網絡連接的各種屬性、頭、流。 */ Connection getConnection(IBasicRequest iBasicRequest); }
看到這裡,有一個返回參數Connection,不要忘了今天為了替換NoHttp的底層為OkHttp,我們要想用OkHttp來實現這個方法,必須要看這個類是什麼樣的:
public interface Connection extends Closeable { /** * 拿到URL對象。 */ URL getURL(); /** * 拿到相應頭。 */ Headers responseHeaders(); /** * 拿到服務器的輸出流。 */ InputStream serverStream(); /** * 拿到請求過程中的異常。 */ Exception exception(); }
這個返回參數類是一個接口,也就說明了我們可以自定義這個類,NoHttp本身提供的類我們暫且不管,這裡其實我們按照下面四個方法返回值即可:一URL、一個拿服務器的響應頭、一個是服務器的輸出流,一個是請求過程中是否發生異常。這樣就非常好替換OkHttp了,我們底層用OkHttp請求網絡,然後同樣給IRestConnection返回這個對象,裡面把服務器的相應頭、輸出流、請求過程中的異常塞進去就OK了。
上面其實是讓大家從上篇博客的思路上繼續,那麼我們明白了原理,接下來就是具體實現了。
這裡很重要,要想做到OkHttp和NoHttp的無縫結合,勢必要對NoHttp和OkHttp有一定的了解,NoHttp的源碼和網絡層我們都了解了,大家更關心的是怎麼使用OkHttp了。
我們要知道NoHttp的底層使用的是URLConnection,OkHttp是一個非常優秀的底層網絡框架,它是為URLConnection提供了接口實現的,OkHttp為URLConnection提供實現的項目叫okhttp-urlconnection,裡面只有簡單的幾個類,實現了兩個基類:HttpURLConnection、HttpsURLConnection。
所以我們今天也會用到這個項目,首先先在gradle中引用okhttp-urlconnection和NoHttp:
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.1' compile 'com.yolanda.nohttp:nohttp:1.0.6'
我們知道URLConnection對http和https的處理的類分別是:HttpURLConnection、HttpsURLConnection,所以OkHttp也為這兩個類提供了實現:OkHttpURLConnection、OkHttpsURLConnection。因此之前用HttpURLConnection和HttpsURLConnection的地方全部替換為OkHttpURLConnection和OkHttpsURLConnection即可。
先來看看用HttpURLConnection的時候請求網絡的基本代碼:
HttpURLConnection connection; URL url = new URL(urlStr); Proxy proxy = request.getProxy(); if (proxy == null) { connection = (HttpURLConnection) url.openConnection(); } else { connection = (HttpURLConnection) url.openConnection(proxy); } connection.set...
有了上面OkHttpURLConnection的引用後換成OkHttp:
OkHttpClient okhttpClient; URL url = new URL(urlStr); Proxy proxy = request.getProxy(); if (proxy != null) { okhttpClient = okhttpClient.newBuilder().proxy(proxy).build(); } HttpURLConnection connection = new OkHttpURLConnection(url, okhttpClient); connection.set...
最重要的也就一句話的變化而已:
HttpURLConnection connection = new OkHttpURLConnection(url, okhttpClient);
因為上面用到了OkHttpClient,OkHttpClient推薦使用單例,因此基於上面的變化我給OkHttp來一個封裝:
public class URLConnectionFactory { private OkHttpClient mClient; private static URLConnectionFactory instance; // 單例封裝。 public static URLConnectionFactory instance() { if (instance == null) { synchronized (URLConnectionFactory.class) { if (instance == null) { instance = new URLConnectionFactory(); } } } return instance; } // 隱藏構造。 private URLConnectionFactory() { this.mClient = new OkHttpClient(); } // 拿到不用代理的連接。 public HttpURLConnection open(URL url) { return open(url, mClient.proxy()); } // 拿到需要代理的連接。 public HttpURLConnection open(URL url, Proxy proxy) { OkHttpClient copy = mClient.newBuilder().proxy(proxy).build(); // 處理http和Https。 String protocol = url.getProtocol(); if (protocol.equals("http")) return new OkHttpURLConnection(url, copy); if (protocol.equals("https")) return new OkHttpsURLConnection(url, copy); // 其他連接不接受。 throw new IllegalArgumentException("Unexpected protocol: " + protocol); } }
這個封裝過後使用起來就更簡單了:
URL url = new URL(urlStr); HttpURLConnection connection; Proxy proxy = request.getProxy(); if (proxy == null) connection = URLConnectionFactory.instance().open(url); else connection = URLConnectionFactory.instance().open(url, proxy); connection.set...
到這裡幾乎沒有什麼懸念了,替換NoHttp底層,只需要在RestConnection中找到調用HttpURLConnection替換為我們的封裝即可,請看下面接續。
這裡就要先看下NoHttp為IRestConnection提供的默認實現類是怎麼做的了。中選IRestConnection後Ctrl + T後發現IRestConnection的默認實現類是RestConnection,我已經替大家找好了RestConnection的getConnection()方法中一步步調用後最終走網絡的地方是:
private HttpURLConnection createHttpURLConnection(IBasicRequest request) throws Exception { // 1.Pre operation notice request.onPreExecute(); // 2.Build URL String urlStr = request.url(); URL url = new URL(urlStr); HttpURLConnection connection; Proxy proxy = request.getProxy(); if (proxy == null) connection = (HttpURLConnection) url.openConnection(); else connection = (HttpURLConnection) url.openConnection(proxy); ... }
果不其然啊,但是別急啊,這裡呢我們新建一個類OkHttpRestConnection,然後把NoHttp原來的RestConnection的內容拷貝過來,把最開始的構造方法和單例換成OkHttpRestConnection既是如下:
private static OkHttpRestConnection instance; public static IRestConnection getInstance() { synchronized (OkHttpRestConnection.class) { if (instance == null) instance = new OkHttpRestConnection(); return instance; } } private OkHttpRestConnection() { }
然後把剛才網絡請求的地方替換一下:
private HttpURLConnection createHttpURLConnection(IBasicRequest request) throws Exception { // 1.Pre operation notice request.onPreExecute(); // 2.Build URL String urlStr = request.url(); URL url = new URL(urlStr); HttpURLConnection connection; Proxy proxy = request.getProxy(); if (proxy == null) connection = URLConnectionFactory.instance().open(url); else connection = URLConnectionFactory.instance().open(url, proxy); ...
最後一個細節注意,因為Android系統的原因,在5.0以下下DELETE請求方法是不允許寫出body的,所以NoHttp做了一個判斷:
private boolean isAllowHasBody(RequestMethod requestMethod) { boolean allowRequestBody = requestMethod.allowRequestBody(); // Fix Android bug. if (Build.VERSION.SDK_INT < AndroidVersion.LOLLIPOP) return allowRequestBody && requestMethod != RequestMethod.DELETE; return allowRequestBody; }
因為OkHttp沒有這個限制,所以我們把這個方法去掉,全部換成Http協議默認的:requestMethod.allowRequestBody();;
上面封裝完了,就是替換創建隊列的傳入的IRestConnection了,我們只需要在創建隊列時按如下調用
newRequestQueue(DiskCacheStore.INSTANCE, OkHttpRestConnection.getInstance(), 3);
第一個參數是緩存接口,第二個就是我們的網絡層的接口實現,第三個隊列並發數,具體可以參考上一篇博客。
這個庫我已經封裝好開源到Github了:https://github.com/yanzhenjie/NoHttp4OkHttp。推薦大家使用我封裝好的,以後還會繼續維護的。
有疑問的朋友可以加我的QQ交流群:547839514,歡迎來一起討論一起進步。
本文將通過radiogroup和radiobutton實現組內信息的單選, 其中radiogroup就是將radiobutton進行分組,同一管理和控制 同時實現默認選
最近到4412最基本的都調了 然後覺得沒事做了 所以增加一個HDMI的設置 不能閒著 以下使用的是廣州斯道的icool210開發板 源碼修改如下 最開始我是先在設置裡加上
安卓手機中,多點觸摸是是最基本的操作,下面就使用程序進行演示多點觸摸操作一、獲取觸摸事件中的點擊,移動和抬起事件,創建新的安卓項目,如下面代碼所示,分別為MainActi
開發android的同學可能會抱怨Toast設定顯示的時長無效,只能是Toast.LENGTH_LONG 或者Toast.LENGTH_SHORT 之一,為了解決這些辦法