--- title: 'Launcher 進程' disqus: kyleAlien --- Launcher 進程 === ## OverView of Content Launcher 是手機啟動後,第一個被創建的 APP [TOC] ## [Launcher](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java) 概述 SystemServer 啟動的最後一步是啟動一個應用 APP 來顯示已經安裝好的應用,該應用就是 Launcher 1. Launcher 會在啟動過程中請求 PKMS (PackageManagerService) 返回系統已經安裝的應用 2. 將所有 APP 以一個 Icon 顯示在系統螢幕上,透過點擊 Icon 就可以啟動 APP ### [SystemServer](https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/SystemServer.java) 到 [AMS](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) * 首先 SystemServer 會先啟動 AMS、PKMS。啟動完 AMS、PKMS 後,透過 AMS#systemReady 準備啟動 Launcher APP ```java= // SystemServer.java public static void main(String[] args) { new SystemServer().run(); } private void run() { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); ...省略部分 try { ... 省略部分 // 1. 初始化 Native 的服務 System.loadLibrary("android_servers"); ... 省略部分 // 2. 創建 SystemService,可以用它來啟動其他不同的服務 mSystemServiceManager = new SystemServiceManager(mSystemContext); } finally { t.traceEnd(); // InitBeforeStartServices } try { // 3. 啟動系統多個服務 startBootstrapServices(t); startCoreServices(t); startOtherServices(t); } /* 省略 catch、finally */ } void startBootstrapServices() { ... 省略部分 ActivityTaskManagerService atm = mSystemServiceManager.startService( ActivityTaskManagerService.Lifecycle.class).getService(); mActivityManagerService = ActivityManagerService.Lifecycle.startService( mSystemServiceManager, atm); mActivityManagerService.setSystemServiceManager(mSystemServiceManager); mActivityManagerService.setInstaller(installer); ... 省略部分 try { PackageManagerService.main(mSystemContext, installer, domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); } finally { Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); } ... 省略部分 } void startOtherServices() { ... 省略部分 mPackageManagerService.systemReady(); // 耗時任務 ... 省略部分 // 主要分析 systemReady mActivityManagerService.systemReady(() -> { ... 省略部分 } } ``` * 從 SystemServer#startOtherServices 呼叫到 [**AMS**](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java),並呼叫 ActivityTaskManagerInternal 的重點方法,^1^ startHomeOnAllDisplays 來啟動 Launcher,^2^ resumeTopActivities 啟動棧頂 Activity ```java= // ActivityManagerService.java public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) { synchronized (this) { ... 省略部分 if (bootingSystemUser) { t.traceBegin("startHomeOnAllDisplays"); mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); t.traceEnd(); } ... 省略部分 mAtmInternal.resumeTopActivities(false /* scheduleIdle */); } } ``` > ![](https://i.imgur.com/mTgrmX4.png) ### 啟動桌面入口 - startHomeActivity * 抽象類 [**ActivityTaskManagerInternal**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java)#**startHomeOnAllDisplays**,它的實現是 [**ActivityTaskManagerService#LocalService**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityTaskManagerService.java) 的內部類 LocalService ```java= // ActivityTaskManagerInternal.java public abstract class ActivityTaskManagerInternal { // 抽象方法 public abstract boolean startHomeOnAllDisplays(int userId, String reason); } // ------------------------------------------------------------- // /ActivityTaskManagerService.java public class ActivityTaskManagerService extends IActivityTaskManager.Stub { RootWindowContainer mRootWindowContainer; final class LocalService extends ActivityTaskManagerInternal { @Override public boolean startHomeOnAllDisplays(int userId, String reason) { synchronized (mGlobalLock) { return mRootWindowContainer.startHomeOnAllDisplays(userId, reason); } } } } ``` * [**RootWindowContainer**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/RootWindowContainer.java)#startHomeOnAllDisplays 方法,最終會透過 ActivityStartController 準備 ActivityStarter 物件來啟動 HomeActivity ```java= // RootWindowContainer.java ActivityTaskManagerService mService; boolean startHomeOnAllDisplays(int userId, String reason) { boolean homeStarted = false; for (int i = getChildCount() - 1; i >= 0; i--) { final int displayId = getChildAt(i).mDisplayId; // 查看 ++startHomeOnDisplay++ homeStarted |= startHomeOnDisplay(userId, reason, displayId); } return homeStarted; } boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey) { // Fallback to top focused display or default display if the displayId is invalid. if (displayId == INVALID_DISPLAY) { final Task rootTask = getTopDisplayFocusedRootTask(); displayId = rootTask != null ? rootTask.getDisplayId() : DEFAULT_DISPLAY; } // 找到指定顯示的螢幕 final DisplayContent display = getDisplayContent(displayId); // 分析 ++startHomeOnTaskDisplayArea++ return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea, allowInstrumenting, fromHomeKey), false /* initValue */); } boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey) { ...省略部分 // 開始啟動 HomeActivity // mService 是 ATMS mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, taskDisplayArea); return true; } ``` * [**ActivityStartController**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityStartController.java)#startHomeActivity 取得覆用 ActivityStarter 並執行 execute 啟動 Activity ```java= // ActivityStartController.java private final ActivityTaskSupervisor mSupervisor; void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, TaskDisplayArea taskDisplayArea) { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); ... 省略部分 mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) .setOutActivity(tmpOutRecord) .setCallingUid(0) .setActivityInfo(aInfo) .setActivityOptions(options.toBundle()) .execute(); mLastHomeActivityStartRecord = tmpOutRecord[0]; if (rootHomeTask.mInResumeTopActivity) { // 如果我們已經在恢復部分,home Activity 將被初始化, // 但不會恢復(以避免遞歸恢復), // 並且會一直保持這種狀態,直到有東西再次戳它。 mSupervisor.scheduleResumeTopActivities(); } } ``` :::success * 後續: 走到 execute 這個步驟就會正常啟動一個 Activity,詳細請參考 [**Activity 啟動**](https://hackmd.io/yvJWnk1sS8G82QcdayBdkw?view) ::: ### [**ActivityTaskSupervisor**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java) - 啟動棧頂 Activity * [**ActivityTaskSupervisor**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java) 透過對 Handler 添加 `RESUME_TOP_ACTIVITY_MSG` 訊息來驅動 resumeFocusedTasksTopActivities 方法 ```java= // ActivityTaskSupervisor.java private final ActivityTaskSupervisorHandler mHandler; RootWindowContainer mRootWindowContainer; final void scheduleResumeTopActivities() { if (!mHandler.hasMessages(RESUME_TOP_ACTIVITY_MSG)) { mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG); } } private final class ActivityTaskSupervisorHandler extends Handler { ... 省略部分 @Override public void handleMessage(Message msg) { synchronized (mService.mGlobalLock) { ...省略部分 case case RESUME_TOP_ACTIVITY_MSG: { mRootWindowContainer.resumeFocusedTasksTopActivities(); } break; } } } // ------------------------------------------------- // RootWindowContainer.java boolean resumeFocusedTasksTopActivities() { return resumeFocusedTasksTopActivities(null, null, null); } boolean resumeFocusedTasksTopActivities( Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) { return resumeFocusedTasksTopActivities(targetRootTask, target, targetOptions, false /* deferPause */); } boolean resumeFocusedTasksTopActivities( Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions, boolean deferPause) { ... 省略部分 boolean result = false; if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea() || getTopDisplayFocusedRootTask() == targetRootTask)) { // 分析 resumeTopActivityUncheckedLocked 方法 result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions, deferPause); } ... 省略部分 } ``` * [**Task**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/Task.java)#resumeTopActivityUncheckedLocked 這個方法會透過遞迴方式來 **找到 Leaf 節點**,找到 Leaf 節點呼叫 resumeTopActivityInnerLocked 來啟動 Activity ```java= // /server/wm/Task.java boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options, boolean deferPause) { ... 省略部分 boolean someActivityResumed = false; try { // Protect against recursion. mInResumeTopActivity = true; if (isLeafTask()) { // 找到最終的葉節點 if (isFocusableAndVisible()) { // 準備啟動 // 準備查看 resumeTopActivityInnerLocked someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause); } } else { int idx = mChildren.size() - 1; while (idx >= 0) { final Task child = (Task) getChildAt(idx--); if (!child.isTopActivityFocusable()) { continue; } if (child.getVisibility(null /* starting */) != TASK_FRAGMENT_VISIBILITY_VISIBLE) { break; } // 遞迴 someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options, deferPause); if (idx >= mChildren.size()) { idx = mChildren.size() - 1; } } } // 啟動頂端 Activity final ActivityRecord next = topRunningActivity(true /* focusableOnly */); if (next == null || !next.canTurnScreenOn()) { checkReadyForSleep(); } } finally { mInResumeTopActivity = false; } return someActivityResumed; } ``` ### 取得 HomeIntent - resumeTopActivityInnerLocked * 這裡著重找到 Home Activity 與一般 APP 不同的啟動流程,如果要看一般 APP 請參考 [**Activity 啟動**](https://hackmd.io/yvJWnk1sS8G82QcdayBdkw?view#Activity-%E5%95%9F%E5%8B%95)) * 首先嘗試取得 topRunningActivity,若是無法取得 topActivity,就會呼叫 `resumeNextFocusableActivityWhenRootTaskIsEmpty` ,創建 HomeActivity ```java= // /wm/Task.java private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options, boolean deferPause) { if (!mAtmService.isBooting() && !mAtmService.isBooted()) { // Not ready yet! return false; } // 1. 分析 topRunningActivity final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */); if (topActivity == null) { // 2. 分析 resumeNextFocusableActivityWhenRootTaskIsEmpty return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options); } ... 省略部分 return resumed[0]; } ``` * 分析 topRunningActivity 方法 1. [**TaskFragment**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/TaskFragment.java):topRunningActivity 取得棧頂 Activity ```java= // TaskFragment.java class TaskFragment extends WindowContainer<WindowContainer> { ActivityRecord topRunningActivity(boolean focusableOnly) { return topRunningActivity(focusableOnly, true /* includingEmbeddedTask */); } // 分析 getActivity ActivityRecord topRunningActivity(boolean focusableOnly, boolean includingEmbeddedTask) { // Split into 4 to avoid object creation due to variable capture. if (focusableOnly) { if (includingEmbeddedTask) { return getActivity((r) -> r.canBeTopRunning() && r.isFocusable()); } return getActivity( (r) -> r.canBeTopRunning() && r.isFocusable() && r.getTask() == this.getTask()); } if (includingEmbeddedTask) { return getActivity(ActivityRecord::canBeTopRunning); } return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask()); } } ``` 2. [**WindowContainer**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowContainer.java):getActivity 方法,透過遞迴方法找到對應的 ActivityRecord 由於在這邊傳入的 boundary 是 null,所以沒得比對,所以返回的就是 null :::warn 這裡的 Predicate#callback 也還沒使用到... ?! ::: ```java= // WindowContainer.java // 這裡的 E 就是 WindowContainer protected final WindowList<E> mChildren = new WindowList<E>(); ActivityRecord getActivity(Predicate<ActivityRecord> callback) { return getActivity(callback, true /*traverseTopToBottom*/); } ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom) { return getActivity(callback, traverseTopToBottom, null /*boundary*/); } ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom, ActivityRecord boundary) { // 因為目前是傳入 true if (traverseTopToBottom) { // 從最後面開始尋找 for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer wc = mChildren.get(i); if (wc == boundary) return boundary; // 遞迴調用 final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary); if (r != null) { return r; } } } else { ... 省略 } return null; } ``` * 分析 resumeNextFocusableActivityWhenRootTaskIsEmpty ```java= // /wm/Task.java private boolean resumeNextFocusableActivityWhenRootTaskIsEmpty(ActivityRecord prev, ActivityOptions options) { final String reason = "noMoreActivities"; ... 省略部分 // 分析 resumeHomeActivity 方法 return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea()); } ``` * [**RootWindowContainer**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/RootWindowContainer.java)#resumeHomeActivity 會做以下事情,^1^ 找到目標顯示視窗 TaskDisplayArea、^2^ 檢查是否已經啟動,若已經啟動則恢復,若尚未啟動則新建一個 Home Activity ```java= // RootWindowContainer.java ActivityTaskManagerService mService; boolean resumeHomeActivity(ActivityRecord prev, String reason, TaskDisplayArea taskDisplayArea) { // 檢查是否已經啟動 if (!mService.isBooting() && !mService.isBooted()) { // Not ready yet! return false; } // 1. 找到顯示視窗 if (taskDisplayArea == null) { taskDisplayArea = getDefaultTaskDisplayArea(); } // 2. 尋找該視窗畫面的 HomeActivity final ActivityRecord r = taskDisplayArea.getHomeActivity(); final String myReason = reason + " resumeHomeActivity"; // Only resume home activity if isn't finishing. if (r != null && !r.finishing) { r.moveFocusableActivityToTop(myReason); return resumeFocusedTasksTopActivities(r.getRootTask(), prev, null); } // 新建一個 HomeActivity return startHomeOnTaskDisplayArea(mCurrentUser, myReason, taskDisplayArea, false /* allowInstrumenting */, false /* fromHomeKey */); } boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey) { // 跳轉 Intent Intent homeIntent = null; // ActivityInfo 是 Acitivy 相關訊息 ActivityInfo aInfo = null; if (taskDisplayArea == getDefaultTaskDisplayArea()) { // 1. 取得 Home 的 intent,追尋 getHomeIntent 方法 homeIntent = mService.getHomeIntent(); // 2. 取得 Home Activity 的 AcitivtyInfo aInfo = resolveHomeActivity(userId, homeIntent); } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) { Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea); aInfo = info.first; homeIntent = info.second; } if (aInfo == null || homeIntent == null) { return false; } ... 省略 // 下一小節分析 ActivityStartController#startHomeActivity mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, taskDisplayArea); return true; } ``` 1. 在 [**ActivityTaskManagerService**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityTaskManagerService.java) 取得 HomeIntent:這裡的重點是創建了 **ACTION_MAIN & CATEGORY_HOME** 類型的 Intent 1. mTopAction 用來描述第一個被啟動的 Activity 的 Action > ![](https://i.imgur.com/daXRAJD.png) 2. mFactoryTest 表示了系統的運行模式:有分為三種,低等級工廠模式、工廠模式、高級工廠模式 ```java= // ActivityTaskManagerService.java // 預設 ACTION_MAIN String mTopAction = Intent.ACTION_MAIN; final int mFactoryTest; Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { // 若非低等級工廠模式則添加 CATEGORY_HOME intent.addCategory(Intent.CATEGORY_HOME); } return intent; } ``` :::info * 從這裡可以看出來,**Launcher 是使用匿名意圖的 Intent** 來啟動目標應用的根 Activity ::: 2. 取得 Home Activity 的 AcitivtyInfo ```java= // RootWindowContainer.java // // 取得 Home 的 ActivityInfo 訊息 ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { final int flags = ActivityManagerService.STOCK_PM_FLAGS; // 包含包名,Class name 訊息 final ComponentName comp = homeIntent.getComponent(); ActivityInfo aInfo = null; try { if (comp != null) { // Factory test. aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId); } else { final String resolvedType = homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver()); final ResolveInfo info = AppGlobals.getPackageManager() .resolveIntent(homeIntent, resolvedType, flags, userId); if (info != null) { aInfo = info.activityInfo; } } } catch (RemoteException e) { // ignore } if (aInfo == null) { Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable()); return null; } aInfo = new ActivityInfo(aInfo); aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId); } ``` ### ActivityStarter - 隱式啟動 Activity * ActivityStartController#startHomeActivity:執行 `execute` 之後的啟動流程請看 [**Activity 啟動**](https://hackmd.io/yvJWnk1sS8G82QcdayBdkw?view) ```java= // ActivityStartController.java void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, TaskDisplayArea taskDisplayArea) { final ActivityOptions options = ActivityOptions.makeBasic(); ... 省略部分 final Task rootHomeTask; try { // Make sure root home task exists on display area. rootHomeTask = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP); } finally { mSupervisor.endDeferResume(); } // 1. 取得 ActivityStarter // 2. 透過 execute 啟動 Acitivty mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) .setOutActivity(tmpOutRecord) .setCallingUid(0) .setActivityInfo(aInfo) .setActivityOptions(options.toBundle()) .execute(); mLastHomeActivityStartRecord = tmpOutRecord[0]; if (rootHomeTask.mInResumeTopActivity) { // If we are in resume section already, home activity will be initialized, but not // resumed (to avoid recursive resume) and will stay that way until something pokes it // again. We need to schedule another resume. mSupervisor.scheduleResumeTopActivities(); } } ``` ## [Launcher](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java) Activity Launcher Activity 就是每個手機的桌面應用,負責顯示該裝置上安裝的所有 APP,並在你點擊 APP 時啟動指定 APP ### 分析 Actiivty - 加載 Widget Icon * 初始化桌面 Widget 的重點在 [**LauncherModel 類**](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/LauncherModel.java) ```java= // Launcher.java public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener { private static final LauncherModel sModel = new LauncherModel(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... 省略部分 if (!mRestoring) { startLoaders(); // 開始 Loading 桌面 Widget 入口 } ... 省略部分 } private void startLoaders() { boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged); // 分析 loadUserItems 方法 sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications); mRestoring = false; } } ``` * 透過 loadUserItems 來 Loading 桌面上所有的 shortcuts 或是 widgets 1. 第一次進入:創建 DesktopItemsLoader Runnable 任務,並創建新線程執行該任務 ```java= // LauncherModel.java void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged, boolean loadApplications) { // 已啟動過 if (isLaunching && isDesktopLoaded()) { ... 省略部分 launcher.onDesktopItemsLoaded(mDesktopItems, mDesktopAppWidgets); return; } ... 省略部分 // 以下是第一次啟動時進入 mDesktopItemsLoaded = false; // DesktopItemsLoader 是 Runnable mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications, isLaunching); // 創建新線程 mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader"); // 運行 DesktopItemsLoader mDesktopLoaderThread.start(); } private class DesktopItemsLoader implements Runnable { public void run() { ... 省略部分 final Launcher launcher = mLauncher.get(); final ContentResolver contentResolver = launcher.getContentResolver(); final PackageManager manager = launcher.getPackageManager(); ... 省略部分 try { ... 省略部分 ApplicationInfo info; while (!mStopped && c.moveToNext()) { try { int itemType = c.getInt(itemTypeIndex); switch (itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: ... 省略部分 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { // 分析 getApplicationInfo 方法 info = getApplicationInfo(manager, intent, launcher); } else { info = getApplicationInfoShortcut(c, launcher, iconTypeIndex, iconPackageIndex, iconResourceIndex, iconIndex); } // 無法 ApplicationInfo 就使用 default icon if (info == null) { info = new ApplicationInfo(); info.icon = manager.getDefaultActivityIcon(); } if (info != null) { info.title = c.getString(titleIndex); info.intent = intent; info.id = c.getLong(idIndex); container = c.getInt(containerIndex); info.container = container; info.screen = c.getInt(screenIndex); info.cellX = c.getInt(cellXIndex); info.cellY = c.getInt(cellYIndex); // 判斷 Widget 放置哪 switch (container) { // 直接放置桌面 case LauncherSettings.Favorites.CONTAINER_DESKTOP: desktopItems.add(info); break; default: // 在桌面,使用者設定的資料夾 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, container); folderInfo.add(info); break; } } break; } } } } finally { c.close(); } if (!mStopped) { ... 省略部分 if (!mStopped) { launcher.runOnUiThread(new Runnable() { public void run() { // 全部準備好後,同樣是呼叫 onDesktopItemsLoaded launcher.onDesktopItemsLoaded(uiDesktopItems, uiDesktopWidgets); } }); } mDesktopItemsLoaded = true; } } } ``` * getApplicationInfo使用 PackageManager #**resolveActivity 找到指定的 Activity**,設定 Activity 的 Icon ```java= // LauncherModel.java private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent, Context context) { // 透過 resolveActivity 找到指定的 Activity final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0); if (resolveInfo == null) { return null; } final ApplicationInfo info = new ApplicationInfo(); final ActivityInfo activityInfo = resolveInfo.activityInfo; info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context); if (info.title == null || info.title.length() == 0) { info.title = activityInfo.loadLabel(manager); } if (info.title == null) { info.title = ""; } info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; return info; } ``` 2. 已經加載過:若已經 Loading 過 Widget 相關訊息,就會呼叫 Launcher 的 onDesktopItemsLoaded 方法 ```java= // Launcher.java void onDesktopItemsLoaded(ArrayList<ItemInfo> shortcuts, ArrayList<LauncherAppWidgetInfo> appWidgets) { if (mDestroyed) { if (LauncherModel.DEBUG_LOADERS) { d(LauncherModel.LOG_TAG, " ------> destroyed, ignoring desktop items"); } return; } // 分析 bindDesktopItems 方法 bindDesktopItems(shortcuts, appWidgets); } ``` ### 創建 Widget Icon - bindDesktopItems * LauncherModel 加載完 Widget 訊息後就會呼叫 Luncher#onDesktopItemsLoaded 方法,在透過 startBindingItems 創建 ActivityIcon ```java= // Launcher.java void onDesktopItemsLoaded(ArrayList<ItemInfo> shortcuts, ArrayList<LauncherAppWidgetInfo> appWidgets) { if (mDestroyed) { if (LauncherModel.DEBUG_LOADERS) { d(LauncherModel.LOG_TAG, " ------> destroyed, ignoring desktop items"); } return; } // 分析 bindDesktopItems 方法 bindDesktopItems(shortcuts, appWidgets); } private void bindDesktopItems(ArrayList<ItemInfo> shortcuts, ArrayList<LauncherAppWidgetInfo> appWidgets) { ... 省略部分 // DesktopBinder 就是一個 Handler mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter); // 分析 startBindingItems 方法 mBinder.startBindingItems(); } ``` * DesktopBinder 類繼承於 Handler,透過傳送不同的消息來觸發 launcher 中的不同函數,接下來我們都會分析 ```java= // Launcher.java private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler { public void startBindingItems() { obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget(); } @Override public void handleMessage(Message msg) { Launcher launcher = mLauncher.get(); if (launcher == null || mTerminate) { return; } switch (msg.what) { case MESSAGE_BIND_ITEMS: { launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2); break; } case MESSAGE_BIND_DRAWER: { launcher.bindDrawer(this, mDrawerAdapter); break; } case MESSAGE_BIND_APPWIDGETS: { launcher.bindAppWidgets(this, mAppWidgets); break; } } } } ``` 1. bindItems:創建 shortcut (捷徑) 添加到螢幕上,完成後呼叫 Handler,傳送 **MESSAGE_BIND_DRAWER** 訊息 ```java= // Launcher.java private void bindItems(Launcher.DesktopBinder binder, ArrayList<ItemInfo> shortcuts, int start, int count) { final Workspace workspace = mWorkspace; ... 省略部分 for ( ; i < end; i++) { final ItemInfo item = shortcuts.get(i); switch (item.itemType) { ... 省略其他 case case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: // 創建 Widget Icon final View shortcut = createShortcut((ApplicationInfo) item); workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1, !desktopLocked); break; } } // 刷新 UI workspace.requestLayout(); if (end >= count) { finishBindDesktopItems(); // startBindingDrawer,一樣是通過 DesktopBinder // 發送一個 MESSAGE_BIND_DRAWER binder.startBindingDrawer(); } else { binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget(); } } ``` 2. bindDrawer:設定 GridView 的 Adapter,並透過 MessageQueue.IdleHandler 中的 `queueIdle` (**只有當該 MessageQueue 內沒有消息才會觸發**) 來傳遞 **MESSAGE_BIND_APPWIDGETS** 訊息 ```java= // Launcher.java private AllAppsGridView mAllAppsGrid; private void bindDrawer(Launcher.DesktopBinder binder, ApplicationsAdapter drawerAdapter) { mAllAppsGrid.setAdapter(drawerAdapter); // startBindingDrawer,一樣是通過 DesktopBinder // 發送一個 MESSAGE_BIND_DRAWER binder.startBindingAppWidgetsWhenIdle(); } private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler { // 添加 IdleHandler public void startBindingAppWidgetsWhenIdle() { // Ask for notification when message queue becomes idle final MessageQueue messageQueue = Looper.myQueue(); messageQueue.addIdleHandler(this); } public boolean queueIdle() { // Queue is idle, so start binding items startBindingAppWidgets(); return false; // 返回 false 就會在使用過後,移除 } public void startBindingAppWidgets() { obtainMessage(MESSAGE_BIND_APPWIDGETS).sendToTarget(); } } ``` 3. bindAppWidgets:一次只會取出一個 LauncherAppWidgetInfo,並透過 createView 創建對應的 View 添加到屏幕上 ```java= // Launcher.java private void bindAppWidgets(Launcher.DesktopBinder binder, LinkedList<LauncherAppWidgetInfo> appWidgets) { final Workspace workspace = mWorkspace; final boolean desktopLocked = mDesktopLocked; if (!appWidgets.isEmpty()) { // 取出 LauncherAppWidgetInfo final LauncherAppWidgetInfo item = appWidgets.removeFirst(); final int appWidgetId = item.appWidgetId; final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); // 創建對應的 WidgetIcon item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); ... item.hostView.setAppWidget(appWidgetId, appWidgetInfo); item.hostView.setTag(item); // 添加到當前螢幕中 workspace.addInScreen(item.hostView, item.screen, item.cellX, item.cellY, item.spanX, item.spanY, !desktopLocked); workspace.requestLayout(); } if (appWidgets.isEmpty()) { // 加載完畢~ if (PROFILE_ROTATE) { android.os.Debug.stopMethodTracing(); } } else { // 尚未加載完畢,再次傳送 MESSAGE_BIND_APPWIDGETS binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS).sendToTarget(); } } ``` ### [**AppWidgetHost**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/appwidget/AppWidgetHost.java) - 創建 Widget Icon * AppWidgetHost 也負責桌面 Icon 的更新、創建、移除..等等 功能;並且透過 **createView 函數創建 WidgetIcon** > [**LauncherAppWidgetHost**](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/LauncherAppWidgetHost.java) 繼承 AppWidgetHost,並且覆寫 onCreateView 方法 ```java= // AppWidgetHost.java static IAppWidgetService sService; // 桌面的所有 APP Icon private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>(); public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { if (sService == null) { return null; } // onCreateView 函數由 LauncherAppWidgetHost 複寫 AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); view.setInteractionHandler(mInteractionHandler); view.setAppWidget(appWidgetId, appWidget); synchronized (mViews) { // 儲存所有 Icon mViews.put(appWidgetId, view); } RemoteViews views; try { views = sService.getAppWidgetViews(mContextOpPackageName, appWidgetId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } // 刷新 App Icon view.updateAppWidget(views); return view; } ``` ### Launcher - APP Icon 點擊事件 * Launcher 的點擊事件,有包括 ^1.^ 長按事件 (移除 APP or 查看訊息等等)、^2.^ 一般的點擊事件 (啟動 APP) 1. 長按事件 [**LauncherAppWidgetHostView**](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/LauncherAppWidgetHostView.java) 繼承 [**AppWidgetHostView**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/appwidget/AppWidgetHostView.java) (AppWidgetHostView 就是一個 FrameLayout),在處理 Launcher Icon 的長按事件 ```java= // AppWidgetHostView.java public class AppWidgetHostView extends FrameLayout { ... 省略 } // -------------------------------------------------------------- // LauncherAppWidgetHostView.java public class LauncherAppWidgetHostView extends AppWidgetHostView { // 確認是否有長按的 Flag private boolean mHasPerformedLongPress; // 一般內部類 private CheckForLongPress mPendingCheckForLongPress; private LayoutInflater mInflater; public LauncherAppWidgetHostView(Context context) { super(context); // 透過 Context 取得 Layout 加載類 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } // 錯誤的 View @Override protected View getErrorView() { return mInflater.inflate(R.layout.appwidget_error, this, false); } // ViewGroup 的攔截 public boolean onInterceptTouchEvent(MotionEvent ev) { // 長按 Flag 若已經成立,就會 reset 掉 if (mHasPerformedLongPress) { mHasPerformedLongPress = false; return true; } // Watch for longpress events at this level to make sure // users can always pick up this widget switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { postCheckForLongClick(); break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mHasPerformedLongPress = false; if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } break; } // Otherwise continue letting touch events fall through to children return false; } class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; public void run() { if ((mParent != null) && hasWindowFocus() && mOriginalWindowAttachCount == getWindowAttachCount() && !mHasPerformedLongPress) { // 點擊事件 if (performLongClick()) { mHasPerformedLongPress = true; } } } public void rememberWindowAttachCount() { mOriginalWindowAttachCount = getWindowAttachCount(); } } // 長按事件由 CheckForLongPress 處理 private void postCheckForLongClick() { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.rememberWindowAttachCount(); // 預設是 400 ms postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout()); } // View 的方法 @Override public void cancelLongPress() { super.cancelLongPress(); mHasPerformedLongPress = false; if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } } } ``` 2. 一般點擊事件:由於 Widget Icon 是添加在 GridView ([**AllAppsGridView**](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/AllAppsGridView.java)) 中,所以 APP 的啟動事件也是由 AllAppsGridView 觸發 * 最終點擊事件會由 [**Luncher**](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java)#**startActivitySafely** 處理,進入一般 Activity 的啟動階段 ```java= // AllAppsGridView.java public class AllAppsGridView extends GridView implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, DragSource { @Override protected void onFinishInflate() { // View 被加載完畢後添加點擊監聽 setOnItemClickListener(this); setOnItemLongClickListener(this); } public void onItemClick(AdapterView parent, View v, int position, long id) { ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position); // 呼叫 startActivitySafely mLauncher.startActivitySafely(app.intent); } } ``` * 回到 Launcher#startActivitySafely 方法 ```java= // Launcher.java void startActivitySafely(Intent intent) { // 在新 Task 中啟動 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { startActivity(intent); } /* 省略 catch */ } ``` ## [Launcher](https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java) 註冊全域廣播 * Launcher 會透過動態註冊指定廣播訊息,當有新安裝、移除、改動等等時,就會觸發 Launcher 不同行為 (觸發 LauncherModel 來改變 AppIcon) | Intent 標記 | 功能 | 觸發 Modle 方法 | | -------- | -------- | - | | ACTION_PACKAGE_ADDED | 新增、替換 APP | addPackage | | ACTION_PACKAGE_REMOVED | 移除 APP | removePackage | | ACTION_PACKAGE_CHANGED | 修改 APP | updatePackage | ```java= // Launcher.java // 內部類 ApplicationsIntentReceiver private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver(); private static final LauncherModel sModel = new LauncherModel(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... 省略部分 registerIntentReceivers(); ... 省略部分 } private void registerIntentReceivers() { ... 省略部分 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); registerReceiver(mApplicationsReceiver, filter); } // 非靜態內部類,就能簡單使用外方法 private class ApplicationsIntentReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final String packageName = intent.getData().getSchemeSpecificPart(); final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); ... Debug 訊息 if (!Intent.ACTION_PACKAGE_CHANGED.equals(action)) { // 移除 APP 種類 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { if (!replacing) { removeShortcutsForPackage(packageName); ... Debug 訊息 sModel.removePackage(Launcher.this, packageName); } // 替換(更新),在之後會發送 ACTION_PACKAGE_ADDED } else { if (!replacing) { ... Debug 訊息 // 新 APP sModel.addPackage(Launcher.this, packageName); } else { ... Debug 訊息 // 升級版本的 APP sModel.updatePackage(Launcher.this, packageName); updateShortcutsForPackage(packageName); } } removeDialog(DIALOG_CREATE_SHORTCUT); } else { ... Debug 訊息 // 同步所有 APP 訊息 sModel.syncPackage(Launcher.this, packageName); } } } ``` ### [**AppWidgetHost**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/appwidget/AppWidgetHost.java) 初始化 - 監聽 AppWidget 改變 ```java= // Launcher.java // AppWidget 的 代理對象 private AppWidgetManager mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; static final int APPWIDGET_HOST_ID = 1024; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAppWidgetManager = AppWidgetManager.getInstance(this); mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); // 分析 ++startListening++ 方法 mAppWidgetHost.startListening(); ... 省略部分 } ``` * LauncherAppWidgetHost 是 AppWidgetHost 的子類,它的主要功能實現在 AppWidgetHost,請跟著以下流程看 * AppWidgetHost 建構式: 1. UpdateHandler:接收 Binder 傳入的訊息 2. Callbacks (內部類):作為 BinderServer 端,**接收 AppWidgetService 的訊息,再透過 UpdateHandler 回傳到 Launcher 線程** 3. bindService:創建 appwidget 代理,用代理類來呼叫 AppWidgetService 服務 ```java= // AppWidgetHost.java public AppWidgetHost(Context context, int hostId) { this(context, hostId, null, context.getMainLooper()); } /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public AppWidgetHost(Context context, int hostId, InteractionHandler handler, Looper looper) { ... 省略部分 mHandler = new UpdateHandler(looper); mCallbacks = new Callbacks(mHandler); bindService(context); } ``` * Callbacks 是 AppWidgetHost 的靜態內部類:**^1^ 它作為 BinderServer 的實現端(繼承 IAppWidgetHost.Stub),^2^ 並把它當成 Callback 傳入 AppWidgetService** ```java= // AppWidgetHost.java static class Callbacks extends IAppWidgetHost.Stub { // 透過 Handler 傳回原來的線程 private final WeakReference<Handler> mWeakHandler; public Callbacks(Handler handler) { mWeakHandler = new WeakReference<>(handler); } public void updateAppWidget(int appWidgetId, RemoteViews views) { if (isLocalBinder() && views != null) { views = views.clone(); } Handler handler = mWeakHandler.get(); if (handler == null) { return; } // 對 Handler (UpdateHandler) 傳送 HANDLE_UPDATE 訊息 Message msg = handler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views); msg.sendToTarget(); } ... 省略部分方法 } ``` * binderService 函數: 1. 取得 PKMS 檢查硬體是否有 `FEATURE_APP_WIDGETS` 功能,以及系統 `config_enableAppWidgetService` 是否開啟 2. 取得 AppWidgetService 代理類 ```java= private static void bindService(Context context) { // 靜態鎖,避免 Service 多次綁定 synchronized (sServiceLock) { // 簡單的避免重複初始化 if (sServiceInitialized) { return; } sServiceInitialized = true; // 取得 PKMS 的代理類 PackageManager packageManager = context.getPackageManager(); // 1. 檢查硬體設置,是否可以創建 APP Widgets if (!packageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS) && !context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) { return; } IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE); // 1. 取得 Binder 代理類 sService = IAppWidgetService.Stub.asInterface(b); } } ``` * [**AppWidgetHost**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/appwidget/AppWidgetHost.java) 初始化完成後,就會呼叫 startListening 函數,而之後呼叫到 [**AppWidgetServiceImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java)#startListening 方法 ```java= // AppWidgetHost.java static IAppWidgetService sService; private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>(); public void startListening() { if (sService == null) { return; } final int[] idsToUpdate; synchronized (mViews) { int N = mViews.size(); idsToUpdate = new int[N]; for (int i = 0; i < N; i++) { idsToUpdate[i] = mViews.keyAt(i); } } List<PendingHostUpdate> updates; try { // 實現類是 AppWidgetServiceImpl.java // 傳入 Callbacks updates = sService.startListening( mCallbacks, mContextOpPackageName, mHostId, idsToUpdate).getList(); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } int N = updates.size(); for (int i = 0; i < N; i++) { PendingHostUpdate update = updates.get(i); switch (update.type) { case PendingHostUpdate.TYPE_VIEWS_UPDATE: updateAppWidgetView(update.appWidgetId, update.views); break; case PendingHostUpdate.TYPE_PROVIDER_CHANGED: onProviderChanged(update.appWidgetId, update.widgetInfo); break; case PendingHostUpdate.TYPE_VIEW_DATA_CHANGED: viewDataChanged(update.appWidgetId, update.viewId); break; case PendingHostUpdate.TYPE_APP_WIDGET_REMOVED: dispatchOnAppWidgetRemoved(update.appWidgetId); break; } } } ``` ### [**AppWidgetServiceImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java) - startListening 儲存 Host callback * App 端呼叫 startListening 方法,並將 Callback ( **IAppWidgetHost.Stub** ) 存入 AppWidgetServiceImpl 中的 mHosts 列表 ```java= // AppWidgetServiceImpl.java class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider, OnCrossProfileWidgetProvidersChangeListener { private final Object mLock = new Object(); ... 省略其他方法 @Override public ParceledListSlice<PendingHostUpdate> startListening(IAppWidgetHost callbacks, String callingPackage, int hostId, int[] appWidgetIds) { final int userId = UserHandle.getCallingUserId(); ... 省略 debug 訊息 // Make sure the package runs under the caller uid. // 驗證呼叫者 mSecurityPolicy.enforceCallFromPackage(callingPackage); synchronized (mLock) { // 免安裝應用無法託管應用小部件 if (mSecurityPolicy.isInstantAppLocked(callingPackage, userId)) { Slog.w(TAG, "Instant package " + callingPackage + " cannot host app widgets"); return ParceledListSlice.emptyList(); } ensureGroupStateLoadedLocked(userId); // NOTE: The lookup is enforcing security across users by making // sure the caller can only access hosts it owns. // 安全訪問,強制呼叫者只能訪問自己的主機 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage); // 1. 創建 Host 對象,並添加到 mHost 列表 // 分析 lookupOrAddHostLocked 方法 Host host = lookupOrAddHostLocked(id); // 一個 Host 對應一個 callback host.callbacks = callbacks; // 原子操作 long updateSequenceNo = UPDATE_COUNTER.incrementAndGet(); int N = appWidgetIds.length; ArrayList<PendingHostUpdate> outUpdates = new ArrayList<>(N); LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>(); for (int i = 0; i < N; i++) { updatesMap.clear(); host.getPendingUpdatesForIdLocked(mContext, appWidgetIds[i], updatesMap); // 我們根據請求 id 對更新進行鍵控,以便按接收到的順序對值進行排序。 int m = updatesMap.size(); for (int j = 0; j < m; j++) { outUpdates.add(updatesMap.valueAt(j)); } } // Reset the update counter once all the updates have been calculated // 重制數量 host.lastWidgetUpdateSequenceNo = updateSequenceNo; return new ParceledListSlice<>(outUpdates); } } } ``` * AppWidgetServiceImpl 會根據傳入的 id 儲存所有訪問它的 Host,而 lookupOrAddHostLocked 就是透過比對 id 來找到需要的 Host ```java= // AppWidgetServiceImpl.java // 儲存所有 lookup 的 Host private final ArrayList<Host> mHosts = new ArrayList<>(); private Host lookupOrAddHostLocked(HostId id) { Host host = lookupHostLocked(id); if (host != null) { return host; } host = new Host(); host.id = id; mHosts.add(host); return host; } private Host lookupHostLocked(HostId hostId) { final int N = mHosts.size(); for (int i = 0; i < N; i++) { // 比對 id 找到相對應的 Host Host host = mHosts.get(i); if (host.id.equals(hostId)) { return host; } } return null; } ``` * 以下舉個例子,如何透過調用 AppWidgetServiceImpl 來呼叫到 Launcher 的 callback 1. 查看 onCrossProfileWidgetProvidersChanged 方法,它會掃描當前是否有 Package 改變 or 移除,如果有則透過 Handler 傳送 MSG_NOTIFY_PROVIDERS_CHANGED 訊息 (**該訊息中包含 callback 對象**) ```java= // AppWidgetServiceImpl.java @Override public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) { final int parentId = mSecurityPolicy.getProfileParent(userId); if (parentId != userId) { synchronized (mLock) { ... 省略部分 // 有需要改變 or 移除的 Package if (providersChanged || removedCount > 0) { saveGroupStateAsync(userId); // 查看 scheduleNotifyGroupHostsForProvidersChangedLocked scheduleNotifyGroupHostsForProvidersChangedLocked(userId); } } } } private void scheduleNotifyGroupHostsForProvidersChangedLocked(int userId) { final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId); // 遍歷所有 Host final int N = mHosts.size(); for (int i = N - 1; i >= 0; i--) { // 取到 Host 中每一個需要通知的 callback Host host = mHosts.get(i); ... 省略部分 SomeArgs args = SomeArgs.obtain(); args.arg1 = host; args.arg2 = host.callbacks; // 對 Handler 發送消息 mCallbackHandler.obtainMessage( CallbackHandler.MSG_NOTIFY_PROVIDERS_CHANGED, args).sendToTarget(); } } ``` 2. Handler 接收到 MSG_NOTIFY_PROVIDERS_CHANGED 訊息後,呼叫 handleNotifyProvidersChanged 方法,並通過 Callback 呼叫到 Launcher 端 ```java= // AppWidgetServiceImpl.java private final class CallbackHandler extends Handler { @Override public void handleMessage(Message message) { switch (message.what) { ... 省略部分 case case MSG_NOTIFY_PROVIDERS_CHANGED: { SomeArgs args = (SomeArgs) message.obj; // 取 SomeArgs 中的參數 Host host = (Host) args.arg1; IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; args.recycle(); // 回收 SomeArgs 對象 (為了之後覆用) // 查看 handleNotifyProvidersChanged 方法 handleNotifyProvidersChanged(host, callbacks); } break; } } } private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) { try { // 呼叫到 APP 端 callbacks.providersChanged(); } catch (RemoteException re) { synchronized (mLock) { Slog.e(TAG, "Widget host dead: " + host.id, re); host.callbacks = null; } } } ``` 3. 到了這個步驟就回到客端 (APP 端) ```java= // AppWidgetHost.java static class Callbacks extends IAppWidgetHost.Stub { private final WeakReference<Handler> mWeakHandler; ... 省略部分 public void providersChanged() { Handler handler = mWeakHandler.get(); if (handler == null) { return; } handler.obtainMessage(HANDLE_PROVIDERS_CHANGED).sendToTarget(); } } ``` ## Appendix & FAQ :::info 參考 1 https://blog.csdn.net/qq_23547831/article/details/51112031?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-12.pc_relevant_antiscanv2&spm=1001.2101.3001.4242.7&utm_relevant_index=15 參考 2 https://blog.csdn.net/luoshengyang/article/details/6689748 ::: ###### tags: `Android Framework`