全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

android非RxJava环境下使用Handler实现预加载

在进行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。
这种情况下我们可以用这种方式来解决。

最后,源码下载地址


# android  # RxJava  # Handler  # 预加载  # Android开发RecyclerView性能优化之异步预加载  # android使用RxJava实现预加载  # Android 开发中fragment预加载问题  # android 预加载进程的实现方法  # 加载  # 的是  # 都是  # 放在  # 移除  # 还可以  # 我们可以  # 这篇文章  # 中对  # 就比  # 是一个  # 也有  # 充要条件  # 在这个  # 多个  # 下载地址  # 可以用  # 作为一个  # 分别为  # 不被 


相关文章: 建站10G流量真的够用吗?如何应对访问高峰?  再谈Python中的字符串与字符编码(推荐)  如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?  如何在腾讯云免费申请建站?  深圳 网站制作,深圳招聘网站哪个比较好一点啊?  台州网站建设制作公司,浙江手机无犯罪记录证明怎么开?  定制建站流程步骤详解:一站式方案设计与开发指南  Python多线程使用规范_线程安全解析【教程】  南京做网站制作公司,南京哈发网络有限公司,公司怎么样,做网页美工DIV+CSS待遇怎么样?  网站制作报价单模板图片,小松挖机官方网站报价?  视频网站制作教程,怎么样制作优酷网的小视频?  制作公司内部网站有哪些,内网如何建网站?  建站之星云端配置指南:模板选择与SEO优化一键生成  网站制作免费,什么网站能看正片电影?  海南网站制作公司有哪些,海口网是哪家的?  洛阳网站制作公司有哪些,洛阳的招聘网站都有哪些?  建站之星如何助力企业快速打造五合一网站?  武汉网站制作费用多少,在武汉武昌,建面100平方左右的房子,想装暖气片,费用大概是多少啊?  一键制作网站软件下载安装,一键自动采集网页文档制作步骤?  中山网站制作网页,中山新生登记系统登记流程?  建站之星与建站宝盒如何选择最佳方案?  如何选择长沙网站建站模板?H5响应式与品牌定制哪个更优?  如何在香港服务器上快速搭建免备案网站?  建站之星如何修改网站生成路径?  如何优化Golang Web性能_Golang HTTP服务器性能提升方法  高防服务器租用指南:配置选择与快速部署攻略  如何在Golang中使用replace替换模块_指定本地或远程路径  青岛网站建设如何选择本地服务器?  如何有效防御Web建站篡改攻击?  购物网站制作公司有哪些,哪个购物网站比较好?  北京网页设计制作网站有哪些,继续教育自动播放怎么设置?  如何获取上海专业网站定制建站电话?  微信网站制作公司有哪些,民生银行办理公司开户怎么在微信网页上查询进度?  如何用已有域名快速搭建网站?  如何制作一个表白网站视频,关于勇敢表白的小标题?  开源网站制作软件,开源网站什么意思?  网站设计制作公司地址,网站建设比较好的公司都有哪些?  建站之星如何开启自定义404页面避免用户流失?  如何选择靠谱的建站公司加盟品牌?  专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?  如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?  头像制作网站在线制作软件,dw网页背景图像怎么设置?  网页设计与网站制作内容,怎样注册网站?  Swift中swift中的switch 语句  常州自助建站费用包含哪些项目?  如何确认建站备案号应放置的具体位置?  利用JavaScript实现拖拽改变元素大小  大连 网站制作,大连天途有线官网?  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  专业网站制作企业网站,如何制作一个企业网站,建设网站的基本步骤有哪些? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。