一个普通handler会持有activity引用吗_详解handler机制
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机制相关推荐
- java 引用传递_详解java的值传递、地址传递、引用传递
详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前 ...
- python自己创建模块引用失败_详解Python import方法引入模块的实例 Python怎么import自己写的模块...
python中 import导入模块失败的问题? python中的import引用不了模块我傻,为你傻;我痛,为你痛;深夜里,你是我一种惯性的回忆. 为什么我用from lianxi import*就 ...
- 一个竖杠在python中代表什么_详解js运算符单竖杠“|”与“||”的用法和作用介绍...
在js开发应用中我们通常会碰到"|"与"||"了,那么在运算中"|"与"||"是什么意思呢? 在js整数操作的时候,相当 ...
- Activity的启动模式详解
Activity的启动模式详解 Activity有四种载入模式:standard(默认), singleTop, singleTask和 singleInstance. (1).standard(默认 ...
- PHP的传值与引用,php 传值与传引用的区别详解
摘要 腾兴网为您分享:php 传值与传引用的区别详解,掌上公交,信用管家,天翼阅读,平安知鸟等软件知识,以及k歌达人,ml2010打印机驱动,维也纳大学app,建玛特,网盘快搜,中国禁毒数字展览馆,爱 ...
- 【iOS沉思录】如何招聘一个靠谱的 iOS程序员+面试题详解
说明:面试题来源是微博@我就叫Sunny怎么了的这篇博文:<招聘一个靠谱的 iOS>,其中共55题,除第一题为纠错题外,其他54道均为简答题. 出题者简介: 孙源(sunnyxx),目前就 ...
- android 启动过程详解,Activity的启动过程详解(基于Android10.0)
一.概述 话说Android中有四大组件:Activity.Service.BroadcastReceiver.ContentProvider.咱们最常接触也是用户直接感受到的便是Activity了, ...
- jq的插件 vue中引用_详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件
本篇文章主要介绍了详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件,具有一定的参考价值,有兴趣的可以了解一下 使用vue-cli构建的vue项目,webpack的配置文 ...
- 演示IPFS的一个完整的流程以及针对部分概念的详解
整体的流程 1,创建ipfs节点 通过ipfs init在本地计算机建立一个IPFS节点 本文有些命令已经执行过了,就没有重新初始化.部分图片拷贝自先前文档,具体信息应以实物为准 $ ipfs ini ...
最新文章
- 京东金融将发布重量级技术与数据产品 招募合作伙伴共拓蓝海市场
- 复旦 哈工大计算机学院,国内高校中哈工大和上交复旦在一个档次吗?从这些方面看你就知道...
- 白盒测试基本路径生成工具_基于基本最短路径列生成的车辆路径问题
- imx6的Linux默认颜色,MY-IMX6 Linux-3.14 测试手册(1)
- React 开发常见报错解决方法
- node.js学习笔记之模拟路由
- R Markdown与RStudio IDE深度结合
- http://95u.free.fr/index.php,Electronic Software Distribution Service
- 关于函数YEAR、NOW的使用方法以及求年龄的函数公式
- seed lab 2020 packet sniffing and spoofing lab
- 计算机音乐谱东演员,抖音计算机乐谱有哪些 抖音计算机乐谱分享
- 奇东锐腾服务器无法显示,奇东锐腾软件 Keydone Return Software
- 6. 中文命名实体提取
- 3.7 Feature envy(依恋情结)
- SqlServer 内存篇(四)—— 各部分内存不足特征及解决方法
- 把本地项目上传到码云的整个过程(图文详解)
- 跨领域的智能云管理平台-孙立辉(云平台 CSM)
- Kotlin1.5 新特性之 Sealed Interface(密封接口)
- 015 《海尔是海:张瑞敏随笔选录》读书记
- h5背景图片尺寸怎么设置_CSS3中background-size实现背景图片大小可自定义的几种效果(代码实例 )...