美文网首页
Android组件_Handler Looper Message

Android组件_Handler Looper Message

作者: 马小藤 | 来源:发表于2018-11-02 14:36 被阅读14次

    Android组件_Handler Looper Message理解

    一、Handler机制概述

    Handler机制是Android中一种消息处理机制。
    主要组成或重要概念:

    1. Message,线程间通讯的数据单元。
    2. Message Queue,消息队列,用来存放Handler发布的消息,按照FIFO执行。
    3. Handler是Message的主要处理者,负责将Message添加到消息队列意见对消息队列中的Message进行处理。
    4. Looper循环器,循环去除Message Queue里面的Message,并交付给相应的Handler进行处理。
    5. Thread UI thread通常就是main thread,Android启动程序时会替它建立一个Message Queue。
      每一个线程里可以含有一个Looper对象以及MessageQueue数据结构。
    6. ThreadLocal 他的作用是帮助Handler获得当前线程的Looper(多个线程可能有多个Looper)

    二、使用场景

    1. 我们常常用Handler来更新UI,但是不是说Handler就是把用来更新UI的,耗时的I/O操作,读取文件,访问网络等等都是可以在Handler里面操作的。

    2. 子线程间通讯,可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

    3. 延时任务等

    三、使用方法

    3.1. 子线程线程里更新UI

    public class MainActivity extends AppCompatActivity {
        //主线程中的handler
        Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //获得刚才发送的Message对象,然后在这里进行UI操作
                Log.e(TAG,"------------> msg.what = " + msg.what);
                ...
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //在子线程中发送更新消息给主线程的handler去更新UI
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message m = mHandler.obtainMessage();
                    ...
                    mHandler.sendMessage(m);
                }
            }).start();
        }
    }
    

    3.2 子线程间通信

            final Handler[] h = new Handler[1];
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();//创建子线程的looper,子线程使用handler消息传递,这一步必须要有,因为默认的子线程是没有Looper对象的
                    h[0] = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e(TAG,"------------> msg.what = " + msg.what);
                        }
                    };
                    Looper.loop();//消息池消息循环处理
                }
            }, "work1").start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message msg = h[0].obtainMessage();
                    //msg.sendToTarget();
                    h[0].sendEmptyMessage(0);//在work2子线程中使用持有work1子线程looper的handler发送消息至work1中去做消息处理
                }
            }, "work2").start();
    

    3.3 HandlerThread使用

        {
            MyHandlerThread mHandlerThread = new MyHandlerThread("work");
            Handler mHandler = new Handler(mHandlerThread.getLooper()){//将该mHandler与HandlerThread对象的Looper关联起来
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
        }
    
        MyHandlerThread mHandlerThread = new MyHandlerThread("work");
            Handler mHandler = new Handler(mHandlerThread.getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
        }
    

    3.4 Handler CallBack参数使用
    在构造Handler对象的时候可以穿入CallBack参数,如下

    //主线程:
      Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
    //其他线程中:
    handler.sendMessageXXX(msg);
    

    3.5 Handler post方法使用

        //主线程中创建mPostHandler
        Handler mPostHandler = new Handler();
        //子线程中使用post方法
         new Thread(new Runnable() {
                @Override
                public void run() {
                   /* Message m = mHandler.obtainMessage();
                    mHandler.sendMessage(m);*/
                    mPostHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            //更新UI ,注意这里虽然操作写在子线程中,事实运行时是在主线程中,原因分析在4.2.5中
                        }
                    });
                }
            }).start();
    

    3.6 Handler 延时任务/循环定时操作

    延时任务
    new Handler().postDelayed(new Runnable(){   
                public void run() {  
                       //show dialog
                }  
      }, 5000);  //延时5秒后执行runnable run方法
    
    循环定时操作:
    1,首先创建一个Handler对象  
     Handler handler=new Handler();  
    
    2,然后创建一个Runnable对
    Runnable runnable=new Runnable(){  
       @Override  
       public void run() {  
        // TODO Auto-generated method stub  
        //要做的事情,这里再次调用此Runnable对象,以实现每两秒实现一次的定时器操作  
        handler.postDelayed(this, 2000);  
       }  
    };  
    
    3,使用PostDelayed方法,两秒后调用此Runnable对象  
    handler.postDelayed(runnable, 2000);  
    
    4,如果想要关闭此定时器,可以这样操作
    handler.removeCallbacks(runnable); 
    

    四、原理分析

    Handler机制的铁三角-Handler、Looper和MessageQueue,另外还有下面进行浅入分析。
    整体架构 :

    image.png

    4.1 主线程Handler对象与主线程Looper对象关联

    对3.1中的例子进行浅入的分析。

    4.1.1 Handler对象初始化获取主线程的Looper对象

    1.主线程Handler对象初始化:

    Handler mHandler = new Handler(){...}
    

    默认的构造函数会将该Handler对象与当前线程的Looper对象关联。下面我们看下构造函数是如何关联上当前线程的looper的。
    Handler的默认构造函数:

    frameworks/base/core/java/android/os/Handler.java
        public Handler() {
            this(callback:null, async:false);
        }
    

    2.如果该线程没有looper对象,该handler将抛异常:

        public Handler(Callback callback, boolean async) {
            ...
            mLooper = Looper.myLooper();//
            if (mLooper == null) {//如果当前线程没有Looper对象将抛出异常
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    3.sThreadLocal对象中保存着当前线程的looper对象,Looper.myLooper()即用来获取当前线程的Looper对象:

    frameworks/base/core/java/android/os/Looper.java
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();//这里获取当前线程的Looper对象
        }
    

    4.1.2 主线程初始化时创建Looper对象及MessageQueue对象

    1.对于UI线程即我们所说的Main线程及ActivityThread,在ActivityThread创建的时候,main函数中创建了Looper对象,并通过Looper.loop()是主线程进入消息循环中:

    frameworks/base/core/java/android/app/ActivityThread.java
        public static void main(String[] args) {
            ...
            Looper.prepareMainLooper(); //创建主线程Looper对象
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            ...
            Looper.loop();//不断循环处理MessageQueue中的消息
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    
    

    2.Looper.prepareMainLooper()创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环:

    frameworks/base/core/java/android/os/Looper.java
        public static void prepareMainLooper() {
            prepare(false);//创建Looper对象,并且将新建的looper对象与当前线程关联
            synchronized (Looper.class) {
                if (sMainLooper != null) { //可以看出主线程应该只有一个Looper对象
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();//返回当前线程的Looper对象
            }
        }
    

    3.Looper.prepare()函数为当前线程(此处为主线程,也可以是其他线程)创建Looper对象,并将该对象设置至线程的sThreadLocal变量中:

      private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) { // 说明prepare不能调用两次,否则会抛出异常,保证一个线程只有一个Looper实例
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。
        }
    

    4.Looper.myLooper()返回当前线程的Looper对象,这里也就验证了myLooper()的确是从sThreadLocal变量中获取了当前线程的Looper对象。

        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    概述一下就是,主线程在初始化时就生成了含有一个不可退出的MessageQueue的Looper对象,并将该Looper对象保存在主线程的sThreadLocal变量中;
    当在主线程中创建Handler对象时,会将该Handler对象与当前线程的sThreadLocal变量中保存的主线程Looper对象关联起来。
    因此,在上面3.1的例子中,主线程new一个Handler对象时会将该Handler对象与主线程中的Looper对象关联,执行Looper.loop()不断循环处理消息;

    4.2 Handler、Looper、Message及HandlerThread浅析

    4.2.1 Handler

    1.可以使用的方法

    post(Runnable)
    postAtTime(Runnable,long)
    postDelayed(Runnable long)
    sendEmptyMessage(int)
    sendMessage(Message)
    sendMessageDelayed(Message,long)
    sendMessageAtTime(Message,long) 
    

    2.以上方法都最终都将调用sendMessageAtTime(),将消息加入msg的目标Handler对象关联的Looper持有的消息队列中,之后就是排队消息队列循环处理消息了:

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis); //将该消息加入该消息的目标handler关联的looper中的消息队列中
        }
    

    4.2.2 Looper:

    1.Looper构造函数,看到Looper的构造函数里新建了一个MessageQueue对象:

        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);//创建消息队列
            mThread = Thread.currentThread();
        }
    

    2.Looper.loop(),循环处理消息队列中消息的函数:

        /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        public static void loop() {
            ...
            // 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,若无消息则会阻塞在这里。
                ...
                try {
                    msg.target.dispatchMessage(msg); //调用发该message的handler的dispatchMessage方法;
                } finally {
                }
                ...
                msg.recycleUnchecked();//处理完的msg可以进行回收,实现的地方清除了该msg对象的内容,并将其保留在消息池中,以供循环使用            
            }
        }
    

    3.在dispatchMessage中进行消息处理,post方法中的runnable就是这里的msg.callback

    frameworks/base/core/java/android/os/Handler.java
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg); //执行post方法中的runnable方法
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) { //如果创建handler时有传入callback的话
                        return;
                    }
                }
                handleMessage(msg);//创建handler时需复写方法handleMessage(),处理消息
            }
        }
    

    3.1首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下:

    private static void handleCallback(Message message) {
      message.callback.run();
    }
    

    这样我们我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。
    3.2.如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。
    3.3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。
    综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。

    4.2.3 MessageQueue

    1 MessageQueue的next()方法
    在消息队列中不断取出消息,next方法是个无限循环的方法,如果有消息返回这条消息并从链表中移除,而没有消息则一直阻塞在这里。

    Message next() {
            for (;;) {
                synchronized (this) {
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        ...
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            msg.markInUse();
                            return msg;
                        }
                    } 
            }
        }
    

    4.2.4 HandlerThread

    1.自带Looper的Thread:

    frameworks/base/core/java/android/os/HandlerThread.java
    public class HandlerThread extends Thread {
    @Override
        public void run() {
            ...
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            onLooperPrepared();
            Looper.loop();
            ...
        }
    }
    

    我们看到HandlerThread继承自Thread,并且在run函数中使用Looper.prepare()为使用线程创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal);在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量;loop()开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
    因此也可以使用HandlerThread,这样可以不用像普通线程那样需要Looper.prepare()和Looper.loop(),因为HandlerThread为我们做好了准备工作;

    2.另外可以通过HandlerThread的getLooper方法获的该HandlerThread的Looper对象:

        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            
            // If the thread has been started, wait until the looper has been created.
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    

    handleMessage

    4.2.5 Handler中post方法

    3.5中的例子中子线程中post中的run方法运行在主线程中,原因分析如下:
    1.传入Runnable对象

    public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    

    2.这里将Runnable封装到一个Message对象中

      private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r; //将runnable传入message对象中的callback变量中
            return m;
        }
    

    3.之后与普通的handler sendMessage方法流程一致,调用用sendMessageAtTime,将该消息加入该消息的目标handler,及mPostHandler关联的looper中的消息队列中,如何处理在4.2.1 中有提到过。

      public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    

    也就是说,虽然runnable复写方法在子线程中,但是同样是封装成message对象传入主线程的looper持有的MessageQueue对象中,在主线程中对封装了runnable变量的message进行消息执行处理,因此post中的run方法是运行在主线程中的。

    五、注意事项

    1. Handler在处理消息需要严格区分是否是在UI线程中,Handler一般用在非UI线程中来传递消息,在非UI线程中使用Handler来发送消息,消息
      处理会被严格执行,但如果在UI线程中使用Handler来发送消息,相同的消息在内部会被合并,且执行时序也得不到保证
    2. 因为HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程,比较合适处理那些需要花费时间偏长的任务。我们只需要把任务发送给HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。
    3. android中handler使用应该注意的由handler引起的OOM内存泄漏) http://blog.csdn.net/javazejian/article/details/50839443
    4. 在普通线程中使用机制时要记得先Looper.prepare(); 最后还要Looper.loop();

    相关文章

      网友评论

          本文标题:Android组件_Handler Looper Message

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