大家好,又见面了,我是你们的朋友全栈君。
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
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/137266.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...