---
# System prepended metadata

title: Launcher 進程
tags: [Android Framework]

---

---
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`
