android launcher启动过程,Android应用启动过程-Launcher源码浅析
本文参考的源码(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源码浅析相关推荐
- 深入探索Android 启动优化(七) - JetPack App Startup 使用及源码浅析
本文首发我的微信公众号:徐公,想成为一名优秀的 Android 开发者,需要一份完备的 知识体系,在这里,让我们一起成长,变得更好~. 前言 前一阵子,写了几篇 Android 启动优化的文章,主要是 ...
- 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- Android Loader机制全面详解及源码浅析
原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...
- android飞行射击游戏代码,android 3D飞行射击游戏《夜鹰行动》源码
压缩包内容概览: android 3D飞行射击游戏<夜鹰行动>源码-airattacker ; 清单 ; 资产 ; 项目 ; 飞骥11 ; 飞骥22 ; 飞骥33 ; 折叠按钮 ; 弗雷格 ...
- Android开发之Theme、Style探索及源码浅析
1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...
- Android应用进程间通信之Messenger信使使用及源码浅析
转载: http://blog.csdn.net/yanbober 1 背景 这个知识点是个low货,刚开始其实想在之前一篇文章<Android异步消息处理机制详解及源码分析>一文中作为一 ...
- Android Jetpack架构组件之 Room(使用、源码篇)
2019独角兽企业重金招聘Python工程师标准>>> 1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发 ...
- Android Glide图片加载框架(二)源码解析之into()
文章目录 一.前言 二.源码解析 1.into(ImageView) 2.GlideContext.buildImageViewTarget() 3.RequestBuilder.into(Targe ...
- 【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 ...
- Android应用进程间通信之Messenger信使使用及源码浅析,Android插件化入门指南
} } break; default: super.handleMessage(msg); break; } } } } 工程中的一个独立进程客户端client代码: public class Mai ...
最新文章
- 矿大计算机控制技术,潜心科研,匠心育人,来看看矿大这位带领国内顶尖团队的“大拿”的多面人生!...
- 环境微生物期刊—FEMS Microbiology Ecology
- 问得最多的十个JavaScript前端面试问题
- 只要暴风骤雨才能使人迅速地成长
- 图片实现 提交/重置 按钮
- 红帽linux配置apache,红帽linux9中Apache服务器的配置
- 提高篇 第一部分 基础算法 第4章 广搜的优化技巧
- 武汉大学计算机学院 情感分析,跨语言情感分析方法研究
- 李航《统计学习方法》第一章课后答案链接
- python env虚拟环境
- Java学习资料整合
- 网件WNDR4300刷openwrt/LEDE固件
- 项目管理第六章项目进度管理
- SQL注入SQLmap简单用法,和SQL注入写入一句话木马
- DDNS设置(自用)
- 工作表保护密码忘了怎么办?
- HTML5系列代码:使用自定义图像来作为空距
- 极客日报:腾讯视频、优酷、爱奇艺取消超前点播;苹果为小学生推出编程指南;Win11会导致游戏性能下降
- 用javaScript制作爱心特效
- MIPI DSI转LVDS的桥接芯片,其应用图如下: ICN6202
热门文章
- [BZOJ1050] [HAOI2006] 旅行comf (Kruskal, LCT)
- Centos7云服务器部署SpringBoot项目(手动配置环境篇)
- 转torchscript报错:Expected a value of type ‘Tensor (inferred)‘ for argument ‘scale‘ but instead found t
- 【修真院“善良”系列之四】怎么识别招聘中的传销公司?
- 硬件知识——主板结构
- [DAX] 日期时间函数
- python画图如何调整图例位置_python图例参数
- 即时通讯sdk哪一个好?
- 黑盒测试方法-----状态转换图
- 2018年第九届蓝桥杯B组第四题:摔手机题解