Android View post 方法

本文详细解析了Android中View.post方法的工作原理,包括其与Handler.post方法的区别,AttachInfo的作用,以及HandlerActionQueue如何缓存并执行任务。

解析View.post方法。分析一下这个方法的流程。

说起post方法,我们很容易联想到Handlerpost方法,都是接收一个Runnable对象。那么这两个方法有啥不同呢?

Handler的post方法

先来简单看一下Handlerpost(Runnable)方法。这个方法是将一个Runnable加到消息队列中,并且会在这个handler关联的线程里执行。

下面是关联的部分源码。可以看到传入的Runnable对象,装入Message后,被添加进了queue队列中。

Handler 有关的部分源码

    // android.os Handler 有关的部分源码
    public final boolean post(@NonNull Runnable r) {
        return sendMessageDelayed(getPostMessage(r), 0);
    }

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

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

    public boolean sendMessageAtTime(@NonNull 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);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码

具体流程,可以看handler介绍

View的post方法

我们直接跟着post的源码走。

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }

    // Postpone the runnable until we know on which thread it needs to run.
    // Assume that the runnable will be successfully placed after attach.
    getRunQueue().post(action);
    return true;
}

private HandlerActionQueue getRunQueue() {
    if (mRunQueue == null) {
        mRunQueue = new HandlerActionQueue();
    }
    return mRunQueue;
}
复制代码

可以看到一开始就查询是否有attachInfo,如果有,则用attachInfo.mHandler来执行这个任务。

如果没有attachInfo,则添加到View自己的mRunQueue中。确定运行的线程后,再执行任务。

post(Runnable action)的返回boolean值,如果为true,表示任务被添加到消息队列中了。 如果是false,通常表示消息队列关联的looper正在退出。

那么我们需要了解AttachInfoHandlerActionQueue

AttachInfo

AttachInfoView的静态内部类。View关联到父window后,用这个类来存储一些信息。

AttachInfo存储的一部分信息如下:

  • WindowId mWindowId window的标志
  • View mRootView 最顶部的view
  • Handler mHandler 这个handler可以用来处理任务

HandlerActionQueue

View还没有handler的时候,拿HandlerActionQueue来缓存任务。HandlerAction是它的静态内部类,存储Runnable与延时信息。

public class HandlerActionQueue {
    private HandlerAction[] mActions;
    
    public void post(Runnable action)
    public void executeActions(Handler handler)
    // ...

    private static class HandlerAction {
        final Runnable action;
        final long delay;
        // ...
    }
}
复制代码

View的mRunQueue

将任务(runnable)排成队。当View关联上窗口并且有handler后,再执行这些任务。

/**
 * Queue of pending runnables. Used to postpone calls to post() until this
 * view is attached and has a handler.
 */
private HandlerActionQueue mRunQueue;
复制代码

这个mRunQueue里存储的任务啥时候被执行?我们关注dispatchAttachedToWindow方法。

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    // ...
    // Transfer all pending runnables.
    if (mRunQueue != null) {
        mRunQueue.executeActions(info.mHandler);
        mRunQueue = null;
    }
    // ...
}
复制代码

这个方法里调用了mRunQueue.executeActions

executeActions(Handler handler)方法实际上是用传入的handler处理队列中的任务。

而这个dispatchAttachedToWindow会被ViewGroup中被调用。

或者是ViewRootImpl中调用

host.dispatchAttachedToWindow(mAttachInfo, 0);
复制代码

小结

View的post方法,实际上是使用了AttachInfohandler

如果View当前还没有AttachInfo,则把任务添加到了View自己的HandlerActionQueue队列中,然后在dispatchAttachedToWindow中把任务交给传入的AttachInfohandler。也可以这样认为,View.post用的就是handler.post

我们在获取View的宽高时,会利用View的post方法,就是等View真的关联到window再拿宽高信息。

流程图归纳如下

post-flow1.png


更多Android资料扫码进群即可领取

<img src="https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1704935888404.jpg?t=0.014764499839815759" style="margin: auto" />

### 使用方法 Viewpost 方法允许在视图完成其布局阶段后,将一个任务放到主线程的消息队列中,以便稍后执行。通常用于确保在 View 的尺寸、位置等布局属性已经计算完成后执行某些操作。以下是一个简单的使用示例: ```java import android.os.Bundle; import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = findViewById(R.id.textView); textView.post(new Runnable() { @Override public void run() { // 在这里可以获取 View 的布局属性,如宽度、高度等 int width = textView.getWidth(); int height = textView.getHeight(); // 执行其他操作 } }); } } ``` 在上述代码中,通过 `textView.post()` 方法将一个 `Runnable` 对象添加到主线程的消息队列中,当视图完成布局后,该 `Runnable` 中的代码会被执行,此时可以安全地获取视图的布局属性。 ### 原理 Viewpost 方法最终其实都是发送给 ViewRootImpl 对象的 mHandler 属性,以在主线程得以执行。其具体执行过程如下: - **View 首次布局前**:调用 `view.post()` 时,任务会存入本地队列。 - **绘制流程启动**:`dispatchAttachedToWindow()` 被调用,`AttachInfo` 传递给所有 View。 - **本地队列任务迁移**:任务被发送到窗口 `Handler` 的消息队列尾部。 - **当前绘制周期完成**:测量、布局、绘制全部结束。 - **执行 `View.post()` 的任务**:此时 View 尺寸已经确定,`Runnable` 中的代码会被执行[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值