重学Handler。

Looper

Looper起到了循环的作用,主要涉及到几个关键方法,prepare()、loop()、构造方法、quit()

prepare()

是一个静态方法,通过prepare方法来构造一个looper对象,并且放到threadLocal中去,如果threadLocal中已经有looper对象了,那么就会抛出异常。所以prepare方法在每个线程中只可以执行一次。

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对象,放到ThreadLocal中
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  //创建MessageQueue对象
    mThread = Thread.currentThread();  //记录当前线程.
}

ThreadLocal

Thread Local Storage,简称TLS,线程本地存储区。每个线程都有自己私有的存储区域,各个线程间不可以相互访问。
TheadLocal主要有几个方法:get()、set()、remove()

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this); // 根据key获取对应的Value,
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);  // map结构,key为TheadLocal
    else
        createMap(t, value);
}
 public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         m.remove(this);  // 从map中移除当前对象
 }

为什么要使用ThreadLocalMap,因为一些map比如:hashmap对它的entry都是强引用的,而ThreadLocalMap中对entry是弱引用WeakReference包装了key值,及threadLocal是被弱引用的。
详见 ThreadLocal弱引用与内存泄漏分析

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;

    // 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(); // 可能会阻塞
        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);
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            msg.target.dispatchMessage(msg);// 分发message
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

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

        msg.recycleUnchecked();  // 回收message,这里会复用msg
    }
}

loop方法主要是以下工作

  1. 从messageQueue中获取下一个message
  2. 获取message的target,分发给target
  3. 回收message

Handler

是我们最常接触到的类,主要负责发送message,和处理message

构造

构造时会检查当前线程的looper,或者要求传入looper。然后获取messageQueue

发送消息

// 最终发送消息会走到这里,
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;  // 指定target为自己
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true); // 设置成异步消息
        }
        return queue.enqueueMessage(msg, uptimeMillis); // 将消息放到MessageQueue中,等待执行
    }

sendMessageAtFrontOfQueue()

public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
    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, 0); // 通过把时间设置成O,来把消息放到队列前端
}

处理消息

 
/**
 * Handle system messages here.
 */
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); // 执行对应的runnable,也是在looper线程执行的,因为此方法由looper调用的
    } else {
        if (mCallback != null) {  // 如果设置了callback,交由callback执行,callback是构造时传入
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); // 空实现,一般由子类继承重写
    }
}

MessageQueue

消息的队列,负责维护消息队列,消息的入队和出队

next()

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    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();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis); //阻塞,等待唤醒,nextPollTimeoutMillis为超时时间

        synchronized (this) {  // 同步代码块,防止多线程入队操作
            // Try to retrieve the next message.  Return if found.
            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) { // 存在要执行的任务
                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;  // return 需要执行的msg对象
                }
            } else {  // 不存在要执行的任务
                // No more messages.
                nextPollTimeoutMillis = -1;  // 无限期的等待下去
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {  //没有要执行的msg,又没有idleHandler需要执行
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;  // 阻塞吧
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle(); // 执行idleHandler的queueIdle方法。
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler); // 如果idleHandler返回了false,那就移除这个IdleHandler
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;  // 调用idleHandler时,可能已经传递了一条新消息,因此请返回并再次查找未决消息,而无需等待。
    }
}

enqueueMessage()

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已经在使用了
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {  // 插入头结点,sendMessageAtFrontOfQueue会插入到这里
            // 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 (;;) { //找到msg在链表中的位置
                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 // msg的下一个结点
            prev.next = msg;   // msg的上一个结点
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr); // 是否唤醒next中的阻塞
        }
    }
    return true;
}

postSyncBarrier()

主要是插入一个同步屏障消息,让所有的异步msg先执行。

public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis()); // 这里可以看出,是使用了当前时间,所以sendMessageAtFrontOfQueue可以比同步屏障更先执行
}

removeSyncBarrier()

删除同步屏障

Message

链表结构,是存储消息内容的实体

消息池

会将用过的消息进行缓存

同步消息、异步消息、同步屏障消息

一般同步消息和异步消息没区别,但是同步屏障存在时,异步消息会先执行

同步屏障的作用

当设置了同步屏障之后,next函数将会忽略所有的同步消息,返回异步消息。换句话说就是,设置了同步屏障之后,Handler只会处理异步消息。再换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。

通常我们使用Handler发消息时,这些消息都是同步消息,如果我们想发送异步消息,那么在创建Handler时使用以下构造函数中的其中一种(async传true),这样发送的所有消息都为异步消息。(此种方式较为罕见)

public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

同步屏障的应用

Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //设置同步障碍,确保mTraversalRunnable优先被执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //内部通过Handler发送了一个异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

sendMessageAtFrontOfQueue可以比同步屏障更先执行

从MessageQueue的next方法可以看出,是否存在同步屏障是通过判断当前要执行的message是否为同步屏障消息(即message.target为nul)来实现的。而sendMessageAtFrontOfQueue可以直接把message插入到MessageQueue的头部,所以直接就将当前要执行的Message替换成了自己。这样执行next的时候肯定会将当前的Front Message返回,这样就打破了同步屏障。

    HandlerThread test = new HandlerThread("test");
    test.start();
    Handler handler = new Handler(test.getLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            Logger.i("handleMessage() what" + msg.what);
            return true;
        }
    });
    int token= handler.getLooper().getQueue().postSyncBarrier(); // 设置同步屏障
    handler.sendEmptyMessageDelayed(1, 0);   //发送同步Message
    handler.sendEmptyMessageDelayed(2, 0);  //发送同步Message
    Message msg = handler.obtainMessage(4);
    msg.setAsynchronous(true);
    handler.sendMessageDelayed(msg, 0);  //发送异步Message
    handler.sendMessageAtFrontOfQueue(handler.obtainMessage(3)); //发送同步Front Message
//    handler.getLooper().getQueue().removeSyncBarrier(token);

以上代码输出结果为:

handleMessage() what3
handleMessage() what4
  1. 因为同步屏障,同步消息1和2没有被执行。
  2. 因为sendMessageAtFrontOfQueue执行时间小于同步屏障消息,所有会被执行

IdleHandler

Handler空闲时回调,可以延时执行一个任务,比如获取view高度,显示dialog等

View.postDelayed()原理

public boolean postDelayed(Runnable action, long delayMillis) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {  // 当view已经attached,调用attachInfo的handler进行执行
        return attachInfo.mHandler.postDelayed(action, delayMillis);
    }

    // Postpone the runnable until we know on which thread it needs to run.
    // Assume that the runnable will be successfully placed after attach.
    getRunQueue().postDelayed(action, delayMillis); // 将runnable缓存起来,等待attach
    return true;
}

private HandlerActionQueue getRunQueue() {
    if (mRunQueue == null) {
        mRunQueue = new HandlerActionQueue();  //此对象中存在一个数组,封装了runnable和delay时间
    }
    return mRunQueue;
}

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mAttachInfo = info;
   
    // Transfer all pending runnables.
    if (mRunQueue != null) {
        mRunQueue.executeActions(info.mHandler); // 执行所有挂起的runnable
        mRunQueue = null;
    }
}

那么dispatchAttachedToWindow()方法是在什么时候执行的呢?我们可以通过重写onAttachedToWindow()方法,在onAttachedToWindow中打印出来调用栈(通过Exception打印)

2021-05-26 14:41:00.478 16540-16540/com.ericcode.diary W/System.err: java.lang.Exception
2021-05-26 14:41:00.478 16540-16540/com.ericcode.diary W/System.err:     at com.ericcode.diary.ui.view.MyTextView.onAttachedToWindow(MyTextView.kt:14)
2021-05-26 14:41:00.478 16540-16540/com.ericcode.diary W/System.err:     at android.view.View.dispatchAttachedToWindow(View.java:20658)
2021-05-26 14:41:00.479 16540-16540/com.ericcode.diary W/System.err:     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3529)
2021-05-26 14:41:00.479 16540-16540/com.ericcode.diary W/System.err:     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3529)
2021-05-26 14:41:00.479 16540-16540/com.ericcode.diary W/System.err:     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2527)
2021-05-26 14:41:00.479 16540-16540/com.ericcode.diary W/System.err:     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2060)
2021-05-26 14:41:00.479 16540-16540/com.ericcode.diary W/System.err:     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8482)
2021-05-26 14:41:00.479 16540-16540/com.ericcode.diary W/System.err:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1077)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.view.Choreographer.doCallbacks(Choreographer.java:897)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.view.Choreographer.doFrame(Choreographer.java:826)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1062)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.os.Handler.handleCallback(Handler.java:938)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.os.Looper.loop(Looper.java:233)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:8053)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
2021-05-26 14:41:00.480 16540-16540/com.ericcode.diary W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

通过调用栈可以看出:dispatchAttachedToWindow()是由ViewRootImpl.doTraversal()调起。而在ViewRootImpl.doTraversal中,设置同步屏障,发送异步消息,来保证渲染逻辑优先执行。所以在View.postDelayed()中传入的runnable,是在view完成绘制后才执行的。
所以这几个方法执行的顺序为:dispatchAttachedToWindow()->onMeasure()->onLayout()->onDraw()->View.post()中的runnable

同步屏障真正的位置在doTraversal()->performTraversals()->scheduleTraversals()

AIASST-1831:
问题原因:由子线程与主线程同时刷新ui导致barrier没有被移除使handler接受不到回调.

barrier存在会阻塞同步消息,在scheduleTraversals时放置barrier,并在执行doTraversal或unscheduleTraversals时去掉,目的是为了保证ui刷新实时性.

1.复现问题时,barrier=9745存在
ACTIVITY com.xiaomi.aiasst.service/.aicall.activities.SettingsActivity 89bbc25 pid=19343
Looper (main, tid 2)

{a033026}
Message 0: { when=-9m42s837ms barrier=9745 }
Message 1:

{ when=-9m42s831ms callback=rx.android.schedulers.LooperScheduler$ScheduledAction obj=rx.android.schedulers.LooperScheduler$HandlerWorker@f6b4467 target=android.os.Handler }
Message 2:

{ when=-9m42s133ms what=13 target=com.xiaomi.aiasst.service.aicall.impl.presenter.SettingInCallPresenter$6 }
Message 3:

{ when=-9m41s131ms what=12 target=com.xiaomi.aiasst.service.aicall.impl.presenter.SettingInCallPresenter$6 }
2.原因是在相近时间两个线程请求scheduleTraversals,放置了barrier,在doTraversal只会移除最新的barrier9746:
07-31 10:07:29.801 1000 19343 19343 D ViewRootImpl[SettingsActivity]: postSyncBarrier mTraversalBarrier = 9745
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: postSyncBarrier mTraversalBarrier = 9746
07-31 10:07:29.812 1000 19343 19343 D ViewRootImpl[SettingsActivity]: doTraversal removeSyncBarrier mTraversalBarrier = 9746.

3.线程19801中进行invalidate操作:
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: java.lang.RuntimeException: ViewRootImpl[SettingsActivity]
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewRootImpl.scheduleTraversals(ViewRootImpl.java:1814)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewRootImpl.invalidate(ViewRootImpl.java:1542)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewRootImpl.onDescendantInvalidated(ViewRootImpl.java:1535)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.onDescendantInvalidated(ViewGroup.java:6004)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.ViewGroup.invalidateChild(ViewGroup.java:6022)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.View.invalidateInternal(View.java:18225)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.View.invalidate(View.java:18185)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at android.view.View.invalidate(View.java:18167)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at com.xiaomi.aiasst.service.aicall.view.RecordingWaveView.update(RecordingWaveView.java:243)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at com.xiaomi.aiasst.service.aicall.view.RecordingWaveView.addVolumes(RecordingWaveView.java:227)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at com.xiaomi.aiasst.service.aicall.view.AudioRecordGroup.updateWave(AudioRecordGroup.java:30)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at com.xiaomi.aiasst.service.aicall.impl.presenter.SettingInCallPresenter.updateMaxAmplitude(SettingInCallPresenter.java:384)
07-31 10:07:29.801 1000 19343 19801 D ViewRootImpl[SettingsActivity]: at com.xiaomi.aiasst.service.aicall.impl.presenter.SettingInCallPresenter$PrologueAudioRecordThread.run(SettingInCallPresenter.java:838)

4.子线程中更新ui没有crash是因为基线临时移除了线程检查限制
http://10.221.160.141:8888/source/xref/miui-r-umi-dev/frameworks/base/core/java/android/view/ViewRootImpl.java#1689

参考

Android消息机制1-Handler(Java层)
Handler机制之消息的同步屏障


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

个人特质 上一篇
做技术预研的一些想法 下一篇