zoukankan      html  css  js  c++  java
  • 【Android】Handler机制

    Android线程相关

    Android应用程序的main函数在ActivityThread中。程序启动后会有默认的主线程(UI线程)。

    在线程中关联一个消息队列,所有操作会被封装成消息交给主线程处理。

    ActivityThread的main():

    public static void main(String[] args) {
        ...
    	looper.prepareMainLooper(); // 创建消息循环Looper
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler(); // UI线程的Handler
        }
        ...
        Looper.loop(); // 执行消息循环
    	...
    }
    

    Handler

    每个Handler会关联消息队列,消息队列封装在Looper中,而每个Looper会关联一个线程(Looper通过ThreadLocal封装),所以每个消息队列会关联一个线程。

    默认情况下,只有主线程的消息队列,通过looper.prepareMainLooper()来创建,Looper.loop()启动消息循环。

    Handler与消息队列的关联

    public Handler(@Nullable Callback callback, boolean async) {
    	... //内存泄漏相关
        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;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    myLooper()通过sThreadLocal.get()来获取Looper对象。

    prepareMainLooper()中调用了prepare(),将Looper对象设置给了sThreadLocal,队列就与线程关联起来了。

    所以更新的Handler必须在主线程中创建,才能跟主线程的消息队列关联上。

    消息循环

    Looper.loop()方法:

    public static void loop() {
        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; //获取消息队列
        for (;;) { // 死循环处理消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            msg.target.dispatchMessage(msg); // 消息分发
            ...
            msg.recycleUnchecked(); // 消息回收
        }
    }
    

    msg的target为Handler,实际为Handler将Message投递到MessageQueue,MessageQueue又将Message分发给Handler处理。

    Handler中的dispatchMessage():

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    如果Runnable类型的callback为空,则执行handlMessage()方法来处理消息;如果不为空,则执行handleCallback()来处理。这分别为Handler的两种分发方式。所以有以下的两种使用方式:

    1. 使用post(Runnalble callback)来发送消息,这时执行handleCallback方式
    2. 使用sendMessage(Message msg)来发送消息,这时执行handleMessage方式

    在子线程中创建Handler

    子线程中默认Looper为空,所以之间创建Handler会报错,所以需要先prepare()为该线程创建Looper,然后loop()启动消息循环。

    new Thread() {
    	Handler handler = null;
        public void run() {
            Looper.prepare();
            handler = new Handler();
            Looper.loop();
        }.start();
    }
    

    Handler内存泄漏问题

    https://juejin.im/post/5ccaa95ae51d453b222b7953
    当Handler定义为非静态内部类或者匿名内部类,使得Handler默认持有外部类的引用。Activity销毁时,由于Handler可能有未执行完或正在执行的Message,由于Handler持有Activity的引用,使得GC无法回收Activity。

    匿名内部类:

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage
        }
    }
    

    非静态内部类:

    protected class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            ...
        }
    }
    

    解决方法:

    1. 在销毁时清空Handler中所有的消息和callback

      @Override
      protected void onDestroy() {
          super.onDestroy();
          mHandler.removeCallbackAndMessage(null);
      }
      
    2. 静态内部类+弱引用

      private static class MyHandler extends Handler {
          WeakReference<Activity> activity;
          MyHandler(Activity activity) {
              this.activity = new WeakReference<Activity>(activity);
          }
          ...
      }
      
  • 相关阅读:
    GridSearchCV.grid_scores_和mean_validation_score报错
    scikit-learn使用fetch_mldata无法下载MNIST数据集的问题
    Python数据科学手册Seaborn马拉松可视化里时分秒转化为秒数的问题
    Jupyter导出PDF从入门到绝望(已解决)
    食谱数据库数据找不到的问题
    TensorBoard计算加速
    TensorBoard可视化
    TensorFlow高层封装:从入门到喷这本书
    <!DOCTYPE html> 详解
    不同数据库之间表数据的迁移
  • 原文地址:https://www.cnblogs.com/y4ngyy/p/12346384.html
Copyright © 2011-2022 走看看