Android---Handler体系

Posted xiaoqiang_0719

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android---Handler体系相关的知识,希望对你有一定的参考价值。

前言

作为一个android程序员Handler机制使用的十分频繁,基本使用方法我不会再去累赘,本篇文章意在阐述自己在使用过程中遇到的问题以及阅读源码后得到的收获。
首先可以先思考下面这几个问题(文末我附了自己的见解)

  • 子线程有哪些更新UI线程的方法?
  • Activity的runOnUiThread(Runnable action) 是如何实现在子线程中更新UI的?
  • Handler导致的内存泄露问题?

源码分析

从使用流程进行源码分析,使用Handler一般就是以下几个过程(子线程更新UI线程)

1、在主线程新建Handler并重写hanleMessage方法
2、创建子线程,在子线程中创建Message对象
3、使用Handler的sendMessage方法发送消息
4、从MessageQueue中取出消息来更新ui

非常简单就能实现子线程更新UI线程的功能,下面我们从源码中分析一下是如何实现的

  • 首先创建Handler对象
    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());
            
        
       // 1. 指定Looper对象,myLooper方法返回的是当前线程的Looper对象,也就是主线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) 
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        
        mQueue = mLooper.mQueue;//2. 获取主线程的MessageQueue
        mCallback = callback;    //3.  将handle的callback复制给mCallback,这里注意最后dispatchMessage的时候就是调
                                // 用的它的 handleMessage方法
        mAsynchronous = async;
    

至此可以看出Handle取得了主线程的Looper对象和MessageQueue对象

  • 然后是第二步创建子线程并创建message对象
         new Thread(new Runnable() 
            @Override
            public void run() 
                Message message = new Message();
                message.obj = "s";
                handler.sendMessage(message);
            
        ).start();
  • 第三步我们调用handler.sendMessage将Message对象发送出去
                  
    public final boolean sendMessage(Message msg)
    
        return sendMessageDelayed(msg, 0);//调用sendMessageDelayed,第二个参数为停留的时间为 0,也就是直接发送
    

                      v
                      v
                      v 
  public final boolean sendMessageDelayed(Message msg, long delayMillis)
    
        if (delayMillis < 0) 
            delayMillis = 0;
        
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);//最终会调用sendMessageAtTime
    
                     v
                     v
                     v

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
        MessageQueue queue = mQueue; //获取Handler对应想成的MessageQueue
        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的enqueueMessage方法
    

                     v
                     v
                     v

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 
        msg.target = this;//将Message的target赋值给当前Handler
        if (mAsynchronous) 
            msg.setAsynchronous(true);
        
        return queue.enqueueMessage(msg, uptimeMillis);//调用MessageQueue的enqueueMessage将Message加入到MessageQueue 中
    


此时MessageQueue中就已经有了一个"持有当前Handler的Message对象"(将handler赋值给了Message的targer字段)。

然后要做的事情就是从MessageQueue中取出Message对象,并调用Message的target对象的dispatchMessage方法拿到数据。

因为我们是在主线程中创建的Handler
所以Activity建立的时候就已经初始化的Looper对象,也就是调用了Looper.prepareMainLooper()、Looper.loop()
Looper.loop()的作用就是不断的从MessageQueue中读取Message对象

      public static void main(String[] args) 
            ... // 仅贴出关键代码

            Looper.prepareMainLooper(); 
            // 1. 类似于Looper.prepare()为主线程创建1个Looper对象、

            ActivityThread thread = new ActivityThread(); 
            // 2. 创建主线程

            Looper.loop(); 
            // 3.不断的从MessageQueue中读取Message对象
        
        
                     v
                     v
                     v

  public static void loop() 

        // 获取当前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;
            // 获取Looper实例中的MessageQueue     
            for (;;) 
            //  从消息队列中取出消息
            Message msg = queue.next(); 
            if (msg == null) 
                return;
            
            //  拿到Message的target(也就是Handler)并调用Handler的dispatchMessage方法
            msg.target.dispatchMessage(msg);

        // 释放消息占据的资源
        msg.recycle();
        


                     v
                     v
                     v

  public void dispatchMessage(Message msg) 

    // 如果msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
    // 则执行handleCallback(msg),即回调Runnable对象里复写的run()    
        if (msg.callback != null) 
            handleCallback(msg);
         else 
            if (mCallback != null) 
                if (mCallback.handleMessage(msg)) 
                    return;
                
            
            // 如果msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息
            // 则回调复写的handleMessage(msg)
            handleMessage(msg);

        
    
    
                     v
                     v
                     v

   //handler.sendMessage(Message message)
   public void handleMessage(Message msg)   
          ... // 创建Handler实例时复写

   //handler.post(Runnable runnable)
  private static void handleCallback(Message message) 
        message.callback.run();
        //runnable的run方法需要执行的代码
    


问题解答

1. 子线程有哪些更新UI线程的方法?
  • 主线程中定义Handler,子线程通过mHander发送消息,主线程Handler的handleMessage更新UI
  • 用Activity对象的runOnUiThread方法(也是调用handler.post)
  • Handler.post(Runnable r)
  • 子线程中创建Handler,传入getMainLooper(也就是将主线程的Looper交给子线程使用)
2. Activity的runOnUiThread(Runnable action) 是如何实现子线程更新UI的?
  • runOnUiThread程序首先会判断当前线程是否是UI线程,如果是就直接运行,如果不是则mHandler.post(runnable);
  • post会调用sendMessageDelayed(getPostMessage( r ), 0);并将runnable封装成Message对象将runnable赋值给Message的callback
  • 再经过加入到MessageQueue->looper循环取出,调用callback就能在主线程运行了
  • 源码如下:
  public final void runOnUiThread(Runnable action) 
        if (Thread.currentThread() != mUiThread) 
            mHandler.post(action);//如果不在主线程则使用post方法
         else 
            action.run();
        
    
                     v
                     v
                     v

 public final boolean post(Runnable r)
    
       return  sendMessageDelayed(getPostMessage(r), 0);//getPostMessage返回一个Message对象
    
                     v
                     v
                     v
  private static Message getPostMessage(Runnable r) 
        Message m = Message.obtain();//创建一个Message对象,并设置callback为传入的runnable,与前面的Handler的
                                    // dispatchMessage方法结合理解,就会调用handleCallback来执行runnable内容了
        m.callback = r;
        return m;
    
              
3. Handler导致的内存泄露问题?

原因:
关闭Activity的时候,Handler消息队列 还有未处理的消息 /或者正在处理消息时,消息队列中的Message持有Handler实例的引用,然而Handler又持有Activity的引用,这样就导致Activity无法回收
解决方法:
使用静态内部类的Handler+弱引用

public class MainActivity extends AppCompatActivity 
    private myHandler handler;   
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     
        handler = new myHandler(this);
      
        new Thread() 
            @Override
            public void run() 
                try 
                    Thread.sleep(2000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
           
                Message msg = Message.obtain();
                msg.what = 1;
                handler.sendMessage(msg);
            
        .start();
    
 
  
    private static class myHandler extends Handler
        // 定义 弱引用实例
        private WeakReference<Activity> reference;
           
        public myHandler (Activity activity) 
            // 使用WeakReference弱引用持有Activity实例
            reference = new WeakReference<Activity>(activity);
         
       
        @Override
        public void handleMessage(Message msg) 
            switch (msg.what) 
                case 1:
                    Log.i("xiaoqiang", "收到消息");
                    break;
            
        
    


以上是关于Android---Handler体系的主要内容,如果未能解决你的问题,请参考以下文章

Android--Handler

Android Handler消息传递机制

Android Handler消息传递机制

Android——Handler详解

深入分析Android Handler消息机制

Android Handler 避免内存泄漏的用法总结