Handler的工作原理。为啥在子线程中使用Handler会抛出异常
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler的工作原理。为啥在子线程中使用Handler会抛出异常相关的知识,希望对你有一定的参考价值。
参考技术A Handler了它在安卓中常见的工作就是子线程与主线程的通信,其实可以直接归类为线程和线程间的通信谈到Handler会涉及到以下几个类
Handler、Message、Looper、MessageQueue
我们一个一个来说,首先设置场景在一个线程中通知另一个线程
1,创建Looper和创建MessageQueue
首先在一个线程里要用Handler,那么需要准备Looper,调用Looper.prepare(),我们在主线程里面不用准备Looper,那么是因为在我们的主线程中已经给我们初始化好了Looper,
在准备Looper的时候会去校验这个线程中是否存在Looper,如果有Looper那么抛出异常(注意:有一些面试官会问如何判断一个线程中是否存在一个Looper的,它用的是ThreadLocal,它的作用是在线程范围内保证变量的唯一性,Thread中会维护一个类似HashMap的东西,然后用ThreadLocal对象作为key,value就是要存储的变量值,这样就保证了存储数据的唯一性)
如果没有Looper那么new一个Looper,new Looper的同时会new 一个MessageQueue(注意:一个线程中只有一个Looper一个MessageQueue)
然后Looper.loop()就可以启动轮训来轮训消息队列了
2,创建Handler
这个我们在熟悉不过了,继承一个Handler然后复写handlerMessage方法,这里其实面试官也可以问一些比较细致的问题,如下
注意:有面试官会问可以创建几个Handler,我负责任的说是多个,哪个Handler发送的消息哪个Handler处理(吐槽一下有的面试官,你自己把自己要问的问题搞清楚了在去问别人,非要跟我犟只能创建一个)
3,创建Message
Message一般都是使用Message.obtain(),它这里面是有一个spool指向一个Message对象,还有一个next指向下一个Message,它里面维护了一个链表,obtain的时候在表头头取Message,在Message回收的时候在表头添加一个Message,类似栈,默认大小是50
4,消息的处理
Handler对象sendMessage发送消息放入的MessageQueue队列中,Looper轮训到它,然后就开始处理Message,Message会有一个target去记录是哪个Handler发送的它,会调用这个Handler中的dispatchMessage()方法,如果说Message中实现了CallBack那么调用Message中的CallBack,如果Handler中实现了Callback调用Handler中CallBack,否则就都调动Handler中的handleMessage方法
根据上面的原理分析,那就是因为你子线程中没有Looper,给他一个Looper就好了
Android--Handler的使用方法:在子线程中更新界面
本文主要介绍Android的Handler的使用方法。Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列。每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联。
- Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器。2)线程间通信。在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息。当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Handler对象,就可以通过该对象向父线程的消息队列发送消息了。由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面。
◆ 通过Runnable在子线程中更新界面的例子
在onCreate中创建Handler public class HandlerTestApp extends Activity { Handler mHandler; TextView mText; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mHandler = new Handler();//创建Handler mText = (TextView) findViewById(R.id.text0);//一个TextView }构建Runnable对象,在runnable中更新界面,此处,我们修改了TextView的文字.此处需要说明的是,Runnable对象可以再主线程中创建,也可以再子线程中创建。我们此处是在子线程中创建的。
Runnable mRunnable0 = new Runnable() { @Override public void run() { // TODO Auto-generated method stub mText.setText("This is Update from ohter thread, Mouse DOWN"); } };
创建子线程,在线程的run函数中,我们向主线程的消息队列发送了一个runnable来更新界面。
private void updateUIByRunnable(){ new Thread() { //Message msg = mHandler.obtainMessage(); public void run() { //mText.setText("This is Update from ohter thread, Mouse DOWN");//这句将抛出异常 mHandler.post(mRunnable0); } }.start(); }
◆ 用Message在子线程中来更新界面
-
用Message更新界面与Runnable更新界面类似,只是需要修改几个地方。
实现自己的Handler,对消息进行处理 private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); switch(msg.what) { case UPDATE://在收到消息时,对界面进行更新 mText.setText("This update by message"); break; } } } 在新的线程中发送消息 private void updateByMessage() { //匿名对象 new Thread() { public void run() { //mText.setText("This is Update from ohter thread, Mouse DOWN"); //UPDATE是一个自己定义的整数,代表了消息ID Message msg = mHandler.obtainMessage(UPDATE); mHandler.sendMessage(msg); } }.start(); }
以上是关于Handler的工作原理。为啥在子线程中使用Handler会抛出异常的主要内容,如果未能解决你的问题,请参考以下文章