---
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 */);
}
}
```
> 
### 啟動桌面入口 - 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
> 
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`