android 开发Handler源码剖析

Android的消息机制主要是Handler的运行机制,而讲Handler的机制,又需要和MessageQueue和Looper结合。MessageQueue中文意思是消息队列,虽说叫队列,但是其内部结构并不是队列组成的,而是采用了单链表的形式来存储消息。MessageQueue只是负责存储消息,并不处理消息(这里指消息的轮训),Looper刚好弥补了这个空缺。我在知道,Handler创建的时候,会默认为我们创建一个Looper对象,那么如何获取当前的Looper呢,这里就使用到了一个TheadLocal的概念,TheadLocal可以轻松的获取当前使用的Looper。

Handler的使用:

Handler 主要有两种用法:( 1 )调度消息和 runnable 对象,并在某一时间点执行。( 2 )依次存放分属不同线程的行为。

线程在默认的时候是没有Looper与之相关联。在线程中,我们可以通过调用prepare 方法来启动一个消息loop,调用loop方法来通知Looper来处理消息,直至结束。Looper提供了很多的静态方法来与线程、消息队列进行交互。一个线程最多允许创建一个Looper

以下是SDK文档中介绍的在线程中使用handler的一种方法:

  1. class LooperThread extends Thread {
  2. public Handler mHandler;
  3. public void run() {
  4. Looper.prepare();
  5. mHandler = new Handler() {
  6. public void handleMessage(Message msg) {
  7. // process incoming messages here
  8. }
  9. };
  10. Looper.loop();
  11. }

注意这里,官方的例子是和Looper关联了,那么如果我们不关联Looper的话会怎么样呢
  1. mRunnable = new Runnable(){
  2. @Override
  3. public void run() {
  4. // TODO Auto-generated method stub
  5. String s = mHandler.getLooper().getThread().getName();
  6. Log.e("handler", s);
  7. mHandler.postDelayed(mRunnable, 1000);
  8. }};
  9. Thread t = new Thread(mRunnable,"thread");
  10. t.start();
运行结果:
ERROR/handler(1630): main,可以发现我们创建了一个名为“thread”的线程,病通过handler发送消息,我们创建的handler自动和UI线程的Looper关联上了。

那么,可不可以在主线程中定义handler,在代码中动态改变handler使之与其他线程的looper相关联呢?是可以的。

  1. mRunnable = new Runnable(){
  2. @Override
  3. public void run() {
  4. // TODO Auto-generated method stub
  5. Looper.prepare();
  6. mHandler = new Handler(Looper.myLooper());
  7. String s = mHandler.getLooper().getThread().getName();
  8. Log.e("handler", s);
  9. mHandler = new Handler(Looper.getMainLooper());
  10. s = mHandler.getLooper().getThread().getName();
  11. Log.e("handler", s);
  12. }};
  13. Thread t = new Thread(mRunnable,"subthread");
  14. t.start();
运行结果:

ERROR/handler(4049):subthread

ERROR/handler(4049): main

不过我们Android为我们提供了一个包含Looper的Thead类,HandlerThread  HandlerThread自带了一个Lopper,

扫描二维码关注公众号,回复: 1901672 查看本文章
  1. handlerThread=new HandlerThread("handlerThead");
  2. handlerThread.start();
  3. handler=new Handler(handlerThread.getLooper());
  4. handler.post(new Runnable() {
  5. @Override
  6. public void run() {
  7. String s=handler.getLooper().getThread().getName();
  8. Log.d("test",s);
  9. }
  10. });

注:android认为在非线程中操作UI界面是不安全的,因此禁止在其它线程中修改UI界面。解决的方法有两种,一是通过在主线程中定义的handler更新界面,二是直接调用被修改的viewpostInvalidate方法刷新单个view

其实上面的描述是不准确的,实际上在activity第一次执行的时候,是可以在子线程直接更新主线程数据的,条件是不能执行Thread.sleep(),

原因是什么呢?

1.当刚启动Activity即onCreate里面此时onResume方法还没有执行的时候可以,因为在线程中更新UI时会调用ViewParent.invalidateChild()方法检查当前的Thread是否是UIThread,若为UIThread则可以更新(ViewParent是一个接口类,其实现是ViewRootImpl;invalidateChild()方法调用checkThread()方法来检查线程是否为主线程)。ViewRootImp是在onResume方法中初始化的,所以只要在ViewRootImpl创建之前更新UI(在onCreate方法中创建线程并执行,此时还没有初始化ViewRootImp),就可以逃避掉checkThread()的检查进而更新UI。
2.-->刚启动的时候,立即在非UI线程更新->不报错(onResume还没有执行)
--->休眠2s钟以后,更新——————>报错

最后贴上一张图


分析完了Android消息机制的流程,那么我们接下来分别理解一些重要的概念。主要从以下几个方面增强理解,Handler,MessageQueue,Looper和Threadloacal.

猜你喜欢

转载自blog.csdn.net/qq_15543985/article/details/80926712
今日推荐