當咱們調用startActivity方法來啓動某個Activity時,系統須要根據該Activity所在進程是否存在而作不一樣的處理,若是不存在,則須要AMS經過socket向Zygote大佬發起進程fork的請求,接着再從fork出來的子進程中run對應APP進程的ActivityThread main方法。這個過程能夠看本文序列文章《我是怎麼把一個個APP進程建立起來的?》。java
而在這以後,就開始了Activity的建立。這個過程能夠看《Activity是如何顯示出來的?(上)》。android
這一章節承接上一個章節《Activity是如何顯示出來的?》繼續分析。其實標題更改成《Activity是如何建立出來的?》可能更爲合適。微信
在開始分析以前,咱們先來張圖大概看看整個流程,方便你們理解。(這個圖來自網絡,大部分博文都有,已不清楚具體的出處,如有做者看到,能夠告知!)網絡
每一個APP的入口都是ActivityThread的main方法,而在main方法中,主要作了三件事:app
1.建立Looper,Handler,Message,用於消息處理,後期AMS對activity的生命週期管理都得間接經過Handler來處理;
2.建立ApplicationThread對象,這個是ActivityThread的內部類,也是個Binder服務端,極其重要,是Activity對外跟AMS通信的接口。
3.建立Activity對象,並調用onCreate方法,view的建立在此開啓。
複製代碼
承接上個章節,從這裏開始:socket
//frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
....
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
....
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.configCallback);
....
mInstrumentation.callActivityOnPostCreate(activity, r.state);
....
}
複製代碼
callActivityOnPostCreate方法最終會調用到activity的onCreate方法,而onCreate方法中,咱們常見的會調用的方法爲setContentView方法,從字面上理解,該方法正是跟設置view相關。進入查看:ide
frameworks/base/core/java/android/app/Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
Activity中涉及到的setContentView方法有三個,只是各自傳參不一樣,咱們挑選了其中一個比較經常使用的方法進行分析。方法很簡單,只有兩行代碼,這裏轉身調用getWindow中的方法來進行實現。getWindow方法最終返回的是一個Window對象:oop
public Window getWindow() {
return mWindow;
}
複製代碼
mWindow是Activity的一個成員變量,這是什麼時候被建立的呢?答案在Activity的attach方法中。還記得在調用activity的onCreate方法前,先調用了Activity的attach方法麼?咱們看看它的實現:佈局
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, ActivityConfigCallback activityConfigCallback) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
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());
}
mWindowManager = mWindow.getWindowManager();
...
}
複製代碼
mWindow變量是一個Window對象,但Window類只是一個抽象類,具體的實現方法倒是在PhoneWindow類中:post
mWindow = new PhoneWindow(this, window, activityConfigCallback);
複製代碼
這裏還稍微帶出了mWindowManager,這是個WindowManager類型的對象,用來跟WMS通信,後面會繼續介紹。
回到setContentView方法上:
getWindow().setContentView(layoutResID);
複製代碼
經過上面的分析,咱們知道getWindow方法返回的實際上是個PhoneWindow對象,那麼setContentView的方法實現也是在這裏:
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor(); //建立DecorView對象和ContentParent對象
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//若是setContentView方法不是首次調用,這裏須要清除ContentParent對象的舊的內容
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//將view佈局文件填充到mContentParent這個容器中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback(); //獲取activity回調
if (cb != null && !isDestroyed()) {
cb.onContentChanged(); //通知Activity佈局改變了
}
mContentParentExplicitlySet = true;
}
複製代碼
在應用界面開發的時候,咱們經過一個xml文件來描述整個界面。當Activity首次調用setContentView方法時,須要先建立DecorView對象和ContentParent對象,並將二者進行關聯。此時的ContentParent對象的內容仍是空的。須要經過inflate方法將佈局文件內容填充到ContentParent對象中。
installDecor的方法比較長,主要關注兩個地方:
private void installDecor() {
...
mDecor = generateDecor(-1); //建立DecorView對象
...
mContentParent = generateLayout(mDecor); //建立ContentParent對象
...
}
複製代碼
該方法主要作了兩件事,建立DecorView對象和ContentParent對象。其中generateDecor方法比較簡單,直接經過new DecorView()的方法返回一個DecorView對象。主要看看generateLayout方法:
protected ViewGroup generateLayout(DecorView decor) {
//獲取窗口風格
TypedArray a = getWindowStyle();
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
...
int layoutResource;
int features = getLocalFeatures();
//以下部分是根據具體的風格,爲layoutresource挑選匹配的資源
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
...
}
...
mDecor.startChanging();
//這裏會對匹配到的資源進行解析,並將資源加載到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//經過findViewById的方法獲取ContentParent對象,待填充內容
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
mDecor.finishChanging();
return contentParent;
}
複製代碼
generateLayout方法中,主要作了四件事:
1.獲取窗口風格;
2.根據具體的風格,爲layoutresource挑選匹配的資源;
3.將獲取到的資源加載到DecorView中,其中包括id爲ID_ANDROID_CONTENT的FrameLayout類型的容器,此時內容爲空,待填充;
4.經過findViewById的方法獲取ContentParent對象;
複製代碼
稍微總結下setContentView方法,若是是第一次調用,則先建立DecorView對象,在這個過程當中,會建立一個ContentParent對象,這是一個容器,用於填充用戶自定義的view內容。接着經過回調的方法,通知activity,我這邊已經將view內容填充完畢。
使用一張圖大概描述下DecorView與ContentParent的關係:
Activity的onCreate流程執行完,還不能顯示出來具體的view內容。要想顯示出來,還須要走onResume流程。在上篇文章中,咱們知道在realStartActivityLocked方法中,建立了一個加載Activity的事務,等一切就緒,就處理該事務。其實此時在該事務中還會設置activity最終須要顯示的狀態:resume or pause。具體以下:
//frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
...
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
...
}
複製代碼
若是咱們想要啓動一個activity,則此時咱們走的是resume的分支,即此時clientTransaction會被設置一個ResumeActivityItem類型的lifecycleItem 。在TransactionExecutor類中,執行完LaunchActivityItem後,就會執行resume流程:
/frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
executeCallbacks(transaction); //這裏最終會執行onCreate流程
executeLifecycleState(transaction); //這裏最終會執行onResume流程
mPendingActions.clear();
log("End resolving transaction");
}
private void executeLifecycleState(ClientTransaction transaction) {
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
...
// Execute the final transition with proper parameters.
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}
複製代碼
這裏實際上執行了ResumeActivityItem的execute方法,最終調用到了ActivityThread的handleResumeActivity方法:
//frameworks/base/core/java/android/app/ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
//最終調用activity的onResume方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
...
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 (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
...
}
複製代碼
handleResumeActivity方法中先獲取DecorView對象(在serContentView的方法中建立),而後經過WindowManager方法添加到窗口中,接着再調用makeVisible方法顯示出來。
//frameworks/base/core/java/android/app/Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
複製代碼
用兩個章節稍微走了一遍activity的建立過程,期間咱們大概瞭解到,AMS把控着activity的生命週期管理,經過IApplicationThread跟Activity進行通信, ApplicationThread做爲ActivityThread的內部類,充滿各類各樣的schedule方法,提供給AMS使用。ApplicationThread自己只是個通信接口,不作具體的業務處理,經過Handler將具體的處理放到了ActivityThread中。
onCreate流程建立了DecorView對象和ContentParent對象,並將二者進行了關聯,關聯的關鍵是id號爲ID_ANDROID_CONTENT的FrameLayout類型的容器。最後將開發者自定義的view填充到了mContentParent這個容器中。以後則開始了onResume流程,在這裏,最終調用makeVisible方法顯示出整個界面,包括標題欄和開發者自定義的view。
從前面的分析也看到,顯示的關鍵在於addView和makeVisible,前者會將view添加到窗口中,這個過程會涉及到跟WMS的通信。下個章節則會開始這一塊的分析。
我在微信公衆號也有寫文章,更新比較及時,有興趣者能夠掃描以下二維碼,或者微信搜索【Android系統實戰開發】,關注有驚喜哦!