美文网首页
Android Handler消息机制原理及总结

Android Handler消息机制原理及总结

作者: Darren的徒弟 | 来源:发表于2019-03-26 10:48 被阅读0次

    Android中的消息机制主要就是指Handler的消息机制,Handler相信大家已经非常熟悉了,它可以将一个任务切换到Handler所在的线程中去执行,开发中,当我们在子线程做了一些操作后需要更新UI,由于Android不允许在子线程中访问UI控件,所以我们一般都会使用handler来实现。

    Handler的机制需要MessageQueue、Looper和Message的支持。他们在消息机制中各扮演了不同的角色

    Handler:负责消息的发送和接收处理
    MessageQueue:消息队列,一个消息存储单位,经常需要进行增减,内部使用的是单链表的结构
    Looper:消息循环。会不停地从MessageQueue中取消息,如果有新消息就会立刻处理,否则就一直阻塞在那里
    Message:消息载体

    下面通过一段简单的代码来分析整个执行过程。

     private TextView textView;
     private Handler handler = new Handler(){
         @Override
         public void handleMessage(Message msg) {
              super.handleMessage(msg);
              textView.setText("消息处理.....");
         }
     };
    @Override
    protected void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.mytv);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //模拟耗时操作
                    SystemClock.sleep(3000);
                    handler.sendMessage(new Message());
                }
            }).start();
    
        }
    
    

    就是在子线程中做了一些耗时操作后,通过Handler发送消息去更新UI

    Hanlder是怎么接受到消息的呢?

    Looper

    Looper几个主要的方法

    • Looper.prepare():为当前线程创建一个Looper
    • Looper.loop():开启消息循环
    • Looper.getMainLoop():可以在任何地方获取到主线程的
    • Looper.quit():直接退出Looper
    • quitSafely():设置一个标记,把消息队列的所有消息处理完后才会退出
    //创建一个新的Looper并放到当前线程的ThreadLocal中去
     private static void prepare(boolean quitAllowed){
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    
    

    再看看Looper的构造方法,发现,Looper中保存有一个MessageQueue

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

    loop()该方法是一个死循环,会不断从MessageQueue中去取消息,当获取到消息后,将交由Handler去处理

    public static void loop() {
        //myLooper方法会从ThreadLocal中获取到当前线程的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        } 
        final MessageQueue queue = me.mQueue;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        for (;;) {
            //不断的去MessageQueue中取消息
            Message msg = queue.next();
            if (msg == null) {
                return;
            }    
           Printer logging = me.mLogging; 
           if (logging != null) {
              logging.println(">>>>> Dispatching to " + msg.target + " " +                msg.callback + ": " + msg.what);    } 
          //调用发送该消息的Handler的dispatchMessage方法处理该消息
          //这里的msg.target == Handler,是在Handler发送消息的时候进行赋值的
           msg.target.dispatchMessage(msg); 
           if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);    } 
           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.recycleUnchecked();
    }
    
    

    Looper大概就是这么一回事,每条线程绑定一个Looper(子线程需要自己调用Loop.prepare()),保证每条线程都只有一个Looper实例
    随后调用Looper.loop开启消息循环,进入堵塞状态,等待消息的到来

    MessageQueue

    相对来说就很简单来,就两个操作,插入和读取。可以看成一个消息容器

    Handler

    先来看看他的构造方法

    ```java
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) { 
               Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                klass.getCanonicalName());        }   
     } 
      //获取当前线程中的Looper
       mLooper = Looper.myLooper();
        if (mLooper == null) { 
        //如果当前线程没有Looper就会抛出异常。
           throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()"); 
       } 
       //获取Looper中的MessageQueue,可以看出,Looper中为每个线程维护了一个Message
       mQueue = mLooper.mQueue; 
       mCallback = callback;
       mAsynchronous = async;
    }
    
    

    接来下看看Handler发送消息的方法,追踪senndmessage到最底层的方法就是enqueueMessage。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    

    主要做了两个操作
    msg.target = this;将发送的Message的target指向当前Handler。
    ueue.enqueueMessage(msg, uptimeMillis);往当前线程的MessageQueue插入一条消息

    前面在Looper.looper()方法中我们看到msg.target.dispatchMessage(msg); 获取到消息后,调用Handler的dispatchMessage方法进行消息处理。

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);//这里是Message的Callback,也就是开启我们在postDelayed传入的Runnable
        } else {
          //mCallback:构造器传入的实现
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                       return;
                }
            }
          //handler自己的handler,一般由派生之类来实现
            handleMessage(msg);
        }
    }
    
    

    可以看到Handler最终将调用handlerMessage处理消息,当然这里的handlerMessage处理是有优先级的

    下面我们总结下流程
    流程总结:
    1,首先调用Looper.prepare()方法,会创建一个Looper实例,该实例包含一个MessageQueue,并将该实例保存在当前线程中Threadlocal
    2,调用Looper.loop()开始消息循环,不断地向MessageQueue中读取消息,并调用msg.target.dispatchMessage(msg);来处理消息
    3,构建Handler的时候,会先获取到当前Handler所在线程的Looper并得到其中的 MessageQueue
    4,使用Handler发送消息的时候,会将一个Message到保存当前线程Looper中的MessageQueue
    5,当Looper.loop()获取到消息的时候,调用msg.target.dispatchMessage(msg)来处理消息,其实Message.target = handler。也就是调用Handler的dispatchMessage来处理
    6,Handler的dispatchMessage最终回去调用handlerMessage方法。到这里就知道,其实Handler的handler在哪条线程执行,取决于构建Handler时所使用的是哪条线程保存的Looper,因为handlerMessage其实是Looper去调用的。

    下面通过一张图来表述下具体流程

    image

    前面说到,Handler必须有Looper才能执行,或者会就抛出异常,当前我们在日常使用中,直接在Activity中定义Handler就可以用啦,并没有说调用Looper.prepare来进行初始化操作。

    其实UI线程默认已经做了这些操作了。对于我们来说是隐形的

    大家都知道Android的程序入口是ActivityThread类,他有个main方法,我们看看他做了什么

     public static void main(String[] args) {
            //省略。。。。
    
            Looper.prepareMainLooper();
    
            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();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    
    

    可以看到main方法主要就是消息循环,当main方法结束了,你的程序也就退出了

    可能有人会问?UI线程已经做了Looper.loop()了?loop不是一个死循环堵塞状态吗?为什么我们程序还能跑起来。

    既然main方法只是做了消息循环,那么我们Activity的生命周期的方法是怎么执行的呢?
    在ActivityThread中有个Handler,Activity的生命周期方法在里面均有case,也就说是我们的代码其实就是在这个循环里面去执行的,自然不会阻塞了

    当前我还有个疑惑。程序是在哪给ActivityThread的Handler发消息的呢?这个我暂时还不清楚。。还得继续学习啊。

    UI线程已经帮我们做了这些工作了,但是如果我们自己new Thread的话,需要在子线程中使用Handler的话,还是要自己来实现的。
    对此,Android也为我们提供了一个HandlerThread类,方便我们快速的实现需求。
    HandlerThread继承了Thread

    public class HandlerThread extends Thread
    
    

    代码相当简单,
    看看run方法

     @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    
    

    如果对前面的Handler机制理解了话,这里是不是一目了然了呢?

    作者:车简
    链接:https://www.jianshu.com/p/7f2fcb43f8d9
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    相关文章

      网友评论

          本文标题:Android Handler消息机制原理及总结

          本文链接:https://www.haomeiwen.com/subject/vszwvqtx.html