1. ホーム
  2. Android

Android デフォルトのホームアプリケーション(Launcher)起動プロセスのソースコード解析

2022-02-18 03:11:04

        前回は、Androidシステムの起動時にアプリがインストールされる過程を分析しました。これらのアプリがインストールされた後、それらをデスクトップに表示するためにホームアプリが必要になりますが、Androidシステムでは、このデフォルトのホームアプリがLauncherにあたります。この記事では、Launcherアプリの起動プロセスについて詳しく分析します。

書籍「"Androidソースコードシナリオ解析"」は、Attack of the Programmersのウェブサイト( http://0xcc0xcd.com をクリックし、お入りください。

        Android用ホームアプリケーションLauncherは、PackageManagerServiceと同様に、起動時にSystemServerコンポーネントによって起動されるActivityManagerServiceによって起動されます。SystemServerコンポーネントは、まず、以前の記事で説明したように、システムのアプリケーションのインストールを担当するePackageManagerServicを起動します。 Androidアプリケーションインストール処理のソースコード解析 システムアプリケーションのインストールが完了すると、SystemServerコンポーネントはActivityManagerServiceを介してホームアプリケーションLauncherを起動します。Launcherは、起動時にPackageManagerServicを介して、インストールされたアプリケーションをデスクトップにショートカットアイコンとして表示し、ユーザーが利用できるようにします(下図参照)。


クリックで拡大画像

        以下、各ステップについて詳しく分析する。

        ステップ1.systemServer.main

        この関数は、前回の記事で紹介した frameworks/base/services/java/com/android/server/SystemServer.java ファイルに定義されています。 Androidアプリケーションインストール時のソースコード解析 は、ステップ1についてです。

        ステップ2.systemServer.init1

        この関数は、前回の記事で紹介した frameworks/base/services/jni/com_android_server_SystemServer.cpp ファイルに実装された JNI メソッドです。 Androidアプリケーションインストール時のソースコード解析 をステップ2へ。

        ステップ3. libsystem_server.system_init

        system_init 関数は、libsystem_server ライブラリに実装されており、ソースコードは framework/base/cmds/system_server/library/system_init.cpp ファイルにあり、前回の記事で紹介した Androidアプリケーションインストール時のソースコード解析 をステップ3へ。

        ステップ4. androidRuntime.callStatic

        この関数は、前回の記事で紹介した frameworks/base/core/jni/AndroidRuntime.cpp ファイルに定義されています。 Androidアプリケーションインストール処理のソースコード解析 をクリックすると、ステップ 4 に進みます。

        ステップ5.systemServer.init2

        この関数は frameworks/base/services/java/com/android/server/SystemServer.java ファイルに定義されており、前回の記事で紹介した Androidアプリケーションインストール時のソースコード解析 をステップ5とします。

        ステップ6.serverThread.run

        この関数は、前回の記事で紹介した frameworks/base/services/java/com/android/server/SystemServer.java ファイルに定義されています。 Androidアプリケーションインストール時のソースコード解析 をクリックすると、ステップ6に進みます。

        ステップ7. ActivityManagerService.main

        この関数は、frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java ファイルに定義されています。

public final class ActivityManagerService extends ActivityManagerNative
		Monitor, BatteryStatsImpl.
	......

	public static final Context main(int factoryTest) {
		AThread thr = new AThread();
		thr.start();

		synchronized (thr) {
			while (thr.mService == null) {
				try {
					thr.wait();
				} catch (InterruptedException e) {
				}
			}
		}

		ActivityManagerService m = thr.mService;
		mSelf = m;
		ActivityThread at = ActivityThread.systemMain();
		mSystemThread = at;
		Context context = at.getSystemContext();
		m.mContext = context;
		m.mFactoryTest = factoryTest;
		m.mMainStack = new ActivityStack(m, context, true);

		m.mBatteryStatsService.publish(context);
		m.mUsageStatsService.publish(context);

		synchronized (thr) {
			thr.mReady = true;
			thr.notifyAll();
		}

		m.startRunning(null, null, null, null);

		return context;
	}

	......
}

        この関数は、まずAThreadスレッドオブジェクトを介して内部でActivityManagerServiceのインスタンスを生成し、このインスタンスをそのメンバー変数mServiceに保存し、このActivityManagerServiceインスタンスをActivityManagerServiceクラスのスタティックメンバー変数mSelfに保存し、最後に他のメンバー変数を初期化して終了です。

        ステップ 8. PackageManagerService.main

        この関数は、前回の記事で紹介した frameworks/base/services/java/com/android/server/PackageManagerService.java ファイルに定義されています。 Androidアプリケーションインストール時のソースコード解析 この手順の後、システム内のアプリケーションの情報はすべてPackageManagerServiceに保存され、ホームアプリランチャーを起動すると、PackageManagerService内のアプリケーション情報が取り出され、デスクトップにショートカットアイコンとして表示されます。 後ほどご紹介するデスクトップ上の

        ステップ9. ActivityManagerService.setSystemProcess

        この関数は、frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java ファイルに定義されています。

public final class ActivityManagerService extends ActivityManagerNative
		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
	......

	public static void setSystemProcess() {
		try {
			ActivityManagerService m = mSelf;

			ServiceManager.addService("activity", m);
			ServiceManager.addService("meminfo", new MemBinder(m));
			if (MONITOR_CPU_USAGE) {
				ServiceManager.addService("cpuinfo", new CpuBinder(m));
			}
			ServiceManager.addService("permission", new PermissionController(m));

			ApplicationInfo info =
				mSelf.mContext.getPackageManager().getApplicationInfo(
				"android", STOCK_PM_FLAGS);
			mSystemThread.installSystemApplicationInfo(info);

			synchronized (mSelf) {
				ProcessRecord app = mSelf.newProcessRecordLocked(
					mSystemThread.getApplicationThread(), info,
					info.processName);
				app.persistent = true;
				app.pid = MY_PID;
				app.maxAdj = SYSTEM_ADJ;
				mSelf.mProcessNames.put(app.processName, app.info.uid, app);
				synchronized (mSelf.mPidsSelfLocked) {
					mSelf.mPidsSelfLocked.put(app.pid, app);
				}
				mSelf.updateLruProcessLocked(app, true, true);
			}
		} catch (PackageManager.NameNotFoundException e) {
			throw new RuntimeException(
				"Unable to find android system package", e);
		}
	}
	......
}


        この関数は、まずActivityManagerServiceのインスタンスをServiceManagerに追加してホストし、ServiceManager.getServiceインタフェースを通じて他の場所からグローバルにユニークなActivityManagerServiceインスタンスにアクセスできるようにします。そして、mSystemThread.installSystemApplicationInfoを呼び出して、アプリケーションフレームワーク層の下でandroidパッケージをロードします。mSystemThreadは、上記のステップ7で作成したActivityThread型のインスタンス変数です。mSystemThreadは、上記Step7で作成したActivityThread型のインスタンス変数で、この後、いくつかの初期化作業が行われます。

        ステップ10. ActivityManagerService.systemReady

        この関数は、上記Step6のServerThread.run関数でシステム内のサービス群を初期化した後に呼び出され、 frameworks/base/services/java/com/android/server/am/ ActivityManagerServcie.java ファイルで定義されています。

public final class ActivityManagerService extends ActivityManagerNative
		Monitor, BatteryStatsImpl.
	......

	public void systemReady(final Runnable goingCallback) {
		......

		synchronized (this) {
			......

			mMainStack.resumeTopActivityLocked(null);
		}
	}

	......
}

        この関数にはもっと内容があるので、ここでは余計な部分を省略して、ホームアプリケーションを起動するロジックに注目します。ホームアプリケーションは、mMainStack.resumeTopActivityLocked関数(mMainStackはActivityStack型の変数のインスタンス)によって起動されます。

        ステップ11. ActivityStack.resumeTopActivityLocked

        この関数は、frameworks/base/services/java/com/android/server/am/ActivityStack.java ファイル内で以下のように定義されています。

public class ActivityStack {
	......

	final boolean resumeTopActivityLocked(ActivityRecord prev) {
		// Find the first activity that is not finishing.
		ActivityRecord next = topRunningActivityLocked(null);

		......

		if (next == null) {
			// There are no more activities! Let's just start up the
			// Launcher...
			if (mMainStack) {
				return mService.startHomeActivityLocked();
			}
		}

		......
	}

	......
}

        ここで関数topRunningActivityLockedが呼ばれ、現在のシステムActivityスタックの一番上にあるActivityを返していますが、この時点ではActivityは起動していないので、戻り値はnull、つまり次の変数の値はnullなので、mService. startHomeActivityLocked文、mServiceは先のステップ7で作成したActivityManagerServiceインスタンスを指します。

        ステップ 12. activityManagerService.startHomeActivityLocked

        この関数は、frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java ファイル内の

public final class ActivityManagerService extends ActivityManagerNative
		Monitor, BatteryStatsImpl.
	......

	boolean startHomeActivityLocked() {
		......

		Intent intent = new Intent(
			mTopAction,
			mTopData ! = null ? Uri.parse(mTopData) : null);
		intent.setComponent(mTopComponent);
		if (mFactoryTest ! = SystemServer.FACTORY_TEST_LOW_LEVEL) {
			intent.addCategory(Intent.CATEGORY_HOME);
		}
		ActivityInfo aInfo =
			intent.resolveActivityInfo(mContext.getPackageManager(),
			STOCK_PM_FLAGS);
		if (aInfo ! = null) {
			intent.setComponent(new ComponentName(
				aInfo.applicationInfo.packageName, aInfo.name));
			// Don't do this if the home app is currently being
			// instrumented.
			ProcessRecord app = getProcessRecordLocked(aInfo.processName,
				aInfo.applicationInfo.uid);
			if (app == null || app.InstrumentationClass == null) {
				intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
				mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
					null, null, 0, 0, 0, 0, false, false);
			}
		}

		return true;
	}

	......
}

        この関数は、まず CATEGORY_HOME タイプの Intent を作成し、次に Intent.resolveActivityInfo 関数を用いて PackageManagerService に Category タイプ HOME の Activity を問い合わせます。ここでは、システムだけが HOME タイプの Activity を登録した Launcher アプリケーションだけが来ていると仮定します (packages/apps/Launcher2/AndroidManifest.xml ファイルを参照) 。

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher"
    android:sharedUserId="@string/sharedUserId"
	>

    ......

	<application
	    android:name="com.android.launcher2.LauncherApplication"
	    android:process="@string/process"
	    android:label="@string/application_name"
	    android:icon="@drawable/ic_launcher_home">

		<activity
			android:name="com.android.launcher2.Launcher"
			android:launchMode="singleTask"
			android:clearTaskOnLaunch="true"
			android:stateNotNeeded="true"
			android:theme="@style/Theme"
			android:screenOrientation="nosensor"
			android:windowSoftInputMode="stateUnspecified|adjustPan">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.HOME" />
				<category android:name="android.intent.category.DEFAULT" />
				<category android:name="android.intent.category.MONKEY"/>
				</intent-filter>
		</activity>

		......
	</application>
</manifest>


        つまり、これはアクティビティ com.android.launcher2.Launcher を返しているのです。このActivityを起動するのは初めてなので、次に関数getProcessRecordLockedを呼び出すとProcessRecordの値がnullになるので、関数mMainStack.startActivityLockedが呼び出されてcom.android.Luncher2.Launcherが起動されます。Launcher2.Launcherは、mMainStackがActivityStack型のメンバー変数である。

        ステップ13. activityStack.startActivityLocked

        この関数は、frameworks/base/services/java/com/android/server/am/ActivityStack.java ファイルで定義されており、その中にあります。 Androidアプリケーション起動処理ソースコード解析 今回のシナリオでは、この関数を呼び出すと、最終的に com.android.launcher2.Launcher が起動し、その後に onCreate 関数が呼び出されます。

        ステップ14.launcher.onCreate

        この関数は、packages/apps/Launcher2/src/com/android/launcher2/Launcher.java ファイル内の

public final class Launcher extends Activity
		OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
	......

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		......

		if (!mRestoring) {
			mModel.startLoader(this, true);
		}

		......
	}

	......
}

        ここで mModel は LauncherModel 型のメンバ変数であり、その startLoader メンバ関数を呼び出すことでアプリケーションの追加操作が行われる。

        ステップ15.launcherModel.startLoader

        この関数は、packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.javaファイルで次のように定義されています。

public class LauncherModel extends BroadcastReceiver {
	......

	public void startLoader(Context context, boolean isLaunching) {
		......

                synchronized (mLock) {
                     ......

                     // Don't bother to start the thread if we know it's not going to do anything
                     if (mCallbacks ! = null && mCallbacks.get() ! = null) {
                         // If there is already one running, tell it to stop.
                         LoaderTask oldTask = mLoaderTask;
                         if (oldTask ! = null) { if (oldTask.
                             if (oldTask.isLaunching()) {
                                 // don't downgrade isLaunching if we're already running
                                 isLaunching = true;
                             }
                             oldTask.stopLocked();
		         }
		         mLoaderTask = new LoaderTask(context, isLaunching);
		         sWorker.post(mLoaderTask);
	            }
	       }
	}

	......
}

        ここでは、アプリケーションを直接読み込むのではなく、アプリケーションを読み込む操作をメッセージとして処理します。ここでのsWorkerはHandlerで、そのpostによってメッセージをメッセージキューに入れ、システムは渡されたパラメータmLoaderTaskのrun関数を呼び出してメッセージを処理します。 mLoaderTaskはLoaderTask型のインスタンスなので、以下は LoaderTaskクラスのrun関数が実行されることになります。

        ステップ16 LoaderTask.run

        この関数はpackages/apps/Launcher2/src/com/android/launcher2/LauncherModel.javaの以下の箇所で定義されています。

public class LauncherModel extends BroadcastReceiver {
	......

	private class LoaderTask implements Runnable {
		......

		public void run() {
			......

			keep_running: {
				......

				// second step
				if (loadWorkspaceFirst) {
					......
					loadAndBindAllApps();
				} else {
					......
				}

				......
			}

			......
		}

		......
	}

	......
}

        ここでは、さらなる操作のためにloadAndBindAllAppsメンバ関数が呼び出されます。

        ステップ17.LoaderTask.loadAndBindAllApps
        この関数はpackages/apps/Launcher2/src/com/android/launcher2/LauncherModel.javaファイルで定義されています。

public class LauncherModel extends BroadcastReceiver {
	......

	private class LoaderTask implements Runnable {
		......

		private void loadAndBindAllApps() {
			......

			if (!mAllAppsLoaded) {
				loadAllAppsByBatch();
				if (mStopped) {
					return;
				}
				mAllAppsLoaded = true;
			} else {
				onlyBindAllApps();
			}
		}


		......
	}

	......
}

        アプリがまだロードされていないため、ここでの mAllAppsLoaded は false となり、さらに処理を行うために loadAllAppsByBatch 関数を呼び出します。

        ローダータスク.loadAllAppsByBatch
        この関数は、packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.javaファイル内で以下のように定義されています。

public class LauncherModel extends BroadcastReceiver {
	......

	private class LoaderTask implements Runnable {
		......

		private void loadAllAppsByBatch() {	
			......

			final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
			mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

			final PackageManager packageManager = mContext.getPackageManager();
			List<ResolveInfo> apps = null;

			int N = Integer.MAX_VALUE;

			int startIndex;
			int i=0;
			int batchSize = -1;
			while (i < N && !mStopped) {
				if (i == 0) {
					mAllAppsList.clear();
					......
					apps = packageManager.queryIntentActivities(mainIntent, 0);
					
					......

					N = apps.size();
					
					......

					if (mBatchSize == 0) {
						batchSize = N;
					} else {
						batchSize = mBatchSize;
					}

					......

					Collections.sort(apps,
						new ResolveInfo.DisplayNameComparator(packageManager));
				}

				startIndex = i;
				for (int j=0; i<N && j<batchSize; j++) {
					// This builds the icon bitmaps.
					mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
					i++;
				}

				final boolean first = i <= batchSize;
				final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
				final ArrayList<ApplicationInfo> added = mAllAppsList.added;
				mAllAppsList.added = new ArrayList<ApplicationInfo>();
			
				mHandler.post(new Runnable() {
					public void run() {
						final long t = SystemClock.uptimeMillis();
						if (callbacks ! = null) { if (first)
							if (first) {
								callbacks.bindAllApplications(added);
							} else {
								callbacks.bindAppsAdded(added);
							}
							......
						} else {
							......
						}
					}
				});

				......
			}

			......
		}

		......
	}

	......
}


        この関数は、最初に CATEGORY_LAUNCHER 型の Intent を構築します。

    final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

        次に,変数mContextからPackageManagerServiceインタフェースを取得します.

    final PackageManager packageManager = mContext.getPackageManager();

       次に、この PackageManagerService.queryIntentActivities インターフェイスを使用して、Intent タイプのすべての Action を取得します。category_launcherを取得します。

       PackageManagerService.queryIntentActivities関数で、これらのActivityをどのように取得するか見てみましょう。

       ステップ19. PackageManagerService.queryIntentActivities

       この関数は、frameworks/base/services/java/com/android/server/PackageManagerService.java ファイルに定義されています。

class PackageManagerService extends IPackageManager.Stub {
	......

	public List<ResolveInfo> queryIntentActivities(Intent intent,
			String resolvedType, int flags) {
		......

		synchronized (mPackages) {
			String pkgName = intent.getPackage();
			if (pkgName == null) {
				return (List<ResolveInfo>)mActivities.queryIntent(intent,
						resolvedType, flags);
			}

			......
		}

		......
	}

	......
}

        以前の投稿を思い出してください Androidアプリのインストール処理のソースコード解析 前回のStep8では、システムがPackageManagerServiceを起動すると、システム内のすべてのアプリケーションを解析し、解析したActivityをmActivities変数に保存していますが、ここではmActivities変数のqueryIntent関数で条件を満たしたActivityを返しています。条件を満たすActivityは、mActivities変数のqueryIntent関数で返され、ここで返されるのは、ActionタイプがIntentのActivityになります。

        Step 18 の LoaderTask.loadAllAppsByBatch 関数に戻り、queryIntentActivities 関数呼び出しから要求された Activity を返した後、関数 tryGetCallbacks(oldCallbacks) が呼び出されて CallBack インターフェースに戻り、このインターフェースは Launcher クラスによって実装されており、さらに操作するためにインターフェースの .bindAllApplications 関数が呼び出されるのです。ここでも、アプリケーションをロードする操作は、メッセージによって処理されることに注意してください。

        ステップ20. ランチャー.bindAllApplications

        この関数はpackages/apps/Launcher2/src/com/android/launcher2/Launcher.javaファイルで定義されています。

public final class Launcher extends Activity
		OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
	......

	private AllAppsView mAllAppsGrid;

	......

	public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
		mAllAppsGrid.setApps(apps);
	}

	......
}

        ここでの mAllAppsGrid は AllAppsView 型の変数で、実際の型は通常 AllApps2D になります。

        ステップ21.AllApps2D.setApps(オールアップス2D.セットアップス

        この関数は、packages/apps/Launcher2/src/com/android/launcher2/AllApps2D.javaファイル内で以下のように定義されています。

public class AllApps2D
	extends RelativeLayout
	implements AllAppsView,
		OnItemClickListener,
		OnItemLongClickListener, OnItemLongClickListener,
		OnKeyListener,
		DragSource {

	......

	public void setApps(ArrayList<ApplicationInfo> list) {
		mAllAppsList.clear();
		addApps(list);
	}

	public void addApps(ArrayList<ApplicationInfo> list) {
		final int N = list.size();

		for (int i=0; i<N; i++) {
			final ApplicationInfo item = list.get(i);
			int index = Collections.binarySearch(mAllAppsList, item,
				LauncherModel.APP_NAME_COMPARATOR);
			if (index < 0) {
				index = -(index+1);
			}
			mAllAppsList.add(index, item);
		}
		mAppsAdapter.notifyDataSetChanged();
	}

	......
}

        setApps関数は、まずmAllAppsListのリストをクリアし、次にaddApps関数を呼び出して、前のステップで取得したアプリケーションごとにApplicationInfoのインスタンスを生成しています。

        この時点で、システムのデフォルトのホームアプリケーションLauncherは、PackageManagerServiceからアプリケーションを読み込み、画面上の以下のアイコンをクリックすると、先ほど読み込んだアプリケーションがアイコンとして表示されます。

        このボタンがクリックされると、Launcher.onClick関数に応答します。

public final class Launcher extends Activity
		OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
	......

	public void onClick(View v) {
		Object tag = v.getTag();
		if (tag instanceof ShortcutInfo) {
			......
		} else if (tag instanceof FolderInfo) {
			......
		} else if (v == mHandleView) {
			if (isAllAppsVisible()) {
				......
			} else {
				showAllApps(true);
			}
		}
	}

	......
}

        次に、showAllApps 関数を呼び出して、アプリケーションアイコンを表示します。

public final class Launcher extends Activity
		OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
	......

	void showAllApps(boolean animated) {
		mAllAppsGrid.zoom(1.0f, animated);

		((View) mAllAppsGrid).setFocusable(true);
		((View) mAllAppsGrid).requestFocus();

		// TODO: fade these two too
		mDeleteZone.setVisibility(View.GONE);
	}

	......
}


        これにより、システム内のアプリケーションを確認することができます。


        これらのアプリケーションアイコンが上記でクリックされると、AllApps2D.onItemClick関数が応答します。

public class AllApps2D
	extends RelativeLayout
	implements AllAppsView,
		OnItemClickListener,
		OnItemLongClickListener, OnItemLongClickListener,
		OnKeyListener,
		DragSource {

	......

	public void onItemClick(AdapterView parent, View v, int position, long id) {
		ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
		mLauncher.startActivitySafely(app.intent, app);
	}


	......
}


        ここでのメンバ変数mLauncherはLauncher型なので、Launcher.startActivitySafely関数が呼び出されてアプリケーションが起動し、次のように表示されていることがわかります。 Androidアプリの起動処理に関するソースコード解析 ある記事

ラオ・ルオの新浪微博。 http://weibo.com/shengyangluo とフォローを歓迎します