前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java中的onresume_android onCreate onResume中获取 View 宽高为0分析

java中的onresume_android onCreate onResume中获取 View 宽高为0分析

作者头像
全栈程序员站长
发布2022-09-05 10:12:18
3660
发布2022-09-05 10:12:18
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

1、问题测试

xmlns:tools=”http://schemas.android.com/tools”

android:layout_width=”match_parent”

android:layout_height=”match_parent”>

android:id=”@+id/btn”

android:layout_width=”100dp”

android:layout_height=”40dp” />

MainActivity.java

public class MainActivity extends AppCompatActivity {

private Button mBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mBtn = (Button) findViewById(R.id.btn);

Log.d(“TAG”, “onCreate() button width=” + mBtn.getWidth());

}

@Override

protected void onResume() {

super.onResume();

mBtn.post(new Runnable() {

@Override

public void run() {

Log.d(“TAG”, “onResume() mBtn post button width=” + mBtn.getWidth());

}

});

new Handler().post(new Runnable() {

@Override

public void run() {

Log.d(“TAG”, “onResume() Handler button width=” + mBtn.getWidth());

}

});

}

}

log 结果:

image.png

根据上面的结果回产生4个疑问:

1、setContentView后获取控件的宽高为什么是0;

2、在 onResume中 handler.post 中获取控件的宽高为什么是0;

3、在 onResume中的 view.post 中为什么能获取控件宽高;

4、在 onResume 中handler.post 在 View.post 后面为什么执行反而在前面;

针对以上4个疑问进行解答

1、setContentView后获取控件的宽高为什么为0;

这个很好理解, setContentView只是解析了 xml 文件并创建了对应的控件,并没有进行控件的测量等工作;

2、在 onResume中 handler.post 中获取控件的宽高为什么是0;

ActivityThread.java类中handleResumeActivity函数

@Override

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,

String reason) {

…………

// TODO Push resumeArgs into the activity for consideration

final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

if (r == null) {

// We didn’t actually resume the activity, so skipping any follow-up actions.

return;

}

…………

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;

// Normally the ViewRoot sets up callbacks with the Activity

// in addView->ViewRootImpl#setView. If we are instead reusing

// the decor view we have to notify the view root that the

// callbacks may have changed.

ViewRootImpl impl = decor.getViewRootImpl();

if (impl != null) {

impl.notifyChildRebuilt();

}

}

if (a.mVisibleFromClient) {

if (!a.mWindowAdded) {

a.mWindowAdded = true;

wm.addView(decor, l);

} else {

// The activity will get a callback for this {@link LayoutParams} change

// earlier. However, at that time the decor will not be set (this is set

// in this method), so no action will be taken. This call ensures the

// callback occurs with the decor set.

a.onWindowAttributesChanged(l);

}

}

// If the window has already been added, but during resume

// we started another activity, then don’t yet make the

// window visible.

} else if (!willBeVisible) {

if (localLOGV) Slog.v(TAG, “Launch ” + r + ” mStartedActivity set”);

r.hideForNow = true;

}

…………..

}

Activity.onResume是在performResumeActivity中调用,此时还没有执行wm.addView(decor, l)创建 ViewRootImpl ,同时在 ViewRootImpl中的doTraversal()是通过mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)发送异步消息等待下一个刷新消息才执行。所以 handler.post 消息回先执行导致获取 view 宽高失败。

ViewRootImpl.java中

@UnsupportedAppUsage

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

if (!mUnbufferedInputDispatch) {

scheduleConsumeBatchedInput();

}

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

3、在 onResume中的 view.post 中为什么能获取控件宽高;

View.java 中的 post()

public boolean post(Runnable action) {

//mAttachInfo 是在 ViewRootImpl 的构造函数中初始化的

//而 ViewRootmpl 的初始化是在 addView() 中调用

//所以此处的 mAttachInfo 为空,所以不会执行该 if 语句

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.

//保存消息到 RunQueue 中,等到在 performTraversals() 方法中被执行

getRunQueue().post(action);

return true;

}

通过第2点可以知道ViewRootImpl 的创建是在onResume 之后,所以此时attachInfo == null,从而消息被保存到RunQueue中,而RunQueue在ViewRootImpl的performTraversals被中执行,所以可以获取到控件宽高。

private void performTraversals(){

// Execute enqueued actions on every traversal in case a detached view enqueued an action

getRunQueue().executeActions(mAttachInfo.mHandler);

}

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;

}

}

4、在 onResume 中handler.post 在 View.post 后面为什么执行反而在前面;

通过上面第2点和点3点分析可以知道View.post的在后面performTraversals中被执行,而handler.post在performTraversals之前就被执行

image.png

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/137266.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年6月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档