我理解的Hanlder--android消息传递机制
每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与ActivityUI线程通信,如果能够自己实现一个Handler,那必然是对Handler的消息传递机制理解渗透了。
一、引入
Android的UI是单线程控制的,实际上,成功的UI框架都是基于单线程的,多线程的UI框架往往因为解决并发和死锁的艰难而胎死腹中。只有UI线程能控制界面控件,但我们总是希望子线程的数据能更新到UI上,于是就有了Handler,它帮助我们实现了非UI线程向UI线程之间的通信,Handler的使用非常简单。
一个简单的示例,让一个TextView动态显示秒数:
1 public class MainActivity extends Activity { 2 private static final int UPDATE_TAG = 0x01; 3 private TextView textView; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 textView = (TextView) findViewById(R.id.textview); 10 // 启动一个线程 11 new Thread(new Runnable() { 12 13 @Override 14 public void run() { 15 int count = 1; 16 while (true) { 17 // 发送计数值到handler 18 mHandler.obtainMessage(UPDATE_TAG, String.valueOf(count)) 19 .sendToTarget(); 20 try { 21 TimeUnit.SECONDS.sleep(1); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 } 27 28 }).start(); 29 } 30 31 public Handler mHandler = new Handler() { 32 public void handleMessage(Message msg) { 33 switch (msg.what) { 34 case UPDATE_TAG: 35 // 显示计数值 36 textView.setText((String)msg.obj); 37 break; 38 } 39 } 40 }; 41 }
通过开启一个线程,线程每隔一秒发送一次计数给handler,让handler来更新TextView的内容,新开的子线程里面不能直接操控TextView,因为这违反了UI线程了单线程控制规范,如果你真要这么做,一运行就会得到CalledFromWrongThreadException异常。
二、这不就是一个观察着模式么?
最初自己理解的Handler就是一个典型的观察着模式。sendToTarget()这个方法一定以某种方式调用了Handler的 handleMessage(Message msg)方法,从而完成了我们指定的任务,于是我写了下面这样一个Handler和Message
/**** 模拟Android的Handler,申明一个供子类覆盖的方法和多个生成消息的方法*/ public class Handler {/**** 子类覆盖本方法,实现想要的操作* * @param msg*/protected void handleMessage(Message msg) {}public Message obtainMessage(int what) {Message msg = new Message(this, what, -1, -1, null);return msg;}public Message obtainMessage(int what, Object obj) {Message msg = new Message(this, what, -1, -1, obj);return msg;}public Message obtainMessage(int what, int arg1, int arg2, Object obj) {Message msg = new Message(this, what, arg1, arg2, obj);return msg;} }
下来就是Message类:
/*** * 模拟Android的Message,这里为方便演示,只写了一个构造方法。构造方法中绑定了一个Handler*/ public class Message {private final Handler target;public final int what;public final int arg1;public final int arg2;public final Object obj;public Message(Handler target, int what, int arg1, int arg2, Object obj) {this.target = target;this.arg1 = arg1;this.arg2 = arg2;this.what = what;this.obj = obj;}/**** 利用OOP的多态,调用子类的覆盖方法*/public void sendToTarget() {target.handleMessage(this);} }
测试代码如下:
![](/assets/blank.gif)
![](/assets/blank.gif)
public class HandlerTest {public static void main(String[] args) {new Thread(new MyTask(Myhandler)).start();}static class MyTask implements Runnable {private Handler handler;public MyTask(Handler handler) {this.handler = handler;}@Overridepublic void run() {while (true) {handler.obtainMessage(0, this.toString()).sendToTarget();}}}static Handler Myhandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 0:System.out.println("I am zero " + (String) msg.obj);break;}}};}
View Code
真想说上面的代码体现了一个简单的观察着模式,上面的测试代码能运行。但是,但是这样的Handler能代替Android的Handler在Activity中使用吗,用上面的Handler去替代Android的handler,结果得到是CalledFromWrongThreadException。
这很显然的,上面的Handler可以只模仿到Android Handler的形,没有模仿到其神,它只是通过一个回调模仿了Handler的工作外表,利用多态的伎俩去调用子类的handleMessage方法,想法是好的,但这最终相当于让子线程直接调用了handleMessage,从而让子线程对界面控件进行了操控,违背了UI界面的单线程控制原则,必须会报CalledFromWrongThreadException的异常。
三、子线程是如何把消息传递给UI线程
前面仿造的Handler让子线程通过Message操纵了UI控件,因此会报错,那Android是如何将子进程的消息发送给UI进程。实际上Handler的工作离不开以下几个组件:
- Message: Handler接收和处理的对象
- MessageQueue:消息队列,以FIFO的方式管理Message对象
- Looper:管理MessageQueue的对象,不断地从MessageQueue中取消息,并发送该消息对应的Handler进行处理。每个线程只有一个Looper,UI线程在创建时就已经默认创建了一个Looper,因此在Activity中可以直接创建Handler即可发送和处理消息;如果在子线程中创建Handler,必须在创建前调用Looper.prepare();这样Looper在才能做好准备,并为当前线程创建MessageQueue,以便接受Handler的Message。可以看一下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));}private Looper(boolean quitAllowed) {//私有的构造方法mQueue = new MessageQueue(quitAllowed);mRun = true;mThread = Thread.currentThread();}
在子进程创建好Handler后,调用Looper.loop()处理消息。Looper.loop()是一个死循环,如果不执行quit(),其后的代码将不会执行。现在看一个例子,我们通过点击Button给子线程的Hanlder传递消息。
public class MainActivity extends Activity {private Mythread thread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);thread = new Mythread();thread .start();}class Mythread extends Thread {public Handler handler;@Overridepublic void run() {Looper.prepare();// 在子线程中定义Handlerhandler = new Handler() {public void handleMessage(Message msg) {Log.i("handleMessage", "子线程handler.handleMessage(...)");// 接到消息让Looper退出死循环 handler.getLooper().quit();}};Toast.makeText(MainActivity.this, "before Looper.loop()",Toast.LENGTH_LONG).show();// 死循环处理消息 Looper.loop();Toast.makeText(MainActivity.this, "after Looper.loop()",Toast.LENGTH_LONG).show();}};public void onClickButton(View v) {thread.handler.obtainMessage().sendToTarget();} }
可以看到线程启动后,会Toast"before Looper.loop()",但不会立刻Toast"after Looper.loop()",只有当我们按下Button后才会显示"after Looper.loop()",因为按下Button,会给子线程的Handler发送一个空消息,而我们定义Handler对消息的处理是调用looper的quit(),从而结束Looper.loop()。注意quit()并不是简单的终止Looper.loop(),而是设置MessageQueue的一个标志,让MessageQueue的next()方法返回null,从而结束Looper.loop(),详情请看MessageQueue源码,这里我们来看看loop()的源码:
public static void loop() {...for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}...msg.target.dispatchMessage(msg);...msg.recycle();}}
另外跟踪sendToTarget()发现最终调用的方法是MessageQueue的enqueueMessage(Message msg, long when),这个方法只是把message放入队列中,并没有调用消息消息方法,消息的分发处理是在Looper的loop()中进行的。
四、是什么东西再帮UI线程跑Looper.loop()
现在理清了前面的问题又来了新的问题,是谁在帮UI线程跑Looper.loop():
- 如果是其他线程帮它Looper.loop(),那么这明显违背了UI线程的单线程控制规范;
- 如果是UI线程自己在跑,那它的死循环在那里跑着,怎么用工夫去执行onCreate(),onResume()...以及我们绑定的各种listener。
如果既要满足UI线程的单线程控制规范,又要让死循环Looper.loop()跑着不漏掉一个消息,还要响应onCreate()、listener,只有一种办法,那就是对界面的所有的操作都是由loop来驱动分发的,onCreate(),onResume()...各种listener都是Looper.loop()来调用。
实际上Android就是这么干的,要理清onCreate等各种方法绑定的Message的过程不容易,但是我们可以很简单地验证我们的上面的假设,那就是在onCreate(),onResume(),以及listener的响应方法里面引发异常,然后看一下栈信息,我们在onCreate发放中加一句int a = 1/0,看看异常的栈信息:
当发现神奇的Handler原来就是靠一个死循环来维持的,一切就变得豁然开朗了。
如果再深入,理解Activity的启动机制,我们最终找到UI线程的Looper.loop()是在ActivityThread中的main方法中执行的,让我们再看看源码:
public final class ActivityThread {...public static void main(String[] args) {SamplingProfilerIntegration.start();// CloseGuard defaults to true and can be quite spammy. We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Set the reporter for event logging in libcoreEventLogger.setReporter(new EventLoggingReporter());Security.addProvider(new AndroidKeyStoreProvider());Process.setArgV0("<pre-initialized>");//为主线程开启Looper Looper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}AsyncTask.init();if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}//UI线程的死循环在这里 Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");} }
五、真的能自己写一个Handler供Activity调用吗
现在回到我最初的问题,我们可以自己写一个Handler供Activity调用吗?答案已经很明显了,如果你足够强,你真实现了一个功能一样的Handler,那么恭喜你,你是在重写了整个Activity组件。
以上是我对Handler的理解,希望对你有所帮助,如果有总结得不恰当的地方,欢迎指正。
感谢阅读,转载请注明出处:http://www.cnblogs.com/fengfenggirl/
转载于:https://www.cnblogs.com/fengfenggirl/p/android_handler.html
我理解的Hanlder--android消息传递机制相关推荐
- Graph Decipher: A transparent dual-attention graph neural network 图解密器:一种透明的双注意图神经网络,用于理解节点分类的消息传递机制
引用 Pang Y, Liu C. Graph Decipher: A transparent dual-attention graph neural network to understand th ...
- Android消息传递机制总结
由于直播项目的推流SDK,拉流SDK已经稳定.所以这段时间一直开发业务层新需求.业务层的核心灵魂就是层层的消息传递,今天就来总结一下andorid的应用层的各种消息传递. 1.线程间通讯 --- Ha ...
- Android学习笔记——Android 签名机制详解
Android 签名机制详解 近期由于工作需要在学习 Android 的签名机制,因为没有现成资料,只能通过开发者文档和阅读博客的方式对 Android 签名机制进行大致了解.过程中查阅到的资料相对零 ...
- android串口补位,Rust多线程中的消息传递机制
代码说话. use std::thread; use std::sync::mpsc; use std::time::Duration; fn main() { let (tx, rx) = mpsc ...
- 理解 Android 消息机制
本人只是Android小菜一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 本文基于原生 Android 9.0 源码来解析 Androi ...
- 覆盖式理解Android 消息处理机制(带源码解析)
转载自:https://www.jianshu.com/p/02962454adf7 Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,A ...
- android handler 传递对象,Android之Handler消息传递机制详解
前言 在Android开发中,多线程应用是非常频繁的,其中Handler机制随处可见. 下面就本人对Handle的一些理解与大家一起分享,共同回顾下Handle异步消息传递机制. 1.Handler是 ...
- aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉
AIDL 是 Android Interface Definition Language(Android 接口定义语言)的缩写,它是 Android 进程间通信的接口语言.由于 Android 系统的 ...
- Android Handler消息传递机制
Android中只允许UI线程(也就是主线程)修改Activity里的UI组件.实际开发中,新启动的线程需要周期性地改变界面组件的属性值就需要借助Handler的消息传递机制. Handler类 Ha ...
最新文章
- HSF服务的开发与使用
- 【laravel5.4】laravel5.4系列之生成_ide_helper.php文件
- go 项目 cmd目录_Golang 项目布局浅析
- 7.16 10.19-10.22
- 重力模型matlab代码,STK基础教程.doc
- php dbutils 使用,dbutilsapi
- 5000字干货原创 | APP版本迭代如何避免踩坑?
- java与python反转Ture与False的方法
- linux无线网卡连不上网,Ubuntu 16.04无线网卡不见,无法用WiFi上网的解决方法
- 机器学习、神经网络中不懂的函数及功能实现
- Java的几种路径总结
- Table(name=“xx“)与Entity(name=“xx“) 问题
- 阶段3 3.SpringMVC·_04.SpringMVC返回值类型及响应数据类型_7 响应json数据之发送ajax的请求...
- 【QT学习之路】Charts的简单使用
- MQTT自定义透传_DTU连接阿里云
- SPSS描述性统计--非连续变量的描述统计
- 手机相机好坏测试软件,专业相机测试 画质表现均为中上等_手机评测-中关村在线...
- vue的watch监听的用法
- visio修改默认字体
- 穷不坑朋友,富不忘恩人!
热门文章
- 你试过这样写C程序吗
- HTML5 标签、事件句柄属性以及浏览器兼容情况速查手册
- Dynamics AX 2009 启动时提示“…the application files in exclusive mode”的解决
- Android UI之ImageView
- android 阴影效果
- 斐波那契数列与阶乘---递归实现
- Android应用开发SharedPreferences存储数据的使用方法
- Android 如何将Canvas上绘制的内容保存成本地图片
- docker-compose部署常用服务
- kotlin学习笔记——sqlite(anko)