前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Handler 消息机制原来解析

Handler 消息机制原来解析

原创
作者头像
李林LiLin
修改2020-12-22 10:09:28
8420
修改2020-12-22 10:09:28
举报
文章被收录于专栏:Android进阶编程Android进阶编程

一、主要角色

1.1、message:消息

分为同步消息、异步消息、屏障消息。但是异步消息和屏障消息的相关API都是隐藏的,需要通过反射才能使用。

1.2、MessageQueue:消息队列

负责消息的存储与管理,负责管理由 Handler 发送过来的 Message。读取会自动删除消息,单链表维护,插入和删除上有优势。在其 next()方法中会无限循环,不断判断是否有消息,有就返回这条消息并移除。

1.3、Looper:消息循环器

负责关联线程以及消息的分发,在该线程下从 MessageQueue 获取 Message,分发给 Handler,Looper 创建的时候会创建一个 MessageQueue,调用 loop()方法的时候消息循环开始,其中会不断调用 messageQueue 的 next()方法,当有消息就处理,否则阻塞在 messageQueue 的 next()方法中。当 Looper 的 quit()被调用的时候会调用messageQueue 的 quit(),此时 next()会返回 null,然后 loop()方法也就跟着退出。

1.4、Handler:消息处理器

负责发送并处理消息,面向开发者,提供 API, 并隐藏背后实现的细节。

二、基本用法

我们可以使用 Handler 发送并处理与一个线程关联的 Message 和 Runnable 。(注意:Runnable 会被封装进一个 Message,所以它本质上还是一个 Message )。

每个 Handler 都会跟一个线程绑定,并与该线程的 MessageQueue 关联在一起,从而实现消息的管理以及线程间通信。

代码语言:java
复制
Handler handler = new Handler(){
  @Override
  public void handleMessage(final Message msg) {
    //这里接受并处理消息
  }
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);

实例化一个 Handler 重写 handleMessage 方法 ,然后在需要的时候调用它的 send 以及 post 系列方法就可以了,非常简单易用,并且支持延时消息。但是奇怪,我们并没有看到任何 MessageQueue 的身影,也没看到它与线程绑定的逻辑,这是怎么回事?

三、原理解析

3.1、整体流程

相信大部分人对Handler的整体工作流程都有所了解了,但是这里还是先简单回顾一下:

第一步:Handler 通过 sendMessage()等系列发送消息 Message 到消息队列 MessageQueue。

第二步:Looper 通过 loop()不断提取触发条件的 Message,并将 Message 交给对应的 target,也就是handler 来处理。

第三步:对应target 调用自身的 handleMessage()方法来处理 Message。

3.2、Handler 与 Looper 的关联

想要了解Handler是怎么和Looper关联的,就要先看一下Handler的构造方法,Handler的显示构造方法有很多种:

代码语言:java
复制
    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback) {
        this(callback, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

Handler这些显示的构造方法最终都会调到Handler的一个隐藏的构造方法,为什么这个最终的构造方法是隐藏的呢,这就涉及到了异步消息和屏障消息相关API了,这个构造方法里有一个参数是async,表示是否是异步,这个API,默认是隐藏的。代码如下:

代码语言: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,如果当前线程没有looper,则抛出一个异常
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //获取当前线程looper对象里的消息队列,也就是上面提到的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

通过上面代码可以看到,在Handler的构造方法里会回去当前线程的looper,并通过获取到的looper对象回去消息队列。也就是说Handler里面持有当前线程的looper和MessageQueue的引用。这样Handler就和looper关联起来了。如果没有获取到当前线程的looper,就会抛出一个异常。

Handler是通过调用Looper.myLooper()方法来获取当前线程的looper的,我们来看下这个方法的实现:

代码语言:java
复制
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

可以看到是通过 sThreadLocal.get() 返回的,那么 sThreadLocal 是何许人也,来看一下:

代码语言:java
复制
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

我们发现 sThreadLocal 是Looper类里的一个静态的 ThreadLocal 对象,里面存放的就是looper。(ThreadLocal 不了解的可以自己查找下资料)。那么sThreadLocal 里面的looper是什么时候存进去的?通过查看Looper源码,我们发现了秘密:

代码语言:java
复制
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));
    }

原来是在 prepare()方法里直接 创建了一个Looper,然后存了进去。这个方法大家应该都很熟悉吧。

这就是为什么在子线程里面使用Handler的时候,如果不调用Looper.prepare() 方法会抛出异常的原因了(就是上面Handler构造方法里面的异常)。Looper.prepare() 方法只能调用一次,否则会抛异常。

我们平时在主线程使用Handler的时候并没有调用这个方法,怎么没有抛出异常呢,这个我放在后面说。(见4.2)

到这里就明白了,handler是通过Looper和线程关联起来的。

3.3、Message 的存储与管理

Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。

不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。

代码语言:javascript
复制
//Handler
sendEmptyMessage(int)
  -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
      -> enqueueMessage(MessageQueue,Message,long)
  			-> queue.enqueueMessage(Message, long);

来看下enqueueMessage方法的实现:

代码语言:java
复制
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        
        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //根据时间,把消息加入到队列
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

通过上面代码可以发现,MessageQueue内部是通过一个链表来维护的,链表里的每一个节点就是一个Message,链表是通过Message的when参数来排序的。

3.4、Message 的分发与处理

我们已经知道通过Looper.prepare() 创建Looper,然后在调用Looper.loop(),looper就开始工作了。我们现在就来看一下loop()

的具体工作怎么实现的。

代码语言:java
复制
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;

        // 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 (;;) {
            // 不断从 MessageQueue 获取 消息
            Message msg = queue.next(); // might block
            //退出 Looper 
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                //将msg交给他对应的target来处理
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            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);
            }
            //回收 message
            msg.recycleUnchecked();
        }
    }

从代码可以看出loop()主要工作如下:

第一步:调用 queue.next() 从 MessageQueue 获取消息,如果msg为null,则表示MessageQueue已经推出了,就退出Looper。

第二步:msg.target.dispatchMessage(msg),将msg讲给对应的target处理。

第三步:msg.recycleUnchecked() 回收msg。

来看看MessageQueue.next()的具体实现:

代码语言:java
复制
Message next() {
        //判断是否退出了
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //1、如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //2、msg.target == null 表示当前消息是屏障消息
                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());//3、遍历消息链表找到最近的一条异步消息
                }
                if (msg != null) {
                    //4、判断消息有没有到处理时间
                    if (now < msg.when) {
                        //如果没有到处理时间则等待
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        //消息到了处理时间,就从链表移除,返回它。
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    // 如果没有消息就一直休眠,等待被唤醒。
                    nextPollTimeoutMillis = -1;
                }

                //。。。省略代码
            }
        }
    }

从上面代码可以看出工作流程如下:

第一步:通过 nativePollOnce(ptr, nextPollTimeoutMillis) 判断是否有新的消息要被处理,如果没有则阻塞。

第二步:判断当前消息是不是屏障消息,如果是屏障消息,则遍历消息链表找到最近的一条异步消息,并处理找到的这个异步消息。如果不是屏障消息,则正常处理该消息。需要注意的是,如果设置了屏障消息,那么MessageQueue就会一直循环的找该屏障消息后面有没有异步消息,如果没有,则会阻塞。而不会在处理同步消息。因为屏障消息没有target,因此屏障消息不会被处理。当你使用了屏障消息后,在不使用的时候一定要手动移除该屏障消息。否则,屏障消息后面的同步消息永远不会被处理。屏障消息前面的消息不受影响。

第三步:判断找到的消息有没有到处理时间,如果已到处理时间,则把该消息从链表中移除,并返回该消息给looper。如果没到处理时间,则阻塞。

Looper在收到 MessageQueue 返回的消息后,会调用 msg.target.dispatchMessage(msg) 来处理消息,target 也就是 Handler

代码语言:java
复制
public void dispatchMessage(Message msg) {
        //如果msg设置了callback 就交给callback处理,这里的callback就是封装成的runnable
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //如果handler设置了mCallback,则优先交给mCallback处理。这个后面还会具体将。
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //回调到 Handler 的 handleMessage 方法
            handleMessage(msg);
        }
    }
    private static void handleCallback(Message message) {
        message.callback.run();
    }

从实现代码可以看出:

如果消息是封装的runnable,则调用runnable的run方法。如果handler设置了mCallback(见4.4),则优先交给mCallback处理,如果mCallback处理返回值为false,则handler可以继续处理。

3.5、小结

Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。

尝试小结一下它们的职责,如下:

  • Looper :负责关联线程以及消息的分发在该线程下**从 MessageQueue 获取 Message,分发给 Handler ;
  • MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
  • Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。

线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

四、 Handler 的延伸问题

Handler 虽然简单易用,但是要用好它还是需要注意一点,另外 Handler相关 还有些鲜为人知的知识技巧,比如 IdleHandler。

由于 Handler 的特性,它在 Android 里的应用非常广泛,比如: AsyncTask、HandlerThread、Messenger、IdleHandler 和 IntentService 等等。

这些我会讲解一些,我没讲到的可以自行搜索相关内容进行了解。

4.1、Handler 引起的内存泄露原因以及最佳解决方案

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。

这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决该问题的最有效的方法是:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息

示例代码如下:

代码语言:javascript
复制
private static class SafeHandler extends Handler {

    private WeakReference<HandlerActivity> ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}
复制代码

并且再在 Activity.onDestroy() 前移除消息,加一层保障:

代码语言:javascript
复制
@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}
复制代码

这样双重保障,就能完全避免内存泄露了。

注意:单纯的在 onDestroy 移除消息并不保险,因为 onDestroy 并不一定执行。

4.2、为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?

前面我们提到了每个Handler 的线程都有一个 Looper ,主线程当然也不例外,但是我们不曾准备过主线程的 Looper 而可以直接使用,这是为何?

在 ActivityThread.main() 方法中有如下代码:

代码语言:javascript
复制
//android.app.ActivityThread
public static void main(String[] args) {
  //...
  Looper.prepareMainLooper();

  ActivityThread thread = new ActivityThread();
  thread.attach(false);

  if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
  }
  //...
  Looper.loop();

  throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper.prepareMainLooper(); 代码如下:

代码语言:javascript
复制
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

可以看到在 ActivityThread 里调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。

注意:Looper.loop() 是个死循环,后面的代码正常情况不会执行。

4.3、主线程的 Looper 不允许退出

如果你尝试退出 Looper ,你会得到以下错误信息:

代码语言:javascript
复制
Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
  at android.os.MessageQueue.quit(MessageQueue.java:415)
  at android.os.Looper.quit(Looper.java:240)

why? 其实原因很简单,主线程不允许退出,退出就意味 APP 要挂。

4.4、Handler 里藏着的 Callback 能干什么?

在 Handler 的构造方法中有几个 要求传入 Callback ,那它是什么,又能做什么呢?

来看看 Handler.dispatchMessage(msg) 方法:

代码语言:javascript
复制
public void dispatchMessage(Message msg) {
  //这里的 callback 是 Runnable
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    //如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

可以看到 Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理

这个就很有意思了,这有什么作用呢?

我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息!

场景:Hook ActivityThread.mH , 在 ActivityThread 中有个成员变量 mH ,它是个 Handler,又是个极其重要的类,几乎所有的插件化框架都使用了这个方法。

4.5、创建 Message 实例的最佳方式

由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗。

方法有二:

  1. 通过 Message 的静态方法 Message.obtain(); 获取;
  2. 通过 Handler 的公有方法 handler.obtainMessage();

4.6、子线程里弹 Toast 的正确姿势

当我们尝试在子线程里直接去弹 Toast 的时候,会 crash :

代码语言:javascript
复制
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

本质上是因为 Toast 的实现依赖于 Handler,按子线程使用 Handler 的要求修改即可,同理的还有 Dialog。

正确示例代码如下:

代码语言:javascript
复制
new Thread(new Runnable() {
  @Override
  public void run() {
    Looper.prepare();
    Toast.makeText(HandlerActivity.this, "不会崩溃啦!", Toast.LENGTH_SHORT).show();
    Looper.loop();
  }
}).start();

4.7、妙用 Looper 机制

我们可以利用 Looper 的机制来帮助我们做一些事情:

  1. 将 Runnable post 到主线程执行;
  2. 利用 Looper 判断当前线程是否是主线程。

完整示例代码如下:

代码语言:javascript
复制
public final class MainThread {

    private MainThread() {
    }

    private static final Handler HANDLER = new Handler(Looper.getMainLooper());

    public static void run(@NonNull Runnable runnable) {
        if (isMainThread()) {
            runnable.run();
        }else{
            HANDLER.post(runnable);
        }
    }

    public static boolean isMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

}

4.8、sendMessageDelayed原理

  1. sendMessageDelayed 最终是调用的 sendMessageAtTime
  2. 消息队列的插入是由msg.when顺序排列。如果当前的消息没有到执行时间,其后的也一定不会到,当前的系统时间小于msg.when,那么会计算一个timeout,在到执行时间时wake up。

4.9、只能保证在when之前消息不被处理,不能够保证一定在when时被处理

  1. 在Loop.loop()中是顺序处理消息,如果前一个消息处理耗时较长,完成之后已经超过了when,消息不可能在when时间点被处理。
  2. 即使when的时间点没有被处理其他消息所占用,线程也有可能被调度失去cpu时间片。
  3. 在等待时间点when的过程中有可能入队处理时间更早的消息,会被优先处理,又增加了(1)的可能性。

所以由上述三点可知,Handler提供的指定处理时间的api诸如postDelayed()/postAtTime()/sendMessageDelayed()/sendMessageAtTime(),只能保证在指定时间之前不被执行,不能保证在指定时间点被执行。

4.10、主线程的死循环一直运行是不是特别消耗CPU资源呢?

并不是,这里就涉及到 Linux pipe/epoll 机制,简单说就是在主线程的 MessageQueue 没有消息时,便阻塞在 loop 的 queue.next()中的 nativePollOnce方法里,此时主线程会释放 CPU 资源进入休眠状态,直到下个消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作。这里采用的epoll 机制,是一种 IO 多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读 或写就绪),则立刻通知相应程序进行读或写操作,本质是同步 I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量 CPU 资 源。

4.11、handler postDelay 这个延迟是怎么实现的? 

handler.postDelay 并不是先等待一定的时间再放入到 MessageQueue 中,而是直接进入MessageQueue,以 MessageQueue 的时间顺序排列和唤醒的方式结合实现的。

五、总结

由前文可得出一些知识点,汇总一下,方便记忆。

  1. Handler 的背后有 Looper、MessageQueue 支撑,Looper 负责消息分发,MessageQueue 负责消息管理;
  2. 在创建 Handler 之前一定需要先创建 Looper;
  3. Looper 有退出的功能,但是主线程的 Looper 不允许退出;
  4. 异步线程的 Looper 需要自己调用 Looper.myLooper().quit(); 退出;
  5. Runnable 被封装进了 Message,可以说是一个特殊的 Message;
  6. Handler.handleMessage() 所在的线程是 Looper.loop() 方法被调用的线程,也可以说成 Looper 所在的线程,并不是创建 Handler 的线程;
  7. 使用内部类的方式使用 Handler 可能会导致内存泄露,即便在 Activity.onDestroy 里移除延时消息,必须要写成静态内部类;

六、参考链接

Android中为什么主线程不会卡死

https://juejin.cn/post/6844903783139393550

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、主要角色
    • 1.1、message:消息
      • 1.2、MessageQueue:消息队列
        • 1.3、Looper:消息循环器
          • 1.4、Handler:消息处理器
          • 二、基本用法
          • 三、原理解析
            • 3.1、整体流程
              • 3.2、Handler 与 Looper 的关联
                • 3.3、Message 的存储与管理
                  • 3.4、Message 的分发与处理
                    • 3.5、小结
                    • 四、 Handler 的延伸问题
                      • 4.1、Handler 引起的内存泄露原因以及最佳解决方案
                        • 4.2、为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?
                          • 4.3、主线程的 Looper 不允许退出
                            • 4.4、Handler 里藏着的 Callback 能干什么?
                              • 4.5、创建 Message 实例的最佳方式
                                • 4.6、子线程里弹 Toast 的正确姿势
                                  • 4.7、妙用 Looper 机制
                                    • 4.8、sendMessageDelayed原理
                                      • 4.9、只能保证在when之前消息不被处理,不能够保证一定在when时被处理
                                        • 4.10、主线程的死循环一直运行是不是特别消耗CPU资源呢?
                                          • 4.11、handler postDelay 这个延迟是怎么实现的? 
                                          • 五、总结
                                          • 六、参考链接
                                          相关产品与服务
                                          消息队列 CMQ 版
                                          消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档