关于Handler消息机制一点愚见与总结
2016-08-20 16:18
393 查看
Handler消息机制在android刷新UI中是经常使用到的一个工具,但是Handler并不是专门用于刷新UI,是为了让任务能够轻易的切换到Handler中操作。
出现的缘由,主要是因为android中的UI组件为了追求效率,而放弃了对UI组件在并发情况下加锁情况的解决。而是规定了子线程无法访问UI控件。
这个时候,修改主线程的UI该怎么解决呢?这时就需要Handler的消息机制,来完成对UI操作切换到主线程。
Handler消息机制包含四个部分:Message,MessageQueue,Looper,Handler。
Message :顾名思义就是线程中传递消息。(IPC中Messenger中也用到Message)。
MessageQueue:是指消息队列,每个消息进入之后,就进入到该队列进行排队,等待Handler的处理。
Handler:处理发送过来的消息。,一般需要重写handleMessage()来达到目的。
Looper:可以说是核心的模块,作为每个线程MessageQueue的管理者。
上面个模块相信开发者都比较熟悉,这一篇文章主要是讲自己对Looper的理解和总结。
Looper从名字可以知道这是一个循环,具体是做什么的呢?
当我们在子线程中声明Handler的时候会发现下面这个错误:
这个错误就是没有调用Looper.prepare()这个函数。
看看Android下面是怎么写的:
从上面可以知道,Looper.prepare()是获取ThreadLocal中的数据(ThreadLocal是用于在线程中存储数据)。由于子线程声明Handler像主线程里面自己已经有MainLooper。所以我们必须在子线程中添加Looper.prepare(),获取存储在线程中数据。
别忘了Handler就是为了将任务切换到Handler线程中去。
Looper主要的工作除了获取线程上的数据,更重要的工作是在loop()方法中有一个死循环,不断的循环检测MessageQueue中是否为空:
源码:
可以知道在这个循环机制中,调用Message msg = queue.next();来不断的获取队列中下一个message,知道获取到的信息为null。接着通过看一下next()函数,真正的消息阻塞是来自于MessageQueue方法中next()。
MessageQueue的next()就是一个循环体,每当消息传来的时候,next()会返回消息,并且从MessageQueue这个链表中移除。
最后通过msg.target.dispatchMessage(msg);将消息传到Handler中处理。
而行代码就是切换线程的关键:
源码:
在这里就进行了对消息的处理,那么切换线程有从何而来呢?
当然是指这个msg.target这个对象了,它来源于下面:
这里的msg.target指派了this,是指插入队列函数enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)这个函数的上下文,哪里又调用它呢,就是Handler的sendMessage(Message msg)嵌套调用了。
这就完成了Handler的分析了,总结为一个图:
![](https://img-blog.csdn.net/20160820160517880)
下面是一个错误的尝试:
就会弹出下面这个错误警告,看到了吧,在线程中创建Handler,最后还是返回到handler的线程,违反了子线程不能访问 UI线程的原则。如果硬要这么要修改就必须中间多一个来自主线程创建的Handler才能够修改上面的textView,不推荐这么做。
正确的调用时第一个Button来调用调用创建在主线程的Handler:
感谢任玉刚的开发探索与艺术,以及鸿样大神的启发。
出现的缘由,主要是因为android中的UI组件为了追求效率,而放弃了对UI组件在并发情况下加锁情况的解决。而是规定了子线程无法访问UI控件。
这个时候,修改主线程的UI该怎么解决呢?这时就需要Handler的消息机制,来完成对UI操作切换到主线程。
Handler消息机制包含四个部分:Message,MessageQueue,Looper,Handler。
Message :顾名思义就是线程中传递消息。(IPC中Messenger中也用到Message)。
MessageQueue:是指消息队列,每个消息进入之后,就进入到该队列进行排队,等待Handler的处理。
Handler:处理发送过来的消息。,一般需要重写handleMessage()来达到目的。
Looper:可以说是核心的模块,作为每个线程MessageQueue的管理者。
上面个模块相信开发者都比较熟悉,这一篇文章主要是讲自己对Looper的理解和总结。
Looper从名字可以知道这是一个循环,具体是做什么的呢?
当我们在子线程中声明Handler的时候会发现下面这个错误:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
这个错误就是没有调用Looper.prepare()这个函数。
看看Android下面是怎么写的:
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(true)); }
从上面可以知道,Looper.prepare()是获取ThreadLocal中的数据(ThreadLocal是用于在线程中存储数据)。由于子线程声明Handler像主线程里面自己已经有MainLooper。所以我们必须在子线程中添加Looper.prepare(),获取存储在线程中数据。
别忘了Handler就是为了将任务切换到Handler线程中去。
Looper主要的工作除了获取线程上的数据,更重要的工作是在loop()方法中有一个死循环,不断的循环检测MessageQueue中是否为空:
源码:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } }
可以知道在这个循环机制中,调用Message msg = queue.next();来不断的获取队列中下一个message,知道获取到的信息为null。接着通过看一下next()函数,真正的消息阻塞是来自于MessageQueue方法中next()。
MessageQueue的next()就是一个循环体,每当消息传来的时候,next()会返回消息,并且从MessageQueue这个链表中移除。
最后通过msg.target.dispatchMessage(msg);将消息传到Handler中处理。
而行代码就是切换线程的关键:
源码:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
在这里就进行了对消息的处理,那么切换线程有从何而来呢?
当然是指这个msg.target这个对象了,它来源于下面:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
这里的msg.target指派了this,是指插入队列函数enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)这个函数的上下文,哪里又调用它呢,就是Handler的sendMessage(Message msg)嵌套调用了。
这就完成了Handler的分析了,总结为一个图:
下面是一个错误的尝试:
public class MyHandler extends Handler{ private Looper myLooper; public MyHandler(Looper myLooper){ this.myLooper = myLooper; } @Override public void handleMessage(Message msg){ switch (msg.what) { case 2: Log.e("threadmsg",""+msg.what); // Message message = handler.obtainMessage(); // message.what = 2; // handler.sendMessage(message); text.setText("hello world"); myLooper.quitSafely(); break; default: super.handleMessage(msg); break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView)findViewById(R.id.text); button = (Button)findViewById(R.id.change); Tbutton = (Button)findViewById(R.id.change2); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Message message = new Message(); message.what = 1; handler.sendMessage(message); } }); Tbutton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Looper.prepare(); MyHandler myHandler = new MyHandler(Looper.myLooper()); Message msg = new Message(); msg.what = 2; myHandler.sendMessage(msg); Looper.loop(); } }).start(); } }); }
就会弹出下面这个错误警告,看到了吧,在线程中创建Handler,最后还是返回到handler的线程,违反了子线程不能访问 UI线程的原则。如果硬要这么要修改就必须中间多一个来自主线程创建的Handler才能够修改上面的textView,不推荐这么做。
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
正确的调用时第一个Button来调用调用创建在主线程的Handler:
public class MainActivity extends Activity { private TextView text; private Button button; private Button Tbutton; private Handler handler = new Handler(){ public void handleMessage(Message msg){ switch (msg.what) { case 1: text.setText("nice to meet you"); break; case 2: text.setText("hello world"); default: break; } } }; public class MyHandler extends Handler{ private Looper myLooper; public MyHandler(Looper myLooper){ this.myLooper = myLooper; } @Override public void handleMessage(Message msg){ switch (msg.what) { case 2: Log.e("threadmsg",""+msg.what); Message message = handler.obtainMessage(); message.what = 2; message.sendToTarget(); myLooper.quitSafely(); break; default: super.handleMessage(msg); break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView)findViewById(R.id.text); button = (Button)findViewById(R.id.change); Tbutton = (Button)findViewById(R.id.change2); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Message message = new Message(); message.what = 1; handler.sendMessage(message); } }); Tbutton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Looper.prepare(); MyHandler myHandler = new MyHandler(Looper.myLooper()); Message msg = new Message(); msg.what = 2; myHandler.sendMessage(msg); Looper.loop(); } }).start(); } }); } }
感谢任玉刚的开发探索与艺术,以及鸿样大神的启发。
相关文章推荐
- 关于纠错机制的一点总结
- Android Handler消息机制原理及总结
- 异步消息处理机制总结(AsynTask和handler)
- 学习总结——android的消息机制handler
- Android 异步消息处理机制 Looper、Handler、Message三者关系总结
- 关于消息机制handler message looper的学习总结
- Android中的消息机制(MessageQueue, Looper, Handler)总结
- 关于 Android 异步消息处理机制 Handler
- Handler消息传送机制总结
- Android消息异步机制(ThreadLocal、MessageQueue、Looper、Handler关系总结)
- Android消息异步机制(ThreadLocal、MessageQueue、Looper、Handler关系总结)
- Handler异步消息处理机制 总结
- Handler(消息机制)的常用场景总结
- 【Android自助餐】Handler消息机制完全解析(五)鸟瞰与总结
- 个人总结android消息处理机制:Looper,Handler,Message
- 关于Handler消息机制的理解
- 关于悬浮框的延迟消失,采用handler的消息机制
- 消息传递机制之Handler使用总结
- 关于用CSplitterWnd分割View的一点总结
- 关于DNN Module开发学习以来的一点总结