Launcher 启动流程


Launcher 启动流程
一、startOtherServices()............................................................................................................ 3
二、systemReady()..................................................................................................................... 4
三、startHomeActivityLocked()............................................................................................ 5
四、onclick()............................................................................................................................... 6
五、startActivity( )......................................................................................................................7
六、父类的startActivity()..........................................................................................................8
七、startActivityForResult()..................................................................................................8
八、Instrumentation.java...........................................................................................................9
总结: ......................................................................................................................................10
在进入此篇文档的主题之前,我们先来了解一下虚拟机运行字节
码的相关知识点。
Dalvik 虚拟机它运行的是经过优化和压缩的DEX 字节码,占用空
间更小,而且还可以进一步优化,Android SDK 中专门提供了dx 工具
把传统的Java 字节码转换为Dalvik 虚拟机可以运行的DEX 字节码,
这个转换过程是在程序编译的时候就完成了。
为了便于传播,Android SDK 将程序编译后的DEX 字节码文件、资
源文件、lib 库、AndroidManifest.xml 等文件一起打包压缩为apk 文件
(其实就是一个zip 压缩文件),因此,apk 的安装与卸载其实就是对
这个zip 压缩包里面的文件进行解压分析拷贝和优化的过程。
由于DEX 字节码位于apk 压缩包中,因此,如果程序启动的时候,
每次都要从apk 文件中解压提取DEX 字节码,明显效率不高,因此,
Android 系统设计了如下策略:
(1) 创建一个"dalvik-cache"文件夹,专门存放DEX 字节码,具
体位于/data/dalvik-cache
(2) 每次安装新的apk 的时候,也同样提取DEX 字节码,优化
后放入dalvik-cache 目录中
(3) 用户点击应用图标后,直接从dalvik-cache 目录中快速加载
优化过的DEX 字节码,这样程序就可以很快的启动了。
Note:可以通过一下命令将DEX 字节码的文件导出来看看:
adb pull /data/dalvik-cache ./
引入:经过上面的阐述,我们大致了解了一下虚拟机运行一个应用
的大致过程。那么具体的流程又是怎样的呢?下面我们从一个点击图
标的事件开始往后跟踪流程吧~!
我们先来看看Launcher.java 。它在V12BN 项目中是位
于./android/packages/apps/Launcher2/src/com/android/launcher2/ 目
录下。当我们去点击一个应用图标时,它会去加载这个类。这个类继
承自Activity 类。
应用程序Launcher 在启动过程中会请求PackageManagerService
返回系统中已经安装的应用程序的信息,并将这些信息封装成一个快
捷图标列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标
来启动相应的应用程序。
Launcher Activity Instrumentation ActivityManagerProxy
ActivityManagerService
1 : startActivity()
2 : startActivityForResult()
3 : execStartActivity()
4 : startActivity()
5 : startActivity()
图1、Launcher 启动时序图.
一、startOtherServices()
启动Launcher 的入口为ActivityManagerService 的systemReady()
Path :./frameworks/base/services/core/java/com/android/server/am/
private void startOtherServices() {
...
mActivityManagerService.systemReady(new Runnable() {
public void run() {
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
...}...}
Note: 从它的slog 可以看出来“ 做好服务的准备”;
PHASE_ACTIVITY_MANAGER_READY,当Activitymanager 准备就绪之后,
mount service 就要做这步操作。
二、systemReady()
public void systemReady(final Runnable goingCallback) {
...
synchronized (this) {
...
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1,
currentUserId);
}
}
Note:在这个方法中,有很多的同步锁synchronized,确保进程的安全。
在函数的最后调用了ActivityStackSupervisor 的
resumeFocusedStackTopActivityLocked 函数。ActivityStack 对象是用来
描述Activity 堆栈的。其次,在ActivityStackSupervisor 中又会去调用
ActivityManagerService 的startHomeActivityLocked 函数。
三、startHomeActivityLocked()
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
return false;
}
Intent intent = getHomeIntent();
..............
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() |
Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo,
reason);}} else { } return true; //启动应用程序
}
Note:FactoryTest 是系统的运行模式分为三种,分别是非工厂模式、低
级工厂模式和高级工厂模式。
mTopAction 则用来描述第一个被启动Activity 组件的Action,它的
值为Intent.ACTION_MAIN。
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;
}
这里的CATEGORY_HOME 标志应用程序是否已经启动,如果没有。
继续根据后面的if 语句判断启动应用程序。
综上所述,我们可以得知Launcher 的Manifest 文件中的intent-filter
标签匹配了Action 为Intent.ACTION_MAIN , Category 为
Intent.CATEGORY_HOME。
这样,应用程序Launcher 就会被启动起来,并执行它的onCreate
函数。
四、onclick()
当点击一个应用图标的时候,它会通过一个onclick()方法去响应
这个点击事件。源码如下所示:
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
boolean success = startActivitySafely(v, intent, tag);
...........
}
Note:首先,从它的注释中也可以看出来,这个响应事件主要是用来启动被单击
的快捷方式的意图。在这个点击事件中,它还会去调用一个重要的方法
startActivitySafely()方法。startActivitySafely()主要返回的是一个boolean 类型的值,
表示是否能成功启动一个Activity。在startActivitySafely()中还会去调用
startActivity()方法,下面我们来看一下这个方法。
注意:当我们指定第一个APP 入口的Activity 的时候,一般都会加下面这两个
默认的属性:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
五、startActivity( )
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 设置启动
模式为singleTask 模式
try {
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
if (useLaunchAnimation) {
if (user == null || user.equals(android.os.Process.myUserHandle()))
{
startActivity(intent, opts.toBundle());//父类的方法
} else { ........ }
Note: 从addFlags()方法中可以看出来应用的启动模式默认设置为
栈内复用模式。在这个方法中它还会继续调用父类的startActivity()
方法。
六、父类的startActivity()
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
Note:这个方法继续调用startActivityForResult()目的是为了获得兼
容性,因为应用程序可能会重写此方法。当第二个参数传进去的值为
1 时,表示不需要返回Activity 的结果。
七、startActivityForResult()
public void startActivityForResult(Intent intent, int requestCode, Bundle
options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken,
this,intent, requestCode, options);
} else { .....}
Note:这里的Intrumentation 是用来监控应用程序和系统的交互。
mMainThread 代表的是应用程序的主线程, 这里通过
mMainThread.getApplicationThread 获得它里面的ApplicationThread 成
员变量,它是一个Binder 对象。ActivityManagerService 会使用它来和
ActivityThread 进行进程间通信
mToken 是一个Binder 对象的远程接口。
八、Instrumentation.java
Path:./frameworks/base/core/java/android/app/
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
Note: 首先会调用ActivityManagerNative 的getDefault 来获取
ActivityManageService 的代理对象ActivitymanagerProxy , 接着调用它的
startActivity 方法。
总结: 至此,调用了ActivityManageService 的startActivity()方法之后,它会走
到ApplicationThread.java 中。开始一些列的后续Activity 的启动。因为Launcher
本身就是一个Activity,后续涉及到的启动过程与Launcher 没有什么差别。这里
就不再跟代码进行分析了。

猜你喜欢

转载自blog.csdn.net/Toc_SunWinner/article/details/79314743