Activityの起動メカニズム
9794 ワード
概要
最近面接に行って、Activityの起動メカニズムを聞かれて、急に呆然とした顔をして、何の鬼ですか!帰ってきたらソースを見て、Activity起動に関する知識を学ぶことにしました.
起動プロセスの説明
Activityの起動はActivity Threadから始まり、Activity Threadのコードを直接見てみましょう.コードが長すぎて重点だけを選んで見る
public static void main(String[] args) {
....( )....
// Looper , UI handler, prepare() loop()。
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ここはActivity Threadの始まりですが、Activityは?Activity ThreadにHandlerの実装があり、中にはLAUNCHが届いています.ACTIVITYメッセージ後にhandleLaunchActivity(r,null)を呼び出して処理開始関連事項に入ります.この関数を見てみましょう
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
....( )....
// Initialize before creating the activity
// WindowManagerGlobal, addView , window , ,
WindowManagerGlobal.initialize();
// performLaunchActivity Activity
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
// Activity resume,create ? performActivity
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
....( )....
}
} else {
// If there was an error, for any reason, tell the activity
// manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
// Ignore
}
}
}
performLaunchActivityによるActivityの作成に成功するとhandleResumeActivityが実行され、ActivityのonResume状態に入ります.初期化の作成手順はperformLaunchActivityの具体的な実装を見てみましょう.
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
....( )....
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
// mInstrumentation Activity , Activity mInstrumentation
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
....( )....
if (activity != null) {
....( )....
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);
....( )....
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
}
....( )....
return activity;
}
attachの後でcallActivity OnCreateを呼び出して、ActivityのonCreate状態に入って、それでは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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// window , window activity
mWindow = new PhoneWindow(this);
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);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
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();
mCurrentConfig = config;
}
実はこのコードはwindowを初期化して、後にactivityの内容をロードする準備をして、さっきも言ったように、attach後にActivityのonCreate状態に入って、onCreateの中で私たちのよくあるsetContentViewを呼び出しました.
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
setContentViewは実はさっきattachで初期化したwindowのsetContentViewメソッドを呼び出して、PhoneWindowのsetContentViewの実現を見てみましょう
@Override
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) {
// DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
// layout mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
....( )....
}
}
protected ViewGroup generateLayout(DecorView decor) {
....( )....
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
....( )....
}
setContentViewの場合、installDecorメソッドとmLayoutInflaterが呼び出されます.inflate(layoutResID, mContentParent);私たちのレイアウトをmContentParentにロードします.しかし、mContentParentはどこから来ましたか?installDecorではgenerateLayoutで私たちのコンテンツエリアを追加し、よく知っているIDを見ました.ANDROID_CONTENTは、私たちのコンテンツエリアです.
まとめ
これらのコードを見た後、Activityの起動プロセスについて大まかな理解が得られました.総じてIPCでAMSのstartActivityを呼び出し、Activity ThreadのhandleLanchActivivytでwindowを初期化し、実際にはPhoneWindowです.それからActivityのonCreateを呼び出して、setContentViewはphoneWindowを呼び出すsetContentViewで、中はdecorViewを初期化して、decorViewはphoneWindowの変数で、FrameLayoutから継承して、私たちのview全体のルートレイアウトで、中にContentView Groupの領域が私たちのlayoutをロードするために使用されています.私たちのLayoutをロードした後、WindowManagerでwindowを追加し、インタフェースに表示します.Windowsは透明な窓領域と言えるが、decorViewは窓の枠で、固定されたもの(ActionBar、Title)、layoutは私のカスタマイズした窓で、彫刻やシールができる.自分の理解は、喜ばないで噴き出さないでください.