Android Handler详解

 本文来源:http://blog.csdn.net/xiangzhihong8/article/details/51722721

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的一种方法:


    class LooperThread extends Thread {  
       public Handler mHandler;  
            public void run() {  
                Looper.prepare();  
                mHandler = new Handler() {  
                    public void handleMessage(Message msg) {  
                        // process incoming messages here  
                    }  
                };  
                Looper.loop();  
            }  


注意这里,官方的例子是和Looper关联了,那么如果我们不关联Looper的话会怎么样呢
     mRunnable = new  Runnable(){  
               @Override  
               public void run() {  
                  // TODO Auto-generated method stub      
                  String s = mHandler.getLooper().getThread().getName();  
                  Log.e("handler", s);  
                  mHandler.postDelayed(mRunnable, 1000);  
               }};  
    Thread t  = new Thread(mRunnable,"thread");  
            t.start();  


运行结果:
ERROR/handler(1630): main,可以发现我们创建了一个名为“thread”的线程,病通过handler发送消息,我们创建的handler自动和UI线程的Looper关联上了。

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

    mRunnable = new  Runnable(){  
                  @Override  
                  public void run() {  
                      // TODO Auto-generated method stub  
                      Looper.prepare();  
                      mHandler = new Handler(Looper.myLooper());  
                      String s = mHandler.getLooper().getThread().getName();  
                      Log.e("handler", s);  
                      mHandler = new Handler(Looper.getMainLooper());  
                       s = mHandler.getLooper().getThread().getName();  
                      Log.e("handler", s);          
                  }};  
        Thread t  = new Thread(mRunnable,"subthread");  
                t.start();  


运行结果:

 
ERROR/handler(4049):subthread

ERROR/handler(4049): main

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

    handlerThread=new HandlerThread("handlerThead");  
           handlerThread.start();  
           handler=new Handler(handlerThread.getLooper());  
           handler.post(new Runnable() {  
               @Override  
               public void run() {  
                   String s=handler.getLooper().getThread().getName();  
                   Log.d("test",s);  
               }  
           });  

注: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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值