国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

android非RxJava環境下使用Handler實現預加載

2019-12-12 03:54:50
字體:
來源:轉載
供稿:網友

在進行Android客戶端界面開發時,我們常常會需要將從服務端獲取的數據展示到頁面布局上,由于數據顯示到布局的前置條件是頁面布局已初始化完成,否則會出現空指針異常,所以一般我們需要將網絡請求放在布局初始化完成之后。
傳統的頁面加載流程是:

問題:

如果加載的UI布局比較復雜,或者初始化邏輯執行的時間比較多,那么網絡請求開始執行的時間就比較晚,最終完成頁面加載的時間就比較長。
如果頁面初始化和網絡加載能同時進行,等兩者都執行結束后,再在布局上展示網絡數據,這樣我們就可以縮短整個頁面的加載時間了。
所以,我們期望的頁面加載流程是:

這個流程我們稱之為:預加載

預加載的目標任務可以是一個網絡請求,也可以是其它一些耗時操作,例如:加載一張圖片到控件上展示
在實現預加載方案之前,我們需要了解一下Handler工作機制中的SyncBarrier概念,對Barrier概念了解可以看這篇文章中對“同步分割欄”的介紹, 此處我們簡單理解為:

在MessageQueue中添加一個特殊的msg,將這個msg作為一個標記,在這個標記被移除之前,當前MessageQueue隊列中排在它后面的其它(非async) 的message不會被handler處理。

我們可以先不理會什么是 非async 的message,若需要了解更多,這篇文章中對“同步分割欄”的介紹中也有相關介紹。

利用這個特性,我們可以:

啟動一個HandlerThread來異步執行網絡請求
設置一個標記SyncBarrier,此后在message將一直在messageQueue中不被執行
網絡請求成功后,post一個任務來執行展示數據
布局初始化成功后,移除SyncBarrier
將展示數據的任務post到ui線程來執行
步驟3和步驟4的先后順序可以交換

其中,在android api 22及之前,設置標記SyncBarrier可以由

HandlerThread.getLooper().postSyncBarrier();

在android api 23以后,需要調用的方法為:

HandlerThread.getLooper().getQueue().postSyncBarrier();

同樣的,移除標記的方法分別為:

HandlerThread.getLooper().removeSyncBarrier(token);HandlerThread.getLooper().getQueue().removeSyncBarrier(token);

不幸的是:這些方法都是@hide的,無法直接調用。
幸運的是:我們還有反射

封裝工具類如下: PreLoader.java

import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.MessageQueue;import java.lang.reflect.Method;/*** 使用Handler方式實現的預加載* @author billy.qi*/public class PreLoader { private int token; private Handler mainThreadHandler;//用于將結果處理的task放在主線程中執行 private HandlerThread handlerThread;//提供一個異步線程來運行預加載任務 private Handler handler;//預加載使用的handler private PreLoader(final Runnable task) { mainThreadHandler = new Handler(Looper.getMainLooper()); handlerThread = new HandlerThread("pre-loader") {  @Override  protected void onLooperPrepared() {  super.onLooperPrepared();  handler = new Handler();  handler.post(task);  //設置同步分割,后面post進來的sync為true的message將暫停執行  Looper looper = handlerThread.getLooper();  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {   postSyncBarrier(looper.getQueue());  } else {   postSyncBarrier(looper);  }  } }; handlerThread.start(); } /** * 開啟預加載 * 比如:開始網絡請求(在HandlerThread中執行) * @param task 預加載任務 */ public static PreLoader preLoad(Runnable task) { return new PreLoader(task); } /** * 處理加載結果, 一般在預加載任務處理完成后調用 * 由于有handler所在的messageQueue設置了同步分割(SyncBarrier),該task * @param task 在主線程中執行的任務 */ public void performResultTask(final Runnable task) { if (handler != null) {  handler.post(new Runnable() {  @Override  public void run() {   mainThreadHandler.post(task);  }  }); } } /** * 可以開始執行預加載結果處理 */ public void readyToGetData() { Looper looper = handlerThread.getLooper(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {  removeSyncBarrier(looper.getQueue()); } else {  removeSyncBarrier(looper); } } private void postSyncBarrier(Object obj) { try{  Method method = MessageQueue.class.getMethod("postSyncBarrier");  token = (int) method.invoke(obj); } catch(Exception e) {  e.printStackTrace(); } } private void removeSyncBarrier(Object obj) { try{  Method method = MessageQueue.class.getMethod("removeSyncBarrier", int.class);  method.invoke(obj, token); } catch(Exception e) {  e.printStackTrace(); } } public void destroy() { handlerThread.quit(); handlerThread = null; handler = null; mainThreadHandler = null; }}

在activity中使用實例:

import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;public class PreLoaderActivity extends AppCompatActivity { private PreLoader preLoader; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { preLoad();//啟動預加載 //進行頁面布局加載及其它初始化工作 super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView); //布局初始化完成 preLoader.readyToGetData(); } private void preLoad() { //預加載的任務 preLoader = PreLoader.preLoad(new Runnable() {  //將得到的請求結果展示到布局控件上  @Override  public void run() {  //模擬網絡請求耗時  try {   Thread.sleep(500);  } catch (InterruptedException e) {   e.printStackTrace();  }  final String result = "result";  //在UI線程執行的顯示任務  preLoader.performResultTask(new Runnable() {   //將得到的請求結果展示到布局控件上   @Override   public void run() {   textView.setText(result);   }  });  } }); } @Override protected void onDestroy() { super.onDestroy(); //在onDestroy()中進行銷毀 preLoader.destroy(); }}


通過預加載,讓一部分異步任務提前執行,可以用來提高整體速度。
以上都是以網絡請求作為預加載的目的,它同時還可以用來預加載圖片、預加載文件、讀取數據庫等;
除了預加載外,還可以用來作為多個任務并行,并全部執行完之后,再執行另一個任務對之前所有任務執行的結果進行處理。

總結:
在多線程開發時,經常會遇到以下情況:

任務A、任務B(甚至更多任務)是任務C的充要條件,為了提高效率,我們希望AB同時執行,當AB都完成之后,開始執行任務C。
這種情況下我們可以用這種方式來解決。

最后,源碼下載地址

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 辽中县| 买车| 全椒县| 界首市| 哈巴河县| 双桥区| 通化市| 永仁县| 印江| 双江| 保德县| 玉山县| 金沙县| 扶风县| 工布江达县| 双牌县| 浠水县| 乾安县| 桃园市| 兴山县| 浑源县| 屏东县| 河津市| 蓬溪县| 卢龙县| 瑞金市| 江陵县| 鹤峰县| 吐鲁番市| 麻阳| 神农架林区| 织金县| 乐昌市| 泸西县| 南川市| 桓仁| 屏东市| 潞城市| 晋中市| 外汇| 廊坊市|