详解Handler机制和模拟Hangler

来源:互联网 发布:郑州学历网络教育 编辑:程序博客网 时间:2024/06/07 05:22

很多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);//主线程设置为10Log.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