启动launcher进程的过程

android源码学习目录

介绍

了解launcher的都知道他是Android系统启动的第一应用,也就是我们通常说的手机主界面,它也是异常普通的Android app.

1.SystemServer对launcher的启动

上文已经介绍过SystemServer对Android系统的各个服务进行了启动,当全部服务启动完成后,会进行launcher的启动,这样才能让用户见到手机的界面。

//systemServer的startOtherService函数内,
 
 mActivityManagerService.systemReady(() -> {
            Slog.i(TAG, "Making services ready");
            traceBeginAndSlog("StartActivityManagerReadyPhase");
            mSystemServiceManager.startBootPhase(
                    SystemService.PHASE_ACTIVITY_MANAGER_READY);
            traceEnd();

            ````

        }, BOOT_TIMINGS_TRACE_LOG);

这里是Android systemServer服务启动最后启动launcher的入口,它调用了ActivityManagerService的systemReady函数,在activitymanagerserver准备好之后,就启动launcher app,

// /framework/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
             ......

            mStackSupervisor.resumeFocusedStackTopActivityLocked(); //1 
            mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
            traceLog.traceEnd(); // ActivityManagerStartApps
            traceLog.traceEnd(); // PhaseActivityManagerReady
        }
    }

详细代码我们不需要了解,因为所要了解的知识太多,这里我们只了解个过程就可以了。重要的是注释1处的代码

// mStackSuperVisor为ActivityStackSupervisor.java
//   
 boolean resumeFocusedStackTopActivityLocked() {
        return resumeFocusedStackTopActivityLocked(null, null, null);
}

boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);  
        }
        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null || r.state != RESUMED) {
            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);  //1
        } else if (r.state == RESUMED) {
            // Kick off any lingering app transitions form the MoveTaskToFront operation.
            mFocusedStack.executeAppTransition(targetOptions);
        }
        return false;
    }

代码会执行到注释1处,ActivityStack是Activity的堆栈,了解这个方法。

    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        .....

        if (prevTask != null && prevTask.getStack() == this &&
                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
            if (DEBUG_STACK)  mStackSupervisor.validateTopActivitiesLocked();
            if (prevTask == nextTask) {
                prevTask.setFrontOfTask();
            } else if (prevTask != topTask()) {
                // This task is going away but it was supposed to return to the home stack.
                // Now the task above it has to return to the home task instead.
                final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
                mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
            } else if (!isOnHomeDisplay()) {
                return false;
            } else if (!isHomeStack()){
                if (DEBUG_STATES) Slog.d(TAG_STATES,
                        "resumeTopActivityLocked: Launching home next");
                return isOnHomeDisplay() &&
                        mStackSupervisor.resumeHomeStackTask(prev, "prevFinished");   //1
            }
        }

         ......

        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
        return true;
    }

这个函数的代码执行重要在注释1处,resumeHomeStackTask函数,

 // /framework/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
        ....
        return mService.startHomeActivityLocked(mCurrentUser, myReason);
    }

这个函数调动了ActivityManagerService的startHomeActivityLocked函数,这个函数是launcher启动的关键函数。

    boolean startHomeActivityLocked(int userId, String reason) {
        ....

        Intent intent = getHomeIntent(); //1
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);  //2
        if (aInfo != null) {
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
          
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            if (app == null || app.instr == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
                // For ANR debugging to verify if the user activity is the one that actually
                // launched.
                final String myReason = reason + ":" + userId + ":" + resolvedUserId;
                mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);  //3
            }
        } else {
            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
        }

        return true;
    }

这里代码就启动launcher的代码。分三部,构建intent,根据intent查找Activity信息,启动Activity。

  • 注释1处
    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) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }

这里mTopAction为String mTopAction = Intent.ACTION_MAIN,ACTION_MAIN为android.intent.action.MAIN,这里我们知道这个是launcher的重要标记,我们开发launcher的时候也是用的这个将APP作为一个launcher。后续的代码为intent增加了一些参数。

  • 代码注释2处:
// 在ActivityManagerService中
    private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
        ActivityInfo ai = null;
        ComponentName comp = intent.getComponent();
        try {
            if (comp != null) {
                // Factory test.
                ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId); //从PackageManagerService中找到这个Activity
            } else {
                ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
                        intent,
                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                        flags, userId);

                if (info != null) {
                    ai = info.activityInfo;
                }
            }
        } catch (RemoteException e) {
            // ignore
        }

        return ai;
    }

上诉代码目的只有一个,根据intent从PackageManagerService中找到这个Activity,

  • 注释3处:
    mActivityStarter.startHomeActivityLocked, 就是启动这个Activity的方法,从这个函数之后,运行就和普通的Activity启动一样了,这里我们不进行详细介绍,后续会对 ActivityManagerService是怎样运行的进行详细介绍。
  • 到这里systemService利用ActivityManagerService对系统启动第一个应用launcher已经完成了。

2.Launcher的图标显示。

我们知道我们手机上launcher启动后,会将我们手机上安装的所有应用进行列表展示。这是怎么操作的呢,从前面文章的分析我们知道这个必定会和包管理服务PackageManagerService有关,我们现在查看launcher主要的Activity启动后只怎样查找使用所有APP的。
launcher启动的主Activity。

// /packages/apps/launcher3/AndroidManifest.xml
        <activity
            android:name="com.android.launcher3.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:windowSoftInputMode="adjustPan|stateUnchanged"
            android:screenOrientation="nosensor"
            android:configChanges="keyboard|keyboardHidden|navigation"
            android:resizeableActivity="true"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
            </intent-filter>
        </activity>

从上面的xml文件中可以看到"android.intent.action.MAIN注释的Activity为com.android.launcher3.Launcher,所以它就是launcher的第一个activity。

1.1 Launcher的onCreate方法

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

        LauncherAppState app = LauncherAppState.getInstance(this);  // 1 创建launcherAppState

     ....

        mSharedPrefs = Utilities.getPrefs(this);
        mIsSafeModeEnabled = getPackageManager().isSafeMode();
        mModel = app.setLauncher(this);   // 2 设置LauncherAppState的Launcher
        mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
        mIconCache = app.getIconCache();
        ....

        mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);   // 3 加载布局文件

        setupViews();
        mDeviceProfile.layout(this, false /* notifyListeners */);
        mExtractedColors = new ExtractedColors();
        loadExtractedColorsAndColorItems();

        mPopupDataProvider = new PopupDataProvider(this);

        ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
                .addAccessibilityStateChangeListener(this);

        lockAllApps();

        restoreState(savedInstanceState);

        if (LauncherAppState.PROFILE_STARTUP) {
            Trace.endSection();
        }

        // We only load the page synchronously if the user rotates (or triggers a
        // configuration change) while launcher is in the foreground
        int currentScreen = PagedView.INVALID_RESTORE_PAGE;
        if (savedInstanceState != null) {
            currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
        }
        if (!mModel.startLoader(currentScreen)) {   //  4 开始加载APP信息
            // If we are not binding synchronously, show a fade in animation when
            // the first page bind completes.
            mDragLayer.setAlpha(0);
        } else {
            // Pages bound synchronously.
            mWorkspace.setCurrentPage(currentScreen);

            setWorkspaceLoading(true);
        }
        ....
    }
  • 注释1处:获取了LauncherAppState对象,并在注释2处将Laucher对象设置到了launcherAppState.

  LauncherModel setLauncher(Launcher launcher) {
        getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
        mModel.initialize(launcher);
        return mModel;
    }


   public void initialize(Callbacks callbacks) {
        synchronized (mLock) {
            Preconditions.assertUIThread();
            // Remove any queued UI runnables
            mHandler.cancelAll();
            mCallbacks = new WeakReference<>(callbacks);
        }
    }

这里setLauncher就是将launcher类对象以弱引用方式放入mCallbacks, WeakReference就是弱引用。

  • 注释3处:加载了这个Activity的布局文件。
  • 注释4处:是很重要的一节,这里开始加载手机内所有APP的数据
// packages/apps/Launcher3/src/com/android/launcher3/LauncherModel
    public boolean startLoader(int synchronousBindPage) { 
        synchronized (mLock) {
            ....
            if (mCallbacks != null && mCallbacks.get() != null) {
                final Callbacks oldCallbacks = mCallbacks.get();
                // Clear any pending bind-runnables from the synchronized load process.
                runOnMainThread(new Runnable() {
                    public void run() {
                        oldCallbacks.clearPendingBinds(); //1清除原有的内容
                    }
                });

                // If there is already one running, tell it to stop.
                stopLoaderLocked();
                mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage); //构建LoaderTask
                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                        && mModelLoaded && !mIsLoaderTaskRunning) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                    return true;
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    sWorker.post(mLoaderTask); //2 执行LoaderTask, sWorker是一个handler。
                }
            }
        }
        return false;
    }
  • 注释1处进行了原有callback也就是launcher数据的清除
  • 注释2处进行了LoaderTask的构建,sWorker对loaderTask进行了执行。
packages/apps/Launcher3/src/com/android/launcher3/LauncherModel--LoaderTask run函数
 public void run() {
            synchronized (mLock) {
                if (mStopped) {
                    return;
                }
                mIsLoaderTaskRunning = true;
            }

            try {
                if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
                // Set to false in bindWorkspace()
                mIsLoadingAndBindingWorkspace = true;
                loadWorkspace();

                verifyNotStopped();
                if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
                bindWorkspace(mPageToBindFirst);

                // Take a break
                if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
                waitForIdle();
                verifyNotStopped();

                // second step
                if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
                loadAllApps();

                .....
            }
        }

上面函数每一步都有日志,翻译过来很好理解它做了什么,

  1. 加载和绑定工作区:Launcher是用工作区来显示系统安装的应用图标的,每个工作区都用来描述一个抽象桌面,它由N个桌面构成,每个桌面又分N个单元格。
  2. loadAllApps加载所有的APP
  private void loadAllApps() {
            ...
            mBgAllAppsList.clear();
            for (UserHandle user : profiles) {
                // Query for the set of apps
                final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
                final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); // 1获取所有APP的launcher-Activity信息
                ....
            mHandler.post(new Runnable() {
                public void run() {

                    final long bindTime = SystemClock.uptimeMillis();
                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        callbacks.bindAllApplications(added);  //2 绑定APP launcher信息。
                    } else {
                        Log.i(TAG, "not binding apps: no Launcher activity");
                    }
          .....
        }

  • 注释1处,获取所有APP的launcher Activity信息,因为每个APP,只要有界面就必定由一个launcher Activity。
  • 注释2处:将所有的APP信息绑定,对之前的分析我们知道callbacks是Launcher类,我们看看绑定了什么。
    public void bindAllApplications(final ArrayList<AppInfo> apps) {
        ...

        if (mAppsView != null) {
            mAppsView.setApps(apps);   //1 将APP数据添加到mAppsVIew内,
        }
        if (mLauncherCallbacks != null) {
            mLauncherCallbacks.bindAllApplications(apps);
        }
    }

重要代码,mAppsView是AllAppsContainerView,位置为/packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView,
AllAppsContainerView中的mApps是AlphabeticalAppsList,设置给它保存了下来,这时候APP的数据就加载完成了 。

1.2 APP数据的展示。

在上一节我们在Launcher类的oncreate函数中了解到它对布局文件进行了解析,最后调用了setContentVIew为Activity进行了界面设置。

mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
setContentView(mLauncherView);

这个加载布局中包含上文中提到的AllAppsContainerView,在看AllAppContainerVIew中的一个函数。

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

       ....

        // Load the all apps recycler view
        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
        mAppsRecyclerView.setApps(mApps);
        mAppsRecyclerView.setLayoutManager(mLayoutManager);
        mAppsRecyclerView.setAdapter(mAdapter);
        mAppsRecyclerView.setHasFixedSize(true);
        mAppsRecyclerView.addOnScrollListener(mElevationController);
        mAppsRecyclerView.setElevationController(mElevationController);

        FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
        mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
        mAppsRecyclerView.preMeasureViews(mAdapter);
        mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());

        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
            getRevealView().setVisibility(View.VISIBLE);
            getContentView().setVisibility(View.VISIBLE);
            getContentView().setBackground(null);
        }
    }

这个函数会在这个控件所在的xml被加载完成后触发, 这里的代码我们熟悉了,这就是对一个recyeleView进行的数据、adapter、layoutManager的设置,这里的mAppRecyclerView就是重写了RecyclerView, 用来展示所有已安装应用的图标等信息。

2 总结

到这里launcher已经启动并显示了所有的已安装的APP,这里我们可以认为Android系统的启动过程完全结束了。我的文章也对对Android整个系统分析完成了,但是这个分析的过程很粗略,很多代码和细节都没有写。想了解的可以跟着这个思路再细化这个Android系统启动的过程文章,欢迎投稿。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,015评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,373评论 3 404
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,253评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,069评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,079评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,550评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,911评论 3 429
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,892评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,435评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,465评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,586评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,178评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,920评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,356评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,493评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,151评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,663评论 2 366

推荐阅读更多精彩内容