本文参考的源码(7.1.1_r6)

Launcher也是一个应用程序,和我们的App没有什么区别,当用户点击应用图标时候,启动其他的App,本文主要为分析Activity的启动流程打基础。

Launcher.java代码量比较多,大约4500多行,但是里面的逻辑并不复杂,不过我这里分析它的意义主要为了更好理解后面的知识,了解Launcher具体的流程才是重要的,不要沉迷代码中无法自拔!

先来张图:

在线源码地址

分析

先看下它的manifest文件,这里面的内容不多,Launcher类是一个Activity,只是比我们普通的app多一个

xmlns:android="http://schemas.android.com/apk/res/android"

package="com.android.launcher3">

android:name="com.android.launcher3.permission.READ_SETTINGS"

android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

android:protectionLevel="normal"

android:label="@string/permlab_read_settings"

android:description="@string/permdesc_read_settings"/>

android:name="com.android.launcher3.permission.WRITE_SETTINGS"

android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

android:protectionLevel="signatureOrSystem"

android:label="@string/permlab_write_settings"

android:description="@string/permdesc_write_settings"/>

android:backupAgent="com.android.launcher3.LauncherBackupAgent"

android:fullBackupOnly="true"

android:fullBackupContent="@xml/backupscheme"

android:hardwareAccelerated="true"

android:icon="@mipmap/ic_launcher_home"

android:label="@string/derived_app_name"

android:largeHeap="@bool/config_largeHeap"

android:restoreAnyVersion="true"

android:supportsRtl="true" >

android:name="com.android.launcher3.Launcher"

android:launchMode="singleTask"

android:clearTaskOnLaunch="true"

android:stateNotNeeded="true"

android:theme="@style/LauncherTheme"

android:windowSoftInputMode="adjustPan"

android:screenOrientation="nosensor"

android:configChanges="keyboard|keyboardHidden|navigation"

android:resumeWhilePausing="true"

android:taskAffinity=""

android:enabled="true">

android:name="com.android.launcher3.SettingsActivity"

android:label="@string/settings_button_text"

android:autoRemoveFromRecents="true">

android:name="com.android.launcher3.LauncherProvider"

android:authorities="com.android.launcher3.settings"

android:exported="true"

android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS"

android:readPermission="com.android.launcher3.permission.READ_SETTINGS" />

为了研究方便,删除了大量代码,完整的请查阅源码。

先查看onCreate方法

class Launcher {

AllAppsContainerView mAppsView;// 桌面app的布局(重要)

@Override

protected void onCreate(Bundle savedInstanceState) {

// 省略.....

if (mLauncherCallbacks != null) {

mLauncherCallbacks.preOnCreate();

}

super.onCreate(savedInstanceState);

LauncherAppState app = LauncherAppState.getInstance();

mModel = app.setLauncher(this);

setContentView(R.layout.launcher);

setupViews();

lockAllApps();

// We only load the page synchronously if the user rotates (or triggers a

// configuration change) while launcher is in the foreground

if (!mModel.startLoader(mWorkspace.getRestorePage())) {

// If we are not binding synchronously, show a fade in animation when

// the first page bind completes.

mDragLayer.setAlpha(0);

} else {

setWorkspaceLoading(true);

}

// On large interfaces, or on devices that a user has specifically enabled screen rotation,

// we want the screen to auto-rotate based on the current orientation

setOrientation();

}

}

跟踪mModel.startLoader() 方法,mModel是一个LauncherModel类,

class LauncherModel extends BroadcastReceiver

这个类是一个BroadcastReceiver,但是没有发现在Manifest中注册,注意上面的 app.setLauncher(this)方法,是的,它是在LauncherAppState 里面动态注册的

class LauncherAppState {

private LauncherAppState() {

// .....................

// Register intent receivers

IntentFilter filter = new IntentFilter();

filter.addAction(Intent.ACTION_LOCALE_CHANGED);

// For handling managed profiles

filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);

// For extracting colors from the wallpaper

if (Utilities.isNycOrAbove()) {

// TODO: add a broadcast entry to the manifest for pre-N.

filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);

}

sContext.registerReceiver(mModel, filter);

}

}

这个不是重点,继续追踪mModel.startLoader()方法。

public boolean startLoader(int synchronousBindPage) {

// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems

InstallShortcutReceiver.enableInstallQueue();

synchronized (mLock) {

// Don't bother to start the thread if we know it's not going to do anything

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();

}

});

// If there is already one running, tell it to stop.

stopLoaderLocked();

mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);

// TODO: mDeepShortcutsLoaded does not need to be true for synchronous bind.

if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded

&& mWorkspaceLoaded && mDeepShortcutsLoaded && !mIsLoaderTaskRunning) {

mLoaderTask.runBindSynchronousPage(synchronousBindPage);

return true;

} else {

sWorkerThread.setPriority(Thread.NORM_PRIORITY);

sWorker.post(mLoaderTask);

}

}

}

return false;

}

该类中synchronized 块有个mLoaderTask = new LoaderTask();查看LoaderTask源码,发现LoaderTask是LauncherModel的内部类,而且是Runnable 类型,直接查看其run方法。

private class LoaderTask implements Runnable {

// .............

public void run() {

synchronized (mLock) {

if (mStopped) {

return;

}

mIsLoaderTaskRunning = true;

}

// Optimize for end-user experience: if the Launcher is up and // running with the

// All Apps interface in the foreground, load All Apps first. Otherwise, load the

// workspace first (default).

keep_running: {

if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");

loadAndBindWorkspace();

if (mStopped) {

break keep_running;

}

waitForIdle();

// second step

if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");

loadAndBindAllApps();

waitForIdle();

// third step

if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");

loadAndBindDeepShortcuts();

}

// Clear out this reference, otherwise we end up holding it until all of the

// callback runnables are done.

mContext = null;

synchronized (mLock) {

// If we are still the last one to be scheduled, remove ourselves.

if (mLoaderTask == this) {

mLoaderTask = null;

}

mIsLoaderTaskRunning = false;

mHasLoaderCompletedOnce = true;

}

}

}

step 1: loading workspace

step 2: loading all apps

step 3: loading deep shortcuts

日志写的非常清楚,就是加载所有app,图标之类的。

private void loadAndBindAllApps() {

// ............ 略

loadAllApps();

}

下面逻辑是载入桌面所有app,并使用handler切换UI线程然后给所有应用bind回调函数。

private void loadAllApps() {

// ............ 略

mBgAllAppsList.clear();

for (UserHandleCompat user : profiles) {

// ............ 略

final List apps = mLauncherApps.getActivityList(null, user);

for (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));

}

// ............ 略

}

final ArrayList added = mBgAllAppsList.added;

callbacks.bindAllApplications(added);

mHandler.post(new Runnable() {

final Callbacks callbacks = tryGetCallbacks(oldCallbacks);

if (callbacks != null) {

callbacks.bindAllApplications(added);

}

}

}

Launcher的布局控件

我们最开始提到Launcher的一个成员变量,AllAppsContainerView,这个类是一个自定义ViewGroup,如下:

/**

* The all apps view container.

*/

public class AllAppsContainerView extends BaseContainerView implements DragSource,

LauncherTransitionable, View.OnLongClickListener, AllAppsSearchBarController.Callbacks {

private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;

private static final int MAX_NUM_MERGES_PHONE = 2;

private final Launcher mLauncher;

private final AlphabeticalAppsList mApps;

private final AllAppsGridAdapter mAdapter;

private final RecyclerView.LayoutManager mLayoutManager;

private final RecyclerView.ItemDecoration mItemDecoration;

// The computed bounds of the container

private final Rect mContentBounds = new Rect();

private AllAppsRecyclerView mAppsRecyclerView;

其中BaseContainerView extends FrameLayout,比较简单。

我们看下它的成员变量-> AllAppsRecyclerView,这是个自定义的RecyclerView,说明它是用RecyclerView对桌面apps布局的。

查看AllAppsGridAdapter ,因为RecyclerView的事件监听 一般是在这里面设置的:

public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener

iconClickListener, View.OnLongClickListener iconLongClickListener) {

// 略 ..............

}

AllAppsGridAdapter的构造函数的参数和AllAppsContainerView的构造函数的方法体来次对比:

public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

// 略 ....

mLauncher = Launcher.getLauncher(context);

mApps = new AlphabeticalAppsList(context);

mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);

mApps.setAdapter(mAdapter);

}

惊喜发现-> AllAppsGridAdapter 构造函数的第三个参数是mLauncher,而且是View.OnClickListener类型。

再次回到Launcher,Launcher实现了 View.OnClickListener,直接找到

public void onClick(View v)方法,到这步就非常简单了,next->next->next...

这些步骤的代码就无须贴了,最终会执行到startActivity,即分析 Activity启动流程的重要入口。

onClick->onClickAppShortcut->startAppShortcutOrInfoActivity->startActivitySafely->startActivity

android launcher启动过程,Android应用启动过程-Launcher源码浅析相关推荐

  1. 深入探索Android 启动优化(七) - JetPack App Startup 使用及源码浅析

    本文首发我的微信公众号:徐公,想成为一名优秀的 Android 开发者,需要一份完备的 知识体系,在这里,让我们一起成长,变得更好~. 前言 前一阵子,写了几篇 Android 启动优化的文章,主要是 ...

  2. 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. Android Loader机制全面详解及源码浅析

    原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...

  4. android飞行射击游戏代码,android 3D飞行射击游戏《夜鹰行动》源码

    压缩包内容概览: android 3D飞行射击游戏<夜鹰行动>源码-airattacker ; 清单 ; 资产 ; 项目 ; 飞骥11 ; 飞骥22 ; 飞骥33 ; 折叠按钮 ; 弗雷格 ...

  5. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  6. Android应用进程间通信之Messenger信使使用及源码浅析

    转载: http://blog.csdn.net/yanbober 1 背景 这个知识点是个low货,刚开始其实想在之前一篇文章<Android异步消息处理机制详解及源码分析>一文中作为一 ...

  7. Android Jetpack架构组件之 Room(使用、源码篇)

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发 ...

  8. Android Glide图片加载框架(二)源码解析之into()

    文章目录 一.前言 二.源码解析 1.into(ImageView) 2.GlideContext.buildImageViewTarget() 3.RequestBuilder.into(Targe ...

  9. 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 JobMasger启动 YarnJobClusterEntrypoint

    1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [三] 上一章:[flink]Flink 1.12.2 源码浅析 : yarn-per-job模式解析 yar ...

  10. Android应用进程间通信之Messenger信使使用及源码浅析,Android插件化入门指南

    } } break; default: super.handleMessage(msg); break; } } } } 工程中的一个独立进程客户端client代码: public class Mai ...

最新文章

  1. 矿大计算机控制技术,潜心科研,匠心育人,来看看矿大这位带领国内顶尖团队的“大拿”的多面人生!...
  2. 环境微生物期刊—FEMS Microbiology Ecology
  3. 问得最多的十个JavaScript前端面试问题
  4. 只要暴风骤雨才能使人迅速地成长
  5. 图片实现 提交/重置 按钮
  6. 红帽linux配置apache,红帽linux9中Apache服务器的配置
  7. 提高篇 第一部分 基础算法 第4章 广搜的优化技巧
  8. 武汉大学计算机学院 情感分析,跨语言情感分析方法研究
  9. 李航《统计学习方法》第一章课后答案链接
  10. python env虚拟环境
  11. Java学习资料整合
  12. 网件WNDR4300刷openwrt/LEDE固件
  13. 项目管理第六章项目进度管理
  14. SQL注入SQLmap简单用法,和SQL注入写入一句话木马
  15. DDNS设置(自用)
  16. 工作表保护密码忘了怎么办?
  17. HTML5系列代码:使用自定义图像来作为空距
  18. 极客日报:腾讯视频、优酷、爱奇艺取消超前点播;苹果为小学生推出编程指南;Win11会导致游戏性能下降
  19. 用javaScript制作爱心特效
  20. MIPI DSI转LVDS的桥接芯片,其应用图如下: ICN6202

热门文章

  1. [BZOJ1050] [HAOI2006] 旅行comf (Kruskal, LCT)
  2. Centos7云服务器部署SpringBoot项目(手动配置环境篇)
  3. 转torchscript报错:Expected a value of type ‘Tensor (inferred)‘ for argument ‘scale‘ but instead found t
  4. 【修真院“善良”系列之四】怎么识别招聘中的传销公司?
  5. 硬件知识——主板结构
  6. [DAX] 日期时间函数
  7. python画图如何调整图例位置_python图例参数
  8. 即时通讯sdk哪一个好?
  9. 黑盒测试方法-----状态转换图
  10. 2018年第九届蓝桥杯B组第四题:摔手机题解