Android的Handler机制

来源:互联网 发布:windows凭据无法保存 编辑:程序博客网 时间:2024/05/22 10:58

   相信刚接触Android开发的朋友总会有一段时间被关于Handler的异常搞得焦头烂额,本篇博客就说说Handler的那些事,从系统原理和实际开发解读,相信对你会有很大助益。

         HandlerAndroid消息机制的上层接口,所以开发过程中只需要和Handler交互即可。很多人认为Handler的作用是更新UI,但事实上更新UI不过是Handler的一个使用场景罢了。

   在Android中主线程要处理界面界面交互相关的逻辑,因此要保证较高的响应速度,一些耗时的操作,比如读取文件或者访问网络,Android要求放到子线程中,当耗时操作完成后可能做一些UI上的改变,但是Android限制不能在子线程访问UI控件,否则会触发异常,这个时候Handler就可以将更新UI的操作切换到主线程来执行。系统不允许在子线程更新UI的原因是AndroidUI控件不是线程安全的,如果在不同线程并发访问UI控件可能导致不可预知的状况。当然有人会考虑用加锁来替代,但加锁一会使UI访问的逻辑更加负责,二来加锁会阻塞某些线程的执行。相比还是Hander更简单高效一点。

   Handler的运行需要底层MessageQueueLooper的支撑。MessageQueue翻译过来是消息队列,但他的内部是采用单链表来存储消息列表的。MessageQueue只是一个消息存储单元,它不能去处理消息。这时就用到Looper了,Looper会以无限循环的形式去查找MessageQueue中是否有新消息,如果有的话就交给Handler处理这个消息,否则就等待着。

         Handler在创建的时候会采用当前线程的Looper来构造循环消息系统,这可以参见下面Handler的构造函数,但Handler是如何获得这个Looper的呢?这就要用到ThreadLocal了,但他并不是线程,一个ThreadLocal实例可以在不同的线程互不干扰的存储并提供数据副本。所以通过ThreadLocal就可以轻松地获取每个线程的Looper。主线程,也就是UI线程,被创建时就会默认初始化Looper,这也是在主线程默认可以使用Handler的原因。其他的线程如果需要创建Handler就必须先为线程创建Looper

   首先定义一个ThreadLocal对象,选择Boolean类型的,如下所示:

private ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<Boolean>();

接着本别在主线程,线程1和线程2中访问他的值。

 

运行结果如下:

 

 

   从日志中可以看出虽然不同线程访问的是同一ThreadLocal对象,但他们获取到的值却不一样,这让我觉得其中的设计一定有绝妙之处。分析过他的源码就可以明白了:

ThreadLocal是一个泛型类,他的定义为pubilc class ThreadLocal<T>,下面是他的set方法:

public void set(T value){

    Thread currentThread = Thread.currentThread();

    Value value = values(currentThread);

    If(values == null){

        Values = initializeValues(currentThread);

     }

     Values.put(this,value);

}

         ThreadLocal.Values localValuesThread类中专门存储ThreadLocal的数据的成员,上面的代码中先判断localValues是否为null,如果是null就先初始化然后存储ThreadLocal的值。get方法同理不再列出。可见数据的写入是和当前线程一对一锁定在一起的,这种代码的思想这的让本人学习了。

下面继续说回LooperLooper的构造方法中会创建一个MessageQueue对象,并且把当前线程保存起来:

private Looper(boolean quitAllowed){

    mQueue = new MessageQueue(quitAllowed);

    mThread = Thread.currentThread();

}

   而在loop方法中当MessageQueuenext方法返回一条消息就会调用msg.target.dispatchMessage(msg);这里的mag.target就是发送这条消息的Handler,而HandlerdiapatchMessage方法是在创建Handler时所使用的Looper(一般是主线程默认生成的Looper)中执行的,这样就成功的在主线程拉取数据并更新UI了。

   下面是Handler的默认构造方法:

public Handler (Callback callback,boolean async){

    ......

    mLooper = Looper.myLoop();

    If(myLoop== null){

         Throw new RuntimeExcption(“Can’t create handler inside thread that has not called Looper.prepare()”);

     }

    .......

}

   看到这里大概就明白为什么在没有Looper的线程中创建Handler会引发程序异常了。

   总结一下,整个过程就是主线程创建了Handler mHandler,当子线程利用mHandler发送一条Message类型的消息时该消息会被插入主线程Looper的MessageQueue,主线程的Looper不断查询MessageQueue是否插入新消息,如果有就交给mHandler处理。这种机制常常被用来通过更新UI。

 

0 0
原创粉丝点击