寫在前面的話
今天有點煩,有點煩。項目寫的亂成團,改起需求真要完。此后當個加班狗,無錢無名心要寬。
昨晚寫到十一點,我都差點不相信這是我自己了。
今天接著昨天的節奏來,準備寫下關于Activity從創建到顯示的整個過程。
1. Activity的attach方法
之前分析過Activity的生命周期具體調用時機,我們知道Activity是通過反射創建出來的,之后會執行attach方法:
ActivityThread.java:
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);
Activity.java:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
//把context賦值給mBase,可以通過getBaseContext獲取到context
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//創建了一個窗口
mWindow = new PhoneWindow(this, window);
//這個好像是畫中畫的callback
mWindow.setWindowControllerCallback(this);
//設置callback,里面有各種事件包括鍵盤、觸摸等事件的回調
mWindow.setCallback(this);
//設置窗口消失的回調
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
//與輸入法有關的設置
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
//ui線程
mUiThread = Thread.currentThread();
//主線程
mMainThread = aThread;
//之前說到的小秘書
mInstrumentation = instr;
//binder
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
......
//設置(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
//WindowManager
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
attach方法大部分都是在做成員變量的賦值操作,比如上下文,主線程和UI線程。里面比較重要的一點是對Window的創建,我們都知道Activity的layout是顯示在窗口上面的,這個PhoneWindow就是我們的窗口。可以看下其構造方法:
PhoneWindow.java:
public PhoneWindow(Context context) {
super(context);
//初始化mLayoutInflater layout加載器
mLayoutInflater = LayoutInflater.from(context);
}
public PhoneWindow(Context context, Window preservedWindow) {
this(context);
mUseDecorContext = true;
//傳過來的preservedWindow為null
if (preservedWindow != null) {
......
}
boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
//是否支持畫中畫
mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_PICTURE_IN_PICTURE);
}
Window.java:
public Window(Context context) {
//把上下文賦值,并設置默認的特征
mContext = context;
mFeatures = mLocalFeatures = getDefaultFeatures(context);
}
attach到這邊基本上就結束了。
2. Activity的onCreate
按照上篇講的,在attach完成后,會執行Activity的onCreate方法。
ActivityThread.java:
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);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
Activity.java:
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
//FragmentController分發Create事件
mFragments.dispatchCreate();
//application分發ActivityCreated事件
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
從上面的onCreate中可以看到,這個過程并沒有做太多的操作,只有當前create的事件分發。
3. Activity的onStart和onResume
在執行完Activity的onCreate方法就會執行Activity的onStart方法,onStart方法更簡單。。。
protected void onStart() {
mCalled = true;
mFragments.doLoaderStart();
getApplication().dispatchActivityStarted(this);
}
看完onStart,就可以猜出onResume做了什么。沒錯,就是:
protected void onResume() {
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
mCalled = true;
}
看到這里,其實會有一點點懵逼的。前人總是說,onStart代表著Activity可見了,onResume代表著可以交互了。但是到這里了,我才發現如何把View添加到窗口上的?什么時候添加的?繪制的時間呢?古人誠但欺我。
到了這里,希望得到的答案并沒有出現,所以繼續分析。
4. 真正的添加過程
真正添加的過程其實在執行完performResumeActivity這個方法后,系統根據該Activity是否要顯示來設置頁面是否需要添加到窗口上,其中最主要的是
wm.addView(decor, l)
這句代碼,通過WindowManager將view和LayoutParams添加到窗口上,并且顯示出來(這部分后面會有分析)。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
......
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
//將要顯示
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//這個條件a.mFinished false r.window == null willBeVisible = true
if (r.window == null && !a.mFinished && willBeVisible) {
//這個r.activity.getWindow()就是我們attach中創建的PhoneWindow
r.window = r.activity.getWindow();
//獲得DecorView,這里暫時理解為一個View吧,后面還會有分析的
View decor = r.window.getDecorView();
//DecorView設為INVISIBLE
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;
//false(在創建這個對象的時候沒有賦值)
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
//mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(com.android.internal.R.styleable.Window_windowNoDisplay, false);
//如果Activity沒有設置NoDisplay,則這個mVisibleFromClient變量是true
//mWindowAdded這個值是默認的,只有被add后才會變為true
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
} else if (!willBeVisible) {
r.hideForNow = true;
}
cleanUpPendingRemoveWindows(r, false /* force */);
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
r.newConfig = null;
}
WindowManager.LayoutParams l = r.window.getAttributes();
......
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
//這里又執行了makeVisible,因為我們在上面已經add了,mWindowAdded這個變量為true,所以不會進行多次add。
//最后decorView設置為顯示
//void makeVisible() {
// if (!mWindowAdded) {
// ViewManager wm = getWindowManager();
// wm.addView(mDecor, getWindow().getAttributes());
// mWindowAdded = true;
// }
// mDecor.setVisibility(View.VISIBLE);
//}
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
//這兩天才了解到這個addIdleHandler是指空閑時處理的消息,MessageQueue有專門的接口MessageQueue.IdleHandler
//這里之前說道會執行前個頁面的onStop
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
} else {
//出現問題就finish
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
5. 大致要總結下
從上面的分析過程中,我們可以知道Activity在創建的過程中,onCreate、onStart以及onResume其實都沒有關于對頁面顯示的操作,真正顯示頁面是在onResmue之后,WindowManager將decorView添加后進行繪制(后面應該會將)。
今天也恰巧看到了一篇講解關于MessageQueue.IdleHandler
的文章,這個IdleHandler會在線程空閑的時候,指定一個操作。使用這個IdleHandler對于某些延時操作,但又不清楚頁面是否真正繪制完成有奇效。
6. 寫在后面的話
這些天講了從Android啟動到HomeActivity的啟動,又從HomeActivity啟動過程開始分析Activity的生命周期,接著這篇講解了Activity從創建到顯示的過程,但是并沒有完全完成。這里只講到了執行這些方法會將頁面顯示,但是具體如何顯示沒有說明。接下來應該將的就是這個頁面如何繪制并展示出來。就這樣。