Handler

作者: _Rice_ | 来源:发表于2019-03-19 10:16 被阅读0次
image.png

Handler机制:

是一个消息传递机制,作用是将子线程需要的UI操作,传递到UI线程执行,保证线程安全。

image.png

相关类介绍

  • Message:线程间通讯的数据单元。

MessageQueue:消息队列,特点先进先出。存储Handler发送过来的消息(Message)

  • Handler:是一个消息处理器,将消息传递给消息队列,然后再由对应线程从消息队列中逐个取出数据,并执行

  • Looper:消息循环器,循环取出消息(Message),将取出的消息分发给对应的处理者(Handler)。
    特点:

  • 一个Handler只能拥有一个Looper。

  • 一个Looper可以绑定多个Handler

    public Handler(Callback callback, boolean async) {
       ...
        //关联Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //关联MessageQueue
        mQueue = mLooper.mQueue;
        ...
    }

在Handler的构造方法中,会关联Looper和MessageQueue(Looper的构造函数中会创建自己的MessageQueue)

源码分析

默认情况,消息队列只有一个,就是主线程的消息队列。这个线程由ActivityThread.main方法中创建,调用Looper.prepareMainLooper()创建,执行Looper.loop()来启动消息循环。

    public static void main(String[] args) {

       Looper.prepareMainLooper(); 
       // 1.创建主线程Looper
       ActivityThread thread = new ActivityThread(); 
       // 2. 创建主线程
       Looper.loop(); 
       // 3. 开启消息循环
}
    //设置和获取Looper,Looper保存在ThreadLocal中
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    //设置Looper,只能调用一次
    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的构造函数中会创建自己的MessageQueue
   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

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

可以看到Looper存储在ThreadLocal,这就实现了线程安全。Looper可以对应多个Handler

/** 
  * 源码分析: Looper.loop()
  * 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
  * 特别注意:
  *       a. 主线程的消息循环不允许退出,即无限循环
  *       b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
  */
  public static void loop() {
        
        ...// 仅贴出关键代码

        // 1. 获取当前Looper的消息队列
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
            // 即loop()执行前必须执行prepare(),从而创建1个Looper实例
            
            final MessageQueue queue = me.mQueue;
            // 获取Looper实例中的消息队列对象(MessageQueue)

        // 2. 消息循环(通过for循环)
            for (;;) {
            
            // 2.1 从消息队列中取出消息
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            // next():取出消息队列里的消息
            // 若取出的消息为空,则线程阻塞

            // 2.2 派发消息到对应的Handler
            msg.target.dispatchMessage(msg);
            // 把消息Message派发给消息对象msg的target属性
            // target属性实际是1个handler对象

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

loop()是一个消息无限循环,不断从消息队列获取消息,将消息派发到对应的Handler

public final class Message implements Parcelable {

    Handler target;
    Runnable callback;
    Message next;
}

  /** dispatchMessage(msg)
  * 定义:属于处理者类(Handler)中的方法
  * 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
  */
  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);

        }
    }

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


   public void handleMessage(Message msg) {  
          ... // 创建Handler实例时复写
   } 

dispatchMessage派发消息最终会调用HandlerMessage或者run方法处理

  1. 当调用Handler.post时msg.callback != null,会走run方法
  2. 当Message.obtain方法里设置时msg.callback != null,会走run方法
  3. 当Handler初始化时传入Callbackh时mCallback != null,会走mCallback.handleMessage(msg)方法
  4. 当前面两者都没有,直接调用sendMessage时,会走handleMessage(msg) 方法
demo
public class HandlerActivity extends AppCompatActivity {
    @BindView(R.id.content_tv)
    TextView mContentTv;

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                mContentTv.setText((CharSequence) msg.obj);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        ButterKnife.bind(this);
    }

    @OnClick({R.id.post, R.id.obtain, R.id.Handler_Callback_is_null, R.id.Handler_Callback_is_not_null})
    public void onClick(View view) {
        switch (view.getId()) {
            //当调用Handler.post时msg.callback != null,会走run方法
            case R.id.post:
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mContentTv.setText("post");
                            }
                        });
                    }
                }.start();
                break;
            //当Message.obtain方法里设置时msg.callback != null,会走run方法
            case R.id.obtain:
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        Message message = Message.obtain(mHandler, new Runnable() {
                            @Override
                            public void run() {
                                mContentTv.setText("obtain");
                            }
                        });
                        mHandler.sendMessage(message);
                    }
                }.start();
                break;
            //当Handler初始化时传入Callback时mCallback != null,会走mCallback.handleMessage(msg)方法
            case R.id.Handler_Callback_is_null:
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        Message msg = Message.obtain();
                        msg.what = 1; // 消息标识
                        msg.obj = "Handler_Callback_is_null"; // 消息内存存放
                        mHandler.sendMessage(msg);
                    }
                }.start();
                break;
            //当前面两者都没有,直接调用sendMessage时,会走handleMessage(msg) 方法
            case R.id.Handler_Callback_is_not_null:
                final Handler handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        if (msg.what == 2) {
                            mContentTv.setText((CharSequence) msg.obj);
                        }
                        return true;

                    }
                });

                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        Message msg = Message.obtain();
                        msg.what = 2; // 消息标识
                        msg.obj = "Handler_Callback_is_not_null"; // 消息内存存放
                        handler.sendMessage(msg);
                    }
                }.start();
                break;
        }
    }
}

为什么可以直接post呢,因为post(Runnable r)会将Runnable包装成Message对象,并将Runnable对象设置给Message对象的callback,最好将Message对象插入消息队列中去。

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

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

    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);
    }

    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;
        }
        //将消息插入MessageQueue
        return enqueueMessage(queue, msg, uptimeMillis);
    }

Handler机制流程图

image.png

问题

1、为什么不new Message()而采用Message.obtain()?

因为Message内部维护了1个Message池,用于Message消息对象的复用,使用obtain()则是直接从池内获取,避免内存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-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
2、Handler可以在子线程创建吗?

通常Handler必须在主线程创建,因为Handler机制的主要作用是更新UI。当然也可以创建在子线程,那么就得自己手动去prepare和loop。

    new Thread(){
        @Override
        public void run() {
            super.run();
            //创建当前线程的Looper,并绑定到ThreadLocal中
            Looper.prepare();
            //创建Handler,关联Looper和MessageQueue
            Handler handler = new Handler();
            //启动消息循环
            Looper.loop();
        }
    }.start();
3、Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

loop虽然是死循环,但是如果messagequeue里面没有消息的话,会休眠在native层那个nativePollonce方法

问题参考:https://www.zhihu.com/question/34652589/answer/90344494?from=profile_answer_card](https://www.zhihu.com/question/34652589/answer/90344494?from=profile_answer_car

4、如何避免不当导致的内存泄漏

问题根源: 非静态匿名内部类持有外部类引用

1、静态Handler内部类, 如果在handMessage方法里需要用到上下文, 进行弱化

public class NoLeakActivity extends AppCompatActivity {

    private NoLeakHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new NoLeakHandler(this);

        Message message = Message.obtain();

        mHandler.sendMessageDelayed(message,10*60*1000);
    }

    private static class NoLeakHandler extends Handler{
        private WeakReference<NoLeakActivity> mActivity;

        public NoLeakHandler(NoLeakActivity activity){
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }
}

但是为什么为static类型就会解决这个问题呢?
因为在java中所有非静态的对象都会持有当前类的强引用,而静态对象则只会持有当前类的弱引用。
声明为静态后,handler将会持有一个Activity的弱引用,而弱引用会很容易被gc回收,这样就能解决Activity结束后,gc却无法回收的情况。

2、Handler内部自带的remove方法

学习参考:

https://www.jianshu.com/p/b4d745c7ff7a

https://www.zhihu.com/question/34652589/answer/90344494?from=profile_answer_card

Android开发进阶从小工到专家

相关文章

网友评论

      本文标题:Handler

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