写点什么

Android-Framework 学习笔记(四)Launcher 启动过程,android 高级开发面试题以及答案

作者:嘟嘟侠客
  • 2021 年 11 月 27 日
  • 本文字数:5586 字

    阅读完需:约 18 分钟

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


ActivityManagerService 的 startHomeActivityLocked()的注释 3 就是启动符合条件的应用程序,即 Launcher。


frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java


ActivityStarter#startHomeActivityLocked()


void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);startActivityLocked(null /caller/, intent, null /ephemeralIntent/,null /resolvedType/, aInfo, null /rInfo/, null /voiceSession/,null /voiceInteractor/, null /resultTo/, null /resultWho/,0 /requestCode/, 0 /callingPid/, 0 /callingUid/, null /callingPackage/,0 /realCallingPid/, 0 /realCallingUid/, 0 /startFlags/, null /options/,false /ignoreTargetSecurity/, false /componentSpecified/, null /outActivity/,null /container/, null /inTask/);if (mSupervisor.inResumeTopActivity) {// 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(); //1}}


注释 1 调用的是 scheduleResumeTopActivities()方法,这个方法其实是关于 Activity 的启动流程的逻辑了,这里我们就不详细说明了,关于 Activity 的启动流程可以参考我后面文章。


这样 Launcher 就会被启动起来,并执行它的 onCreate 函数。


Android 应用程序安装 Android 系统在启动的过程中,Zygote 进程启动 SystemServer 进程,SystemServer 启动 PackageManagerService 服务,这个服务负责扫描系统中特定的目录,找到里面的应用程序文件,即以 Apk 为后缀的文件,然后对这些文件进解析(其实就是解析应用程序配置文件 AndroidManifest.xml 的过程),并从里面得到得到应用程序的相关信息,例如得到应用程序的组件 Package、Activity、Service、Broadcast Receiver 和 Content Provider 等信息,保存到 PackageManagerService 的 mPackages、mActivities、mServices、mReceivers 等成员变量(HashMap 类型)中,得到应用程序的相关信息之后,完成应用程序的安装过程。


这些应用程序只是相当于在 PackageManagerService 服务注册好了,如果我们想要在 Android 桌面上看到这些应用程序,还需要有一个 Home 应用程序(Android 系统默认的 Home 应用程序就是 Launcher),负责从 PackageManagerService 服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式,接着往下看。


Launcher 中应用图标显示流程从 Launcher 的 onCreate 函数开始分析。


packages/apps/Launcher3/src/com/android/launcher3/Launcher.java


Launcher#onCreate()


@Overrideprotected void onCreate(Bundle savedInstanceState) {...LauncherAppState app = LauncherAppState.getInstance();//1mDeviceProfile = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ?app.getInvariantDeviceProfile().landscapeProfile: app.getInvariantDeviceProfile().portraitProfile;mSharedPrefs = Utilities.getPrefs(this);mIsSafeModeEnabled = getPackageManager().isSafeMode();mModel = app.setLauncher(this);//2....if (!mRestoring) {if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);//3} else {mModel.startLoader(mWorkspace.getRestorePage());}}...}


注释 1 处获取 LauncherAppState 的实例。注释 2 处调用它的 setLauncher 函数并将 Launcher 对象传入。


packages/apps/Launcher3/src/com/android/launcher3/LauncherAppState.java


LauncherAppState#setLauncher()


LauncherModel setLauncher(Launcher launcher) {getLauncherProvider().setLauncherProviderChangeListener(launcher);mModel.initialize(launcher);//1mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ? new LauncherAccessibilityDelegate(launcher) : null;return mModel;}


注释 1 处会调用 LauncherModel 的 initialize 函数。


packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java


LauncherModel#initialize()


public void initialize(Callbacks callbacks) {synchronized (mLock) {unbindItemInfosAndClearQueuedBindRunnables();mCallbacks = new WeakReference<Callbacks>(callbacks);}}


在 initialize 函数中会将 Callbacks,也就是传入的 Launcher 封装成一个弱引用对象。因此我们得知 mCallbacks 变量指的就是封装成弱引用对象的 Launcher,这个 mCallbacks 后文会用到它。


再回到 Launcher 的 onCreate 函数,在注释 3 处调用了 LauncherModel 的 startLoader 函数:


LauncherModel#startLoader()


...@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");//1static {sWorkerThread.start();}@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());//2...public void startLoader(int synchronousBindPage, int loadFlags) {InstallShortcutReceiver.enableInstallQueue();synchronized (mLock) {synchronized (mDeferredBindRunnables) {mDeferredBindRunnables.clear();}if (mCallbacks != null && mCallbacks.get() != null) {stopLoaderLocked();mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags); //3if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {mLoaderTask.runBindSynchronousPage(synchronousBindPage);} else {sWorkerThread.setPriority(Thread.NORM_PRIORITY);sWorker.post(mLoaderTask);//4}}}}


注释 1 处创建了具有消息循环的线程 HandlerThread 对象。注释 2 处创建了 Handler,并且传入 HandlerThread 的 Looper。Hander 的作用就是向 HandlerThread 发送消息。注释 3 处创建 LoaderTask。注释 4 处将 LoaderTask 作为消息发送给 HandlerThread 。LoaderTask 类实现了 Runnable 接口。


LoaderTask


private class LoaderTask implements Runnable {...public void run() {synchronized (mLock) {if (mStopped) {return;}mIsLoaderTaskRunning = true;}keep_running: {if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");loadAndBindWorkspace();//1if (mStopped) {break keep_running;}waitForIdle();if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");loadAndBindAllApps();//2}mContext = null;synchronized (mLock) {if (mLoaderTask == this) {mLoaderTask = null;}mIsLoaderTaskRunning = false;mHasLoaderCompletedOnce = true;}}...


}


Launcher 是用工作区的形式来显示系统安装的应用程序的快捷图标,每一个工作区都是来描述一个抽象桌面的,它由 n 个屏幕组成,每个屏幕又分 n 个单元格,每个单元格用来显示一个应用程序的快捷图标。注释 1 处调用 loadAndBindWorkspace 函数用来加载工作区信息。注释 2 处的 loadAndBindAllApps 函数是用来加载系统已经安装的应用程序信息。


LauncherModel#loadAndBindAllApps()


private void loadAndBindAllApps() {if (DEBUG_LOADERS) {Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);}if (!mAllAppsLoaded) {loadAllApps();//1synchronized (LoaderTask.this) {if (mStopped) {return;}}updateIconCache();synchronized (LoaderTask.this) {if (mStopped) {return;}mAllAppsLoaded = true;}} else {onlyBindAllApps();}}


如果系统没有加载已经安装的应用程序信息,则会调用注释 1 处的 loadAllApps()函数。


LauncherModel#loadAllApps()


private void loadAllApps() {...final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user); //1// Fail if we don't have any apps// TODO: Fix this. Only fail for the current user.if (apps == null || apps.isEmpty()) {return;}// Create the ApplicationInfosfor (int i = 0; i < apps.size(); i++) {LauncherActivityInfoCompat app = apps.get(i);// This builds the icon bitmaps.mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode)); //2}...// Huh? Shouldn't this be inside the Runnable below?final ArrayList<AppInfo> added = mBgAllAppsList.added;mBgAllAppsList.added = new ArrayList<AppInfo>();


mHandler.post(new Runnable() {public void run() {final long bindTime = SystemClock.uptimeMillis();final Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindAllApplications(added); //3if (DEBUG_LOADERS) {Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - bindTime) + "ms");}} else {Log.i(TAG, "not binding apps: no Launcher activity");}}});...}


注释 1 处获取所有已经安装的符合要求的 Application 信息。注释 2 中将 Application 信息封装成 AppInfo 并添加到 mBgAllAppsList 列表中。注释 3 处会调用 callbacks 的 bindAllApplications 函数并传入 AppInfo 列表,在前面我们得知这个 callbacks 实际是指向 Launcher 的,因此这里调用的是 Launcher 的 bindAllApplications 函数。


下面先看看注释 1 如何获取 Application 信息:


packages/apps/Launcher3/src/com/android/launcher3/compat/LauncherAppsCompatV16.java


LauncherAppsCompatV16#getActivityList()


public List<LauncherActivityInfoCompat> getActivityList(String packageName, UserHandleCompat user) {//1final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);mainIntent.setPackage(packageName);List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0); //2List<LauncherActivityInfoCompat> list =new ArrayList<LauncherActivityInfoCompat>(infos.size());for (ResolveInfo info : infos) {list.add(new LauncherActivityInfoCompatV16(mContext, info));}return list;}


注释 1 处构造带有 ACTION_MAIN 和 CATEGORY_LAUNCHER 的 intent。注释 2 处通过 PackageManagerService.queryIntentActivities 接口来取回系统中所有符合 intent 条件的 Activity,即需要显示到桌面上的应用。(前面启动 PackageManagerService 时,会把系统中的应用程序都解析一遍,然后把解析得到的 Activity 都保存在 mActivities 成员变量中,这里通过这个 mActivities 变量的 queryIntent 函数返回符合 intent 条件的 Activity,即 Action 类型为 Intent.ACTION_MAIN,并且 Category 类型为 Intent.CATEGORY_LAUNCHER 的 Activity)


回退一步,继续来看 Launcher 的 bindAllApplications 函数:


Launcher#bindAllApplications()


public void bindAllApplications(final ArrayList<AppInfo> apps) {if (waitUntilResume(mBindAllApplicationsRunnable, true)) {mTmpAppsList = apps;return;}if (mAppsView != null) {mAppsView.setApps(apps); //1}if (mLauncherCallbacks != null) {mLauncherCallbacks.bindAllApplications(apps);}}


注释 1 处会调用 AllAppsContainerView 的 setApps 函数,并将包含应用信息的列表 apps 传进去。


packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java


AllAppsContainerView#setApps()


public void setApps(List<AppInfo> apps) {mApps.setApps(apps);}


包含应用信息的列表 apps 已经传给了 AllAppsContainerView,查看 AllAppsContainerView 的 onFinishInflate 函数。


AllAppsContainerView#onFinishInflate()


@Overrideprotected void onFinishInflate() {super.onFinishInflate();...// Load the all apps recycler viewmAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);//1mAppsRecyclerView.setApps(mApps);//2mAppsRecyclerView.setLayoutManager(mLayoutManager);mAppsRecyclerView.setAdapter(mAdapter);//3mAppsRecyclerView.setHasFixedSize(true);mAppsRecyclerView.addOnScrollListener(mElevationController);mAppsRecyclerView.setElevationController(mElevationController);...}


onFinishInflate 函数在加载完 xml 文件时就会调用,注释 1 处得到 AllAppsRecyclerView 用来显示 App 列表。注释 2 处将 apps 的信息列表传进去。注释 3 处为 AllAppsRecyclerView 设置 Adapter。到这里,应用程序快捷图标的列表就会显示在屏幕上了。


推荐阅读:做了六年Android,终于熬出头了,15K到31K全靠这份高级面试题+解析


原文作者:huaxun66 原文链接:https://blog.csdn.net/huaxun66/article/details/78149322



总结

最后为了帮助大家深刻理解 Android 相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的 14 套腾讯、字节跳动、阿里、百度等 2021 面试真题解析,我把技术点整理成了视频和 PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。






网上学习 Android 的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。



**本文已被[CODING 开源项目:《Android 学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](


《Android 学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享


https://docs.qq.com/doc/DSkNLaERkbnFoS0ZF)收录**

用户头像

嘟嘟侠客

关注

还未添加个人签名 2021.03.19 加入

还未添加个人简介

评论

发布
暂无评论
Android-Framework学习笔记(四)Launcher启动过程,android高级开发面试题以及答案