我理解的Hanlder--android消息传递机制
每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与ActivityUI线程通信,如果能够自己实现一个Handler,那必然是对Handler的消息传递机制理解渗透了。
一、引入
Android的UI是单线程控制的,实际上,成功的UI框架都是基于单线程的,多线程的UI框架往往因为解决并发和死锁的艰难而胎死腹中。只有UI线程能控制界面控件,但我们总是希望子线程的数据能更新到UI上,于是就有了Handler,它帮助我们实现了非UI线程向UI线程之间的通信,Handler的使用非常简单。
一个简单的示例,让一个TextView动态显示秒数:
public class MainActivity extends Activity {
private static final int UPDATE_TAG = 0x01;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textview);
// 启动一个线程
new Thread(new Runnable() {
@Override
public void run() {
int count = 1;
while (true) {
// 发送计数值到handler
mHandler.obtainMessage(UPDATE_TAG, String.valueOf(count))
.sendToTarget();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TAG:
// 显示计数值
textView.setText((String)msg.obj);
break;
}
}
};
}
通过开启一个线程,线程每隔一秒发送一次计数给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);
}
}
测试代码如下:
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;
}
@Override
public void run() {
while (true) {
handler.obtainMessage(0, this.toString()).sendToTarget();
}
}
}
static Handler Myhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
System.out.println("I am zero " + (String) msg.obj);
break;
}
}
};
}
真想说上面的代码体现了一个简单的观察着模式,上面的测试代码能运行。但是,但是这样的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; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread = new Mythread();
thread .start();
} class Mythread extends Thread {
public Handler handler; @Override
public void run() {
Looper.prepare();
// 在子线程中定义Handler
handler = 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 block
if (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 libcore
EventLogger.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(new
LogPrinter(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/
我理解的Hanlder--android消息传递机制的更多相关文章
- Android 消息传递机制
线程间消息传递机制 1.消息怎么发送的? 我们都知道当调用Handler发送消息的时候,不管是调用 sendMessage,sendEmptyMessage,sendMessageDelayed还是其 ...
- Handler Looper源码解析(Android消息传递机制)
Android的Handler类应该是常用到的,多用于线程间的通信,以及子线程发送消息通知UI线程刷新View等等.这里我主要总结下我对整个消息传递机制,包括Handler,Looper,Messag ...
- 安卓开发_深入理解Handler消息传递机制
一.概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面 二.消息类(Message) 消息类是存 ...
- Android学习笔记-事件处理之Handler消息传递机制
内容摘要:Android Handler消息传递机制的学习总结.问题记录 Handler消息传递机制的目的: 1.实现线程间通信(如:Android平台只允许主线程(UI线程)修改Activity里的 ...
- Android消息传递之Handler消息机制
前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...
- 理解Android安全机制
本文从Android系统架构着手,分析Android的安全机制以SE Android,最后给出一些Android安全现状和常见的安全解决方案. 1.Android系统架构 Android采用分层的系统 ...
- Android学习之Handler消息传递机制
Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户 ...
- Android异步消息传递机制源码分析
1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...
- (Android数据传递)Intent消息传递机制 “Intent”“数据传递”
Intent类的继承关系: 需要注意的是,该类实现了Parcelable(用于数据传递)和Cloneable接口. Intent是一种(系统级别的)消息传递机制,可以在应用程序内使用,也可以在应用 ...
随机推荐
- JavaScript Patterns 4.8 Function Properties - A Memoization Pattern
Gets a length property containing the number of arguments the function expects: function func(a, b, ...
- Servlet/JSP-08 EL表达式
EL - Expression Language 一. 基本语法 EL表达式以 ${} 的形式访问变量的值 1. "." 和 "[]" 运算符 <%@ p ...
- tomcat黑/白名单设置
vim $tomcat_home/conf/server.xml(可以单个IP或者多个ip,多个ip用|分隔,支持正则) <Context path=" reloadable=&quo ...
- html页面高亮关键词
function hightLightTheKeyWord(searchParam,$dom){ if(searchParam&&!/^\s*$/.test(searchParam)) ...
- Android 项目中文件夹的说明与作用(转)
(转自:http://blog.csdn.net/goodshot/article/details/11529731) Android 项目中文件夹的作用 1. src:存放所有的*.java源程序. ...
- 010 使用netmap API接管网卡,接收数据包,回应ARP请求
一.本文目的: 上一节中,我们已经在CentOS 6.7 上安装好了netmap,也能接收和发送包了,这节我们来调用netmap中的API,接管网卡,对网卡上收到的数据包做分析,并回应ARP请求. 二 ...
- aircack-ng抓握手包
1.关闭影响进程 airmon-ng check kill 将要进入监听模式的无线网卡断开它已连接的AP 2.查看无线网卡的名字 ifconfig ,例如 wlan0 3.进入监听模式: airmon ...
- CTO和技术副总裁应该如何分工?谁才是技术领导者?
谁是初创公司的技术领导者,是CTO还是技术副总裁?任何在创业公司工作的人都知道,我们不应该去问这个问题.因为这两个是非常不同的角色,角色本身会随着创业公司的发展而变化,两者对于业务规模都很重要. 简单 ...
- 鸿雁电器oa系统中决策支持模块效果
公司简介鸿雁电器是国内著名的建筑电器产品的生产.经营企业,同时也是国家863计划CIMS(计算机集成制造系统)应用工程示范企业.浙江省高新技术企业.浙江省专利示范企业和杭州市信息化试点企业.企业系统泛 ...
- 《100种过度医疗大公开》:转译自日文版,日文版依据的是美国的“Choosing Wisely”项目。三星推荐
本书转译自日文,日文版则是在美国的“Choosing Wisely”项目中选择了100个相对常见的过度医疗项目做解说.Choosing Wisely项目,是由美国多个专业医学组织发起的列出过度医疗项目 ...