引言
現在的Android開發很多都使用Volley、OkHttp、Retrofit等框架,這些框架固然有優秀的地方(以后會寫代碼學習分享),但是我們今天介紹一種基於Java線程池的網絡訪問框架。
實現思路及實現
APP界面上面的數據都是通過網絡請求獲取的,我們能不能將網絡請求依次入隊,然后配合着Java線程池,讓線程依次處理我們的請求,最后返回結果給我們。下面我們先來看一下線程池工具類的實現:
1 public class ThreadPoolUtils { 2 3 private ThreadPoolUtils() {} 4 //核心線程數 5 private static int CORE_POOL_SIZE = 8; 6 //最大線程數 7 private static int MAX_POOL_SIZE = 64; 8 //線程池中超過corePoolSize數目的空閑線程最大存活時間;可以allowCoreThreadTimeOut(true)使得核心線程有效時間 9 private static int KEEP_ALIVE_TIME = 5; 10 //任務隊列 11 private static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(64); 12 13 private static ThreadPoolExecutor threadpool; 14 15 static { 16 threadpool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, workQueue); 17 } 18 19 public static void execute(Runnable runnable) { 20 threadpool.execute(runnable); 21 } 22 }
我們來看一下ThreadPoolExecutor的構造函數及相關參數:
參數名 | 作用 |
corePoolSize | 核心線程池大小 |
maximumPoolSize | 最大線程池大小 |
keepAliveTime | 線程池中超過corePoolSize數目的空閑線程最大存活時間;可以allowCoreThreadTimeOut(true)使得核心線程有效時間 |
TimeUnit | keepAliveTime時間單位 |
workQueue | 阻塞任務隊列 |
threadFactory | 新建線程工廠 |
RejectedExecutionHandler | 當提交任務數超過maxmumPoolSize+workQueue之和時,任務會交給RejectedExecutionHandler來處理 |
重點講解:
其中比較容易讓人誤解的是:corePoolSize,maximumPoolSize,workQueue之間關系。
1.當線程池小於corePoolSize時,新提交任務將創建一個新線程執行任務,即使此時線程池中存在空閑線程。
2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會創建新線程執行任務
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
5.當線程池中超過corePoolSize線程,空閑時間達到keepAliveTime時,關閉空閑線程
6.當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize線程空閑時間達到keepAliveTime也將關閉
網絡訪問的封裝
通過上面的分析,我們知道ThreadPoolExecutor里面可以執行Runable對象,那么我們將網絡訪問邏輯封裝成Runable對象,然后扔進線程池進行執行。我們來看一下封裝的邏輯:
1 /** 2 * post線程 3 */ 4 public class HttpPostThread implements Runnable { 5 6 private Handler hand; 7 private String strURL; 8 private String method; 9 private List<String> params; 10 private Handler netHand; 11 12 public HttpPostThread(Handler hand, String strURL, String method, List<String> params) { 13 this.hand = hand; 14 //實際的傳值 15 this.strURL = strURL; 16 this.method = method; 17 this.params = params; 18 } 19 20 public HttpPostThread(Handler hand, Handler netHand, String strURL, String method, List<String> params) { 21 this.hand = hand; 22 //實際的傳值 23 this.strURL = strURL; 24 this.method = method; 25 this.params = params; 26 this.netHand = netHand; 27 } 28 29 @Override 30 public void run() { 31 Message msg = hand.obtainMessage(); 32 try { 33 String result; 34 if(!strURL.startsWith("https")) { 35 RpcHttp rpcHttp = new RpcHttp(); 36 result = rpcHttp.post(strURL, method, params); 37 } 38 else { 39 RpcHttps rpcHttps = new RpcHttps(); 40 result = rpcHttps.post(strURL, method, params); 41 } 42 /** 43 * 根據訪問http來設置標識位 44 * 然后發送msg到handlerMessage進行處理(此處配合Handler進行使用) 45 */ 46 if (result.equals("noNet")) { 47 if (netHand != null) { 48 netHand.sendEmptyMessage(600); 49 } 50 } else { 51 msg.what = 200; 52 msg.obj = result; 53 } 54 } catch(Exception e){ 55 e.printStackTrace(); 56 } 57 finally { 58 hand.sendMessage(msg); 59 } 60 } 61 }
我們看到,我們封裝的這個類的構造函數只需要使用者提供回調的Handler、Http訪問的Url、訪問的方法及參數。這樣就可以將其放入線程中進行處理,然后我們只需要在客戶端使用寫好回調的Handler即可。我們看34-40行,這時候我們看到會使用封裝的Http類去進行網絡訪問,我們來看一下:
1 /** 2 * post請求 3 * 4 * @param strURL 請求的地址 5 * @param method 請求方法 6 * @param params 請求元素 7 * @return 8 */ 9 public String post(String strURL, String method, List<String> params) { 10 Log.e("開始請求","獲取請求"); 11 String RequestParams = ""; 12 long timestamp = System.currentTimeMillis(); 13 RequestParams += "{\"method\":\"" + method + "\""; 14 if (params != null && params.size() > 0) { 15 RequestParams += ",\"params\":{"; 16 for (String item : params) { 17 String first = item.substring(0, item.indexOf(":")); 18 String second = item.substring(item.indexOf(":") + 1); 19 RequestParams += "\"" + first + "\":\"" + second + "\","; 20 } 21 22 RequestParams = RequestParams.substring(0, (RequestParams.length() - 1)); 23 RequestParams += "}"; 24 } else { 25 RequestParams += ",\"params\":{}"; 26 } 27 RequestParams += ",\"id\":\"" + timestamp + "\""; 28 RequestParams += "}"; 29 return this.post(strURL, RequestParams); 30 } 31 32 private String post(String strURL, String params) { 33 try { 34 URL url = new URL(strURL);// 創建連接 35 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 36 37 connection.setDoOutput(true); 38 connection.setDoInput(true); 39 connection.setUseCaches(false); 40 connection.setInstanceFollowRedirects(true); 41 connection.setRequestMethod("POST"); // 設置請求方式 42 connection.setRequestProperty("Accept", "application/json"); // 設置接收數據的格式 43 connection.setRequestProperty("Content-Type", "application/json"); // 設置發送數據的格式 44 connection.setConnectTimeout(10000);//設置超時 45 connection.setReadTimeout(10000);//設置超時 46 Log.e("開始連接","開始連接"); 47 connection.connect(); 48 49 OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); // utf-8編碼 50 out.append(params); 51 out.flush(); 52 out.close(); 53 54 String result = convertStreamToString(connection.getInputStream()); 55 Log.e("responseContent",result); 56 return result; 57 } catch (Exception e) { 58 Log.e("responseException",String.valueOf(e.getStackTrace())); 59 Log.e("responseException",String.valueOf(e.getLocalizedMessage())); 60 Log.e("responseException",String.valueOf(e.getMessage())); 61 e.printStackTrace(); 62 } 63 return "noNet"; // 自定義錯誤信息 64 }
我們看到,我們將Http訪問進行了簡單的封裝。在客戶端使用的時候我們就只需要簡單的幾行代碼即可:
1 List<String> params = new ArrayList<>(); 2 params.add("access_token:" + getAccessToken()); 3 //開始用戶更新信息 4 ThreadPoolUtils.execute(new HttpPostThread(userhand, APIAdress.UserClass, APIAdress.GetUserInfoMethod, params));
我們看到,我們創建了一個Runable實例,然后傳遞了回調的Handler、Url、Method及參數。