Android View#post()源码分析

Android View#post()源码分析

概述

在 Activity 中,在 onCreate() 和 onResume() 中是无法获取 View 的宽高,可以通过 View#post() 获取 View 的宽高。

onCreate和onResume不能获取View的宽高

Activity 的生命周期都是在 ActivityThread 中,当调用 startActivity() ,最终会调用 ActivityThread#performLaunchActivity()。

// ActivityThread#performLaunchActivity()
// 核心代码:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    Activity activity = null;

    java.lang.ClassLoader cl = appContext.getClassLoader();
    // 通过反射新建一个Activity
    activity = mInstrumentation.newActivity(
        cl, component.getClassName(), r.intent);


    try {
         

        if (activity != null) {
            // 创建并初始化Window
            activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstances, config,
                            r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
                            r.assistToken, r.shareableActivityToken);
            
            r.activity = activity;
            if (r.isPersistable()) {
                // 回调Activity#onCreate()
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }            
        } 
    }
    return activity;
}
// ActivityThread#handleResumeActivity()
// 核心代码:
public void handleResumeActivity()(ActivityClientRecord r, boolean finalStateRequest,
        boolean isForward, String reason) {
    // 最终会回调Activity#onResume()
    if (!performResumeActivity(r, finalStateRequest, reason)) {
        return;
    }
    
    final Activity a = r.activity;

    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE); // 设置不可见
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        if (r.mPreserveWindow) {
            a.mWindowAdded = true;
            r.mPreserveWindow = false;
            ViewRootImpl impl = decor.getViewRootImpl();
            if (impl != null) {
                impl.notifyChildRebuilt();
            }
        }
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                // 添加View,开始View的操作
                wm.addView(decor, l);
            } else { 
                a.onWindowAttributesChanged(l);
            }
        } 
    }   
}

说明:Activity 先执行 onCreate(),再执行 onResume(),最后才调用 wm.addView() 将 DecorView 添加到视图中,也就是从这里才开始执行 View 测量布局绘制流程。简单说 View 的流程晚于 onResume()。

post可以获取View的宽高

// View#post()
public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        // 如果attachInfo不为null,表示View已经添加到Window,直接通过Handler发送到主线程队列
        return attachInfo.mHandler.post(action);
    }

    // 如果attachInfo为null,表示View未添加到Window,暂存在mRunQueue中
    getRunQueue().post(action);
    return true;
}

private HandlerActionQueue getRunQueue() {
    if (mRunQueue == null) {
        mRunQueue = new HandlerActionQueue();
    }
    return mRunQueue;
}

说明:在 onCreate() 和 onResume() 中调用 View#post(),最终都会调用 getRunQueue().post(action)。

wm.addView(decor, l) 执行流程:

  • 调用 WindowManagerImpl#addView()。
  • 调用 WindowManagerGlobal#addView()。
  • 执行 new ViewRootImpl,创建 root 对象(布局管理器),并在内部创建 mAttachInfo 对象、Handler 对象。
  • 调用 ViewRootImpl#setView()。
// ViewRootImpl#setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
			// 请求布局,最终调用ViewRootImpl#performTraversals()
            requestLayout();           
            try {
                // 通过Binder调用WMS添加Window
                res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), userId,
                        mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                        mTempControls);               
            }   
        }
    }
}
// ViewRootImpl#performTraversals()
private void performTraversals() {
    final View host = mView;
    // 调用View#dispatchAttachedToWindow()分发mAttachInfo    
    host.dispatchAttachedToWindow(mAttachInfo, 0);
	// 测量流程
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
	// 布局流程
    performLayout(lp, mWidth, mHeight);
	// 绘制流程
    performDraw()
}
// View#dispatchAttachedToWindow()
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mAttachInfo = info;
   	// 执行缓存任务
    if (mRunQueue != null) {
        mRunQueue.executeActions(info.mHandler);
        mRunQueue = null;
    }  
}
// HandlerActionQueue#executeActions()
public class HandlerActionQueue {
    private HandlerAction[] mActions;
    
    public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }
}

说明:执行 mRunQueue.executeActions(),会将所有缓存的任务发送到 handler 中,等待主线程执行完 performTraversals() 方法后,就会执行 mActions 中的任务,这时就可以获取到 View 的宽高。

总结

执行流程:

  • Activity#onCreate()
  • Activity#onResume()
  • WindowManagerImpl#addView()
  • new ViewRootImpl()
  • ViewRootImpl#setView()
  • View的测量流程
  • View的布局流程
  • View的绘制流程
  • WMS添加Window
  • 获取View的宽高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值