红橙Darren视频笔记 面试题 为什么view获取宽高为0 onCreate onResume view.post源码浅析(继承activity api27)

面试题

下面的输出分别为多少 为什么

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="abc"
        android:background="#000"/>

</LinearLayout>
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    MyProgressView myProgressView;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv);
        Log.d(TAG, "onCreate: "+textView.getWidth());//1
        textView.post(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "onCreate: post"+textView.getWidth());//2
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: "+textView.getWidth());//3
    }
}

运行结果:

12-10 12:27:10.392 4222-4222/? D/MainActivity: onCreate: 0
12-10 12:27:10.397 4222-4222/? D/MainActivity: onResume: 0
12-10 12:27:10.524 4222-4222/? D/MainActivity: onCreate: post100

原因分析

要想正真获取控件的宽高 必须要等到控件的onMesure方法调用之后才能得到。也就是说在onCreate和onResume中 控件的onMesure都还没有执行。那么为什么textView.post却能得到正确的宽高呢?

下面进入分析为什么onCreate 和onResume不行

首先我们看看activity在onCreate和onResume前都做了什么
ActivityThread

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

        Activity a = performLaunchActivity(r, customIntent);//观察该方法 最终能够调用到activity的onCreate方法

        if (a != null) {
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);//观察该方法 最终能够调用到activity的onResume方法

            if (!r.activity.mFinished && r.startsNotResumed) {
                // The activity manager actually wants this one to start out paused, because it
                // needs to be visible but isn't in the foreground. We accomplish this by going
                // through the normal startup (because activities expect to go through onResume()
                // the first time they run, before their window is displayed), and then pausing it.
                // However, in this case we do -not- need to do the full pause cycle (of freezing
                // and such) because the activity manager assumes it can just retain the current
                // state it has.
                performPauseActivityIfNeeded(r, reason);

                // We need to keep around the original state, in case we need to be created again.
                // But we only do this for pre-Honeycomb apps, which always save their state when
                // pausing, so we can not have them save their state when restarting from a paused
                // state. For HC and later, we want to (and can) let the state be saved as the
                // normal part of stopping the activity.
                if (r.isPreHoneycomb()) {
                    r.state = oldState;
                }
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManager.getService()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
 		...
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
        ...         
        return activity;
    }

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        ...
        r = performResumeActivity(token, clearHide, reason);

        ...
    }

    public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide, String reason) {
        ...
            r.activity.performResume();
        ...
                
        return r;
    }

Instrumentation

public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
    prePerformCreate(activity);
    activity.performCreate(icicle, persistentState);
    postPerformCreate(activity);
}

    /**
     * Perform calling of an activity's {@link Activity#onResume} method.  The
     * default implementation simply calls through to that method.
     * 
     * @param activity The activity being resumed.
     */
    public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();//activity的onResume被调用
        
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    am.match(activity, activity, activity.getIntent());
                }
            }
        }
    }

Activity

    @UnsupportedAppUsage
    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        dispatchActivityPreCreated(icicle);
        mCanEnterPictureInPicture = true;
        restoreHasCurrentPermissionRequest(icicle);
        //onCreate方法被调用
        if (persistentState != null) {
            onCreate(icicle, persistentState);
        } else {
            onCreate(icicle);
        }
        writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
        mActivityTransitionState.readState(icicle);

        mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                com.android.internal.R.styleable.Window_windowNoDisplay, false);
        mFragments.dispatchActivityCreated();
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
        dispatchActivityPostCreated(icicle);
    }

    final void performResume(boolean followedByPause, String reason) {
        ...
        // mResumed is set by the instrumentation
        mInstrumentation.callActivityOnResume(this);//关键
        ...

        // Now really resume, and install the current status bar and menu.
        mCalled = false;

        mFragments.dispatchResume();
        mFragments.execPendingActions();

        onPostResume();
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
        dispatchActivityPostResumed();
    }

小结
ActivityThread handleLaunchActivity
–>performLaunchActivity–>Instrumentation callActivityOnCreate–>Activity performCreate–>onCreate
–>handleResumeActivity–>performResumeActivity–>Activity performResume–>Instrumentation callActivityOnResume–>Activity onResume
到目前为止 没有调用测量方法 那么是什么时候开始测量的呢
再看看
ActivityThread handleResumeActivity调用 performResumeActivity的后面部分

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        
        r = performResumeActivity(token, clearHide, reason);

        ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//这里更新view
                } 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;
        }

    ...
        if (r.activity.mVisibleFromClient) {
            ViewManager wm = a.getWindowManager();
            View decor = r.window.getDecorView();
            wm.updateViewLayout(decor, l);这里更新view
        }
    }

    r.activity.mVisibleFromServer = true;
    mNumVisibleActivities++;
    if (r.activity.mVisibleFromClient) {
        r.activity.makeVisible();
    }
    }

    if (!r.onlyLocalRequest) {
        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(
            TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }
    r.onlyLocalRequest = false;

    // Tell the activity manager we have resumed.
    if (reallyResume) {
        try {
            ActivityManager.getService().activityResumed(token);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

    } else {
        ...
        }
    }

要想view测量出真正的数据 至少要在addview之后才行 根据我们的分析 在调用onResume的时候 还没有addview
至于setContentView中做了什么 可以参考这一文章
https://blog.csdn.net/u011109881/article/details/111085949

下面进入分析为什么view.post可以

View.java

    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();//action没有立即执行 而是放在HandlerActionQueue里面了
        }
        return mRunQueue;
    }
    
   void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);//action执行时间
            mRunQueue = null;
        }
        performCollectViewAttributes(mAttachInfo, visibility);
        onAttachedToWindow();

       
        }

        int vis = info.mWindowVisibility;
        if (vis != GONE) {
            onWindowVisibilityChanged(vis);
            if (isShown()) {
                // Calling onVisibilityAggregated directly here since the subtree will also
                // receive dispatchAttachedToWindow and this same call
                onVisibilityAggregated(vis == VISIBLE);
            }
    	}
    }

从上面看 我们实际post的测量在dispatchAttachedToWindow中进行 实际上此时已经测量完毕。之后我在记录view的绘制流程时在详述为什么测量是在dispatchAttachedToWindow之前进行的(立个flag。。。)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值