Android中有很多机制,打开源码最先遇到的应该就是handler机制了,handler主要为了解决线程间通讯的问题,首先看一下handler该怎么用。

##handler的用法

1.在主线程中用handler

首先自定义一个handler,这里这么写是为了避免handler造成的内存泄漏

 static class LoadDataHandler extends Handler { private SoftReference activitySRF = null; public LoadDataHandler(MainActivity activity) { activitySRF = new SoftReference(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 因为Handler是异步的,存在退出当前类之后才接收到handler消息的情况, // 并且软引用持有的对象会在堆内存不足时存在被回收的可能, // 所以这里需要判空处理 if(null == activitySRF || null == activitySRF.get()){ return; } switch(msg.what){ case 0:{ activitySRF.get().mUserNameTxt.setText(msg.obj.toString()); } break; default:{ } break; } } }

上面通过复写handler的handleMessage方法,将具体的主线程操作放进来。

 private TextView mUserNameTxt = null; public LoadDataHandler handler= new LoadDataHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mUserNameTxt=findViewById(R.id.gone); Thread thread = new Thread(){ @Override public void run() { super.run(); try { Thread.sleep(3000); Message message = Message.obtain(); message.what=0; handler.sendMessage(message); } catch (InterruptedException e) { e.printStackTrace(); } } }; }

当我们在子线程中完成耗时操作,通过 handler.sendMessage(message);发送一个消息,主线程通过复写handleMessage方法,进行相应的UI操作。

大家是不是觉得很神奇,下面带大家看一下handler的源码,源码中了无秘密。

刚才和大家提到了handler机制中的两个大将,一个handler ,一个是message

handler主要做的是消息的发送和处理

message则是对信息的封装

还有两个幕后黑手,一个是Looper和一个MessageQueue

在我们的主线程中,会自动的给我们创建一个Looper对象,当创建好后,通过一个ThreadLock对线程和Looper进行一个绑定,确保一个线程只有一个Looper对象,而Looper内部管理着一个MessageQueue消息队列,当我们在子线程中通过handler.sendmessage发送消息后,消息就回被放到这个消息队列中,Looper通过loop方法,开启一个消息泵,对MessageQueue进行轮询,当有满足条件的消息时就会取出消息,通过handle.dispatchMessage()去处理消息

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }

通过对Message中callback的判断,进入两个handler的方法

1. handleCallback(msg);

2.handleMessage(msg);

有人肯定会问callback哪里哪里的,大家不要忘了handler还有有个post方法,可以传一个Runnable接口,当我们在主线程中调用handler.post()方法时,也会把消息放到消息队列中,当取出消息调用handleCallback时,就会去执行Runnable的run方法,所以可以看出,run()的执行是在主线程中,而不是通过post发送的消息,则交给了handleMessage()去处理,也就完成了线程间的通信。

上面是在主线程中应用handler,进行线程间通信,那我们在子线程中该怎么用handler了

2.在子线程中用handler

Thread thread1 = new Thread(){ @Override public void run() { super.run(); Looper.prepare(); handler =new Handler(Looper.myLooper()){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: break; } } }; Looper.loop();  } };Thread thread2 = new Thread(){ @Override public void run() { super.run(); for (int i=0 ; i<10; i++){ handler.sendEmptyMessage(1); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; thread1.start(); thread2.start();

上边的代码很好理解,新建一个线程隔3s发送一次消息,这和在之前handler发送消息没什么不同。下面来看接受消息的线程,在代码开始的位置,先调用了Looper.prepare();方法,来看一下这个方法做了什么

public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }

通过代码可以知道,通过prepare方法,新建了一个loop对象,并通过ThreadLocal就线程和looper进行了一个绑定,这也正好印证了我在上边说过主线程的操作,我们要在子线程中操作handler也要仿照主线程的做法,给子线程绑定一个looper对象,在通过调用looper.loop()方法,开启轮训。这就是在子线程中应用handler。

#注

问:有人可能比较迷惑,不是主线程不能做耗时操作吗,主线程中也通过looper.loop()方法进行轮训,为什么系统没有卡死在那里?

答:有编程经验的肯定都知道,等代码执行完了,程序也就结束了,正因为主线程一直做着死循环,所以程序才没有一闪而过,还有就是 ,android是以事件驱动的,主线程的操作都是通过handler进行的,虽然处于死循环,但是在循环的过程中线程也进行这事件的处理,比如,打开新页面,更新ui,所以系统永远不会卡死在那里

问:一直这个死循环,会不会很费电

答:这里设计到Linux 的pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源

一个普通handler会持有activity引用吗_详解handler机制相关推荐

  1. java 引用传递_详解java的值传递、地址传递、引用传递

    详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前 ...

  2. python自己创建模块引用失败_详解Python import方法引入模块的实例 Python怎么import自己写的模块...

    python中 import导入模块失败的问题? python中的import引用不了模块我傻,为你傻;我痛,为你痛;深夜里,你是我一种惯性的回忆. 为什么我用from lianxi import*就 ...

  3. 一个竖杠在python中代表什么_详解js运算符单竖杠“|”与“||”的用法和作用介绍...

    在js开发应用中我们通常会碰到"|"与"||"了,那么在运算中"|"与"||"是什么意思呢? 在js整数操作的时候,相当 ...

  4. Activity的启动模式详解

    Activity的启动模式详解 Activity有四种载入模式:standard(默认), singleTop, singleTask和 singleInstance. (1).standard(默认 ...

  5. PHP的传值与引用,php 传值与传引用的区别详解

    摘要 腾兴网为您分享:php 传值与传引用的区别详解,掌上公交,信用管家,天翼阅读,平安知鸟等软件知识,以及k歌达人,ml2010打印机驱动,维也纳大学app,建玛特,网盘快搜,中国禁毒数字展览馆,爱 ...

  6. 【iOS沉思录】如何招聘一个靠谱的 iOS程序员+面试题详解

    说明:面试题来源是微博@我就叫Sunny怎么了的这篇博文:<招聘一个靠谱的 iOS>,其中共55题,除第一题为纠错题外,其他54道均为简答题. 出题者简介: 孙源(sunnyxx),目前就 ...

  7. android 启动过程详解,Activity的启动过程详解(基于Android10.0)

    一.概述 话说Android中有四大组件:Activity.Service.BroadcastReceiver.ContentProvider.咱们最常接触也是用户直接感受到的便是Activity了, ...

  8. jq的插件 vue中引用_详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件

    本篇文章主要介绍了详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件,具有一定的参考价值,有兴趣的可以了解一下 使用vue-cli构建的vue项目,webpack的配置文 ...

  9. 演示IPFS的一个完整的流程以及针对部分概念的详解

    整体的流程 1,创建ipfs节点 通过ipfs init在本地计算机建立一个IPFS节点 本文有些命令已经执行过了,就没有重新初始化.部分图片拷贝自先前文档,具体信息应以实物为准 $ ipfs ini ...

最新文章

  1. 京东金融将发布重量级技术与数据产品 招募合作伙伴共拓蓝海市场
  2. 复旦 哈工大计算机学院,国内高校中哈工大和上交复旦在一个档次吗?从这些方面看你就知道...
  3. 白盒测试基本路径生成工具_基于基本最短路径列生成的车辆路径问题
  4. imx6的Linux默认颜色,MY-IMX6 Linux-3.14 测试手册(1)
  5. React 开发常见报错解决方法
  6. node.js学习笔记之模拟路由
  7. R Markdown与RStudio IDE深度结合
  8. http://95u.free.fr/index.php,Electronic Software Distribution Service
  9. 关于函数YEAR、NOW的使用方法以及求年龄的函数公式
  10. seed lab 2020 packet sniffing and spoofing lab
  11. 计算机音乐谱东演员,抖音计算机乐谱有哪些 抖音计算机乐谱分享
  12. 奇东锐腾服务器无法显示,奇东锐腾软件 Keydone Return Software
  13. 6. 中文命名实体提取
  14. 3.7 Feature envy(依恋情结)
  15. SqlServer 内存篇(四)—— 各部分内存不足特征及解决方法
  16. 把本地项目上传到码云的整个过程(图文详解)
  17. 跨领域的智能云管理平台-孙立辉(云平台 CSM)
  18. Kotlin1.5 新特性之 Sealed Interface(密封接口)
  19. 015 《海尔是海:张瑞敏随笔选录》读书记
  20. h5背景图片尺寸怎么设置_CSS3中background-size实现背景图片大小可自定义的几种效果(代码实例 )...

热门文章

  1. 《Go语言程序设计》读书笔记 (九) 命令工具集
  2. Minor GC和Full GC触发条件
  3. docker搭建mysql主从
  4. sklearn 随机森林
  5. Python Django 之 Views HttpRequest HttpReponse
  6. [转]分布式中Redis实现Session终结篇
  7. 创建主机地址 (A) DNS 记录
  8. C语言实现控制台中光标随意移动
  9. 2013年新年礼物---CrossFPC 终于出来了
  10. HDU-2525 Clone Wars 模拟