blog

アクティビティはどのように作成されるのか?

Activityを開始するメソッドを呼び出すとき、Activityのプロセスが存在するかどうかによって、システムは異なる処理を行う必要があります。 存在しない場合、AMSはソケット経由でZygoteに...

Jun 26, 2020 · 9 min. read
シェア

はじめに

startActivityメソッドを呼び出してActivityを開始する場合、Activityプロセスが存在するか否かによって異なる処理を行う必要があります。存在しない場合、AMSはソケットを介してZygoteの兄貴分にプロセスフォーク要求を開始し、フォークされた子プロセスから対応するAPPプロセスを実行する必要があります。そして、フォークされた子プロセスから対応するAPPプロセスのActivityThreadメインメソッドを実行します。この処理は、この記事の一連の記事「APPプロセスを1つずつ作成するには?アプリプロセスを1つずつ作成するには?

そしてこの後、アクティビティの作成が始まります。このプロセスは「アクティビティはどのように表示されるのか?.

このセクションでは、前セクション「アクティビティはどのように表示されるのか?の分析を続けます。実際には、タイトルを「アクティビティはどのように作成されるのか?の方が適切かもしれません。

分析の最初に、まず、全体のプロセスを大まかに見るための図、理解しやすい。

概要

すべてのアプリのエントリーポイントはActivityThreadのmainメソッドで、mainメソッドでは主に3つのことが行われます:

1.メッセージ処理のために、Looper、Handler、Messageを作成し、後のAMSのアクティビティライフサイクル管理は、Handlerを介して間接的に処理する必要がある;
2.アプリケーション Threadオブジェクトを作成する。これはアクティビティ Threadの内部クラスであり、Binderサーバーでもある。アクティビティがAMSと通信するためのインターフェイスである。
3.Activityオブジェクトを作成し、onCreateメソッドを呼び出す。

前章に引き続き、ここから始めてください:

//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);
 ....
}

onCreate

callActivityOnPostCreateメソッドは、最終的にアクティビティのonCreateメソッドを呼び出しますが、onCreateメソッドで共通に呼び出されるメソッドは、文字通りビューの設定に関連するsetContentViewメソッドです。ビューを入力します:

frameworks/base/core/java/android/app/Activity.java
public void setContentView(@LayoutRes int layoutResID) {
 getWindow().setContentView(layoutResID);
 initWindowDecorActionBar();
}

setContentViewメソッドに関与するアクティビティは3つありますが、ちょうどそれぞれが異なる参照を渡し、分析のためのより一般的に使用されるメソッドのいずれかを選びました。メソッドは非常にシンプルで、わずか2行のコードは、ここで実装するgetWindowメソッドを呼び出すようになります。 getWindowメソッドは、最終的にWindowオブジェクトを返します:

public Window getWindow() {
 return mWindow;
}

mWindowはアクティビティのメンバ変数です。答えはアクティビティのattachメソッドにあります。アクティビティのattachメソッドは、アクティビティのonCreateメソッドを呼び出す前に呼び出されることを覚えていますか?その実装を見てみましょう:

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クラスにあります:

mWindow = new PhoneWindow(this, window, activityConfigCallback);

mWindowManagerはWMSとの通信に使用されるWindowManager型のオブジェクトです。

setContentView

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 {
 //ビューレイアウトファイルをmContentParentコンテナに入れる。
 mLayoutInflater.inflate(layoutResID, mContentParent);
 }
 mContentParent.requestApplyInsets();
 final Callback cb = getCallback(); //アクティビティコールバックの取得
 if (cb != null && !isDestroyed()) {
 cb.onContentChanged(); //レイアウトが変更されたことをアクティビティに通知する
 }
 mContentParentExplicitlySet = true;
}

アプリケーションのインターフェースを開発する場合、インターフェース全体はxmlファイルを通して記述されます。Activityが初めてsetContentViewメソッドを呼び出すとき、まずDecorViewオブジェクトとContentParentオブジェクトを生成し、関連付ける必要があります。このとき、ContentParentオブジェクトの中身は空のままです。inflateメソッドでContentParentオブジェクトにレイアウトファイルの内容を入れる必要があります。

installDecor

installDecorメソッドはかなり長く、主に2つの領域に焦点を当てています:

private void installDecor() {
 ...
 mDecor = generateDecor(-1); //DecorViewを作成する
 ...
 mContentParent = generateLayout(mDecor); //ContentParentオブジェクトの作成
 ...
}

このメソッドは主に2つのことを行います、createDecorViewオブジェクトとContentParentオブジェクトです。generateDecorメソッドは比較的簡単で、直接newDecorView()メソッドを通して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メソッドは主に4つのことを行います:

1.ウィンドウスタイルを取得する;
2.特定のスタイルに従って、レイアウト・リソースがマッチングされる;
3.取得したリソースをDecorViewにロードする。_ANDROID_CONTENTFrameLayout型のコンテナは空であり、充填される;
4.findViewByIdメソッドでContentParentオブジェクトを取得する;

setContentViewメソッドを少し要約すると、初めて呼び出された場合、まずDecorViewオブジェクトを作成し、その過程でContentParentオブジェクトを作成します。そして、コールバックメソッドを通して、アクティビティに通知し、ビューコンテンツを満たします。

DecorViewとContentParentの関係を図で大まかに説明します:

表示

アクティビティのonCreate処理が実行された後、特定のビューコンテンツを表示することはできません。表示するには、onResume処理を実行する必要があります。前回の記事で、realStartActivityLockedメソッドで、アクティビティを読み込むためのトランザクションが作成され、準備が整ったらトランザクションの処理が行われることがわかりました。実際には、トランザクション内のこの時点で、また、最終的に状態を表示するために必要なアクティビティを設定します:再開または一時停止:

//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);
 ...
}
/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);
}
//frameworks/base/core/java/android/app/ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
 String reason) {
 
 ...
 //アクティビティの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();
 }
 ...
}
//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);
}

結論

AMSはActivityThreadの内部クラスであるIApplicationThreadを通じてActivityと通信し、AMSが使用する様々なスケジュールメソッドを提供します。ApplicationThreadはActivityThreadの内部クラスであり、AMSが使用するための様々なスケジュールメソッドで満たされています。ApplicationThread自体は単なる通信インターフェースであり、具体的な業務処理は行わず、具体的な処理はHandlerを通してActivityThreadに委ねられます。

onCreateプロセスでは、DecorViewオブジェクトとContentParentオブジェクトを作成し、2つを関連付けます。最後に、開発者のカスタムビューがmContentParentコンテナに入力されます。その後、onResume処理が始まり、最後にmakeVisibleメソッドが呼び出され、タイトルバーと開発者のカスタムビューを含むインターフェイス全体が表示されます。

前の分析からわかるように、表示の鍵はaddViewとmakeVisibleで、前者はウィンドウに表示を追加し、この処理はWMSとの通信を伴います。次のセクションでは、この部分の分析に入ります。

Read next