详解Handler机制和模拟Hangler

很多android开发者都赞叹Handler的方便和易用,这几天花了点时间仔细研究下,谈谈Handler的机制,刚开始写博客,写得不好还望大家多多指教。分4部分写吧:Handler的创建,Handler获得消息,发送消息,和模拟handler。


1.new Handler()

我们来看下Handler的构造方法:

public Handler(Callback callback, boolean async) {
    //省略一些代码
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

来看下这行代码:

mLooper = Looper.myLooper();

此处先去看此线程有无创建过Looper(由ThreadLocal获去处理,ThreadLocal是一个线程内部的数据存储类,每个线程可通过ThreadLocal存储数据,数据存储之后,只有在相应线程中才可以获取到存储的数据,这就保证了一个线程只能创建一个唯一的Looper对象。如果没有创建过就会创建一个Looper),如果创建过,就直接拿出来用,没有就创建一个,包括我们在子线程创建Handler时要先Looper.prepare()也是为了先获得Looper对象,但为什么在主线程不需要呢?因为在ActivityThread(Activity应用入口)时会先调用Looper.prepareMainLooper(),为主线创建Loope对象。

再看下Looper的构造函数:

private  Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

发现它是私有的,并且在创建的时候保存了当前线程以及创建了一个消息队列MessageQueue。

所以综上我们可以看到,在一个线程里Handler,Looper,MessageQueue的数量比=n:1:1.即可以有多个Handler,但只能有一个Looper和MessageQueue。

 

ThreadLocal的举例:

ThreadLocal<Integer> mIntegerThreadLocal=new ThreadLocal<>();
mIntegerThreadLocal.set(10);//主线程设置为10
Log.d(TAG,"[Thread#main]mIntegerThreadLocal="+mIntegerThreadLocal.get());//主线程打印

new Thread("ThreadA"){
           @Override
          public void run() {
                Log.d(TAG,"[Thread#ThreadA]mIntegerThreadLocal="+mIntegerThreadLocal.get());//子线程打印
                mIntegerThreadLocal.set(100);//在子线程改为100
            }
        }.start();

        new Thread("ThreadB"){
            @Override
            public void run() {
                    try {
                        Thread.sleep(1000);//稍稍延时一下,让前面线程先执行完
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    mIntegerThreadLocal.set(1000);//在子线程改为1000
                    Log.d(TAG,"[Thread#ThreadB]mIntegerThreadLocal="+mIntegerThreadLocal.get());
                }
        }.start();

        //最后再在主线程访问一遍
        Thread.sleep(2000);
        Log.d(TAG,"[Thread#main]mIntegerThreadLocal="+mIntegerThreadLocal.get());
打印结果:

D/ThreadLocalTest:[Thread#main]mIntegerThreadLocal=10

D/ThreadLocalTest:[Thread#ThreadA]mIntegerThreadLocal=null

D/ThreadLocalTest:[Thread#ThreadB]mIntegerThreadLocal=1000

D/ThreadLocalTest:[Thread#main]mIntegerThreadLocal=10

从上面日志可以看出,虽然在不同线程中访问的是同一个ThreadLocal对象,但是他们通过ThreadLocal获取到的值却是不一样的,这就是ThreadLocal的奇妙之处,更具体的大家可以去看看ThreadLocal内部set和get方法,以及类内部维护的ThreadLocal.Values对象,对Looper创建的唯一性会有更好的了解。


2.通过Handler获取消息:

Handler.obtainMessage()方法,实际上调用的是Message.obtain()方法,该方法会从Message的消息池里获得Message对象,如果消息池有可用的消息对象,就直接拿过来使用,没有的话就会新建一个。改消息池初始化为0,最大数量是50。使用消息池的好处:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。这样也就推荐大家在使用handler发送消息时,尽量用handler.obtainMessage()方法获得消息对象,而不是new 一个Message对象。

public static Message obtain() {
       synchronized (sPoolSync) {
           if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-useflag
               sPoolSize--;
                return m;
           }
       }
       return new Message();
  }

3.Handler发送消息.

       这部分是核心。Handler发送消息实际上是把消息发送到该Handler对应的Looper所管理的MessageQueue,这里没啥可说,重要的是如何把信息又发回给Handler。这里起作用的就是Looper.loop()方法了。我们都知道,在子线程中,我们除了要Looper.prepare()之外,还要再调用Looper.loop()这个方法.在主线程中不需要,也是因为ActivityThread入口main方法帮我们调用了Looper.loop()这个方法:

public static void main(String[] args) {  
  	//此处省略了一些代码
        Looper.prepareMainLooper();//为主线程创建Looper对象
        // 创建ActivityThread实例  
        ActivityThread thread = new ActivityThread();  
        thread.attach(false);  
  
        if (sMainThreadHandler == null) {  
            sMainThreadHandler = thread.getHandler();  
        }  
  
        AsyncTask.init();  
  
        if (false) {  
            Looper.myLooper().setMessageLogging(new  
                    LogPrinter(Log.DEBUG, "ActivityThread"));  
        }  
  
        Looper.loop();//开启循环
}

那个loop()方法究竟有什么作用呢?我们看一下它的源码:

public static void loop() {  
        final Looper me = myLooper();  
		//所以在子线程中必须先prepare,再loop
        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;  //这里Looper对象会一直在消息队列里查看是否有消息
            }  
  
            // 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);//如果有消息,会被分发到对应的Handler,target就代表要被接收此消息的handler
  
            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();  
        }  
}  

简单来说,就是在loop方法里,Looper对象会一直在消息队列里查看是否有消息,有就分发消息,没有就继续循环,所以如果在子线程中没有调用Looper.loop()方法,handler是没有办法处理消息的。

4.总结:

       Handler能在线程之间通讯,很重要的也是依赖于MessageQueue这个容器,不同的线程从容器存取东西都会影响其他线程对容器的访问结果。

       自己画了一张简易的流程图:

最后简单地模拟Handler对象在线程间的通讯:

private ArrayList<Integer> arrayList=new ArrayList<>();
private TextView textView=(TextView) findViewById(R.id.handler_textView);
new ThreadA().run();
        try {
            Thread.sleep(500);
            	textView.setText("获得的数字是:"+arrayList.get(0));//子线程加进去的10主线程这里就可以拿到了
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
private class ThreadA implements  Runnable{
        @Override
        public void run() {
            arrayList.add(10);
        }
    }

觉得写得好,点个赞吧~写得不好,还望大家多多指出









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值