我们点击launcher桌面图标后Activity是如何被启动并显示出来的,下面是流程,我们一步步的讲
Launcher
↓
ATMS
↓
AMS
↓
ProcessList.startProcessLocked()
↓
ZygoteProcess.start()
↓
Zygote Socket
↓
Zygote进程
↓
fork()
↓
新App进程
↓
ActivityThread.main()
Launcher是什么?
Launcher 本质上就是一个 App,而且它的首页就是一个 Activity。
如我的小米手机我查看当前栈顶的Activity,我这里的ll和llf是我自定义的bat就是查看Activity和Fragment的

标红的Launcher就是一个 activity,我们通过这个图会发现小米手机的桌面app是com.miui.home,图标该有的id也是有的。也就是说点击图标就相当点给这个图标设置了点击事件 一样

AOSP中的Launcher
源码位置:
packages/apps/Launcher3
查看源码的地址:https://cs.android.com/

看注释
/**
* Default launcher application. 默认 启动器 应用程序
*/
public class Launcher extends StatefulActivity<LauncherState>
implements Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
PluginListener<LauncherOverlayPlugin> {
}
这个Activity也有布局填充和获取View设置点击事件

在这里可以证实上面说得不错,就是inflate出了View

ItemInflater 的作用:根据 ItemInfo 的类型(应用、快捷方式、文件夹、小部件等)创建对应的 View,并在创建时设置点击监听器

mItemInflater = new ItemInflater<>(this, mAppWidgetHolder, getItemOnClickListener(),
mFocusHandler, new CellLayout(mWorkspace.getContext(), mWorkspace));

看下面的调用链
/**
* Event handler for an app shortcut click.
*
* @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
*/
public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
if (shortcut.isDisabled() && handleDisabledItemClicked(shortcut, launcher)) {
return;
}
// Check for abandoned promise 很多定义系统预装快捷方式点击的时候会触发下面的方法下载应用
if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()
&& (!Flags.enableSupportForArchiving() || !shortcut.isArchived())) {
String packageName = shortcut.getTargetPackage();
if (!TextUtils.isEmpty(packageName)) {
onClickPendingAppItem(
v,
launcher,
packageName,
(shortcut.runtimeStatusFlags
& ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0);
return;
}
}
// Start activities 正常启动应用
startAppShortcutOrInfoActivity(v, shortcut, launcher);
}

Launcher的startActivitySafely是从ActivityContext继承过来的

方法解读:
Android Launcher 中用于安全启动 Activity 的默认方法(default)。它综合考虑了安全模式、快捷方式支持、用户空间、动画回调等场景,确保启动过程健壮且可追踪。
参数解读:
v:触发启动的视图,用于获取启动动画选项及视图边界(作为 Intent#setSourceBounds 的源区域)。
intent:基础启动意图,会被添加 FLAG_ACTIVITY_NEW_TASK 标志。
item:与视图关联的项信息(如应用图标、快捷方式、小部件等),可为 null。
返回值:
若活动成功启动,返回一个 RunnableList(通常用于监听动画结束的回调列表);
若启动失败或被安全模式拦截,返回 null。
default RunnableList startActivitySafely(
View v, Intent intent, @Nullable ItemInfo item) {
Preconditions.assertUIThread();
Context context = (Context) this;
/**
当 Launcher 处于安全模式时,并且非系统应用。
非系统应用被阻止,并提示错误 Toast,返回 null。
**/
if (LauncherAppState.getInstance(context).isSafeModeEnabled()
&& !new ApplicationInfoWrapper(context, intent).isSystem()) {
Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return null;
}
boolean isShortcut = (item instanceof WorkspaceItemInfo)
&& item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& !((WorkspaceItemInfo) item).isPromise();
if (isShortcut && !WIDGETS_ENABLED) {
return null;
}
ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
: makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
UserHandle user = item == null ? null : item.user;
Bundle optsBundle = options.toBundle();
// Prepare intent 准备intent
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (v != null) {
intent.setSourceBounds(Utilities.getViewBounds(v));
}
try {
if (isShortcut) {
String id = ((WorkspaceItemInfo) item).getDeepShortcutId();
String packageName = intent.getPackage(); //获取了包名
// Android 7.1+ 的应用快捷方式
// 你点的不是普通应用图标,而是长按应用图标弹出来的快捷入口(比如长按微信直接点“扫一扫”)
((Context) this).getSystemService(LauncherApps.class).startShortcut(
packageName, id, intent.getSourceBounds(), optsBundle, user);
} else if (user == null || user.equals(Process.myUserHandle())) {
/***
点的是当前登录的这个用户自己的普通应用图标(99% 的情况都属于这里),
直接用最普通的方法打开这个应用 —— 就像你从桌面点微信图标一样,这个时候并不知道打开哪个Activity
所有信息都在Intent里面。
**/
// Could be launching some bookkeeping activity
context.startActivity(intent, optsBundle);
} else {
/***
情景还原:
你在手机里开了两个“空间”——比如自己正常用的空间,还有一个工作空间(工作资料)。两个空间就像个独 立的房间,里面的应用不能串着用普通方式打开
举个例子:
你现在正在自己房间(当前用户),看到桌面上有个钉钉图标,但它其实是工作房间里的钉钉。你一点它所以系统必须换一种方式:找 LauncherApps 这个“管家”,让管家去工作房间里把那个钉钉的主界面启动起来。
***/
context.getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
}
if (item != null) {
//日志记录表示打开应用上面的三种情况都会记录
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
logAppLaunch(getStatsLogManager(), item, instanceId);
}
return options.onEndCallback;
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
}
return null;
}
context.startActivity(intent, optsBundle);
else if (user == null || user.equals(Process.myUserHandle())) {
//点的是当前登录的这个用户自己的普通应用图标(99% 的情况都属于这里)
// 直接用最普通的方法打开这个应用 —— 就像你从桌面点微信图标一样
// Could be launching some bookkeeping activity
context.startActivity(intent, optsBundle);
}
这里的context 被赋值的地方

通过继承关系,我们知道了这个context就是Launcher它是Activity的子类

所以 context.startActivity(intent, optsBundle); 就是Activity的 startActivity
进入了我们可以不借助读网站可以看源码了地方了

一个应用从桌面点击,也就是 Launcher.startActivitySafely 最终会调到startActivityForResult


有个疑问?mInstrumentation 是在Activity的attach里赋值的,但我们点击Launcher上的图标时,我们的应用还在启动我们应用的luancher Activity并没有创建,没有被创建那 mInstrumentation是谁的啊?其实这个阶段 mInstrumentation是启动者的,对于我们的应用(被启动者)这个时候 我们的应用,还有创建阶段。
Instrumentation.execStartActivity方法
android.app.Instrumentation#execStartActivity(android.content.Context, android.os.IBinder, android.os.IBinder, android.app.Activity, android.content.Intent, int, android.os.Bundle)
在 Android 启动流程里,Instrumentation 可以理解成 Activity 启动过程中的“执行官”或者“代理人”,它负责把启动请求发送给ATMS(AMS)
在 Android 启动流程中,Instrumentation 是 ActivityThread 与 Activity 之间的执行代理:
启动 Activity
→ Instrumentation.execStartActivity()
创建 Activity
→ Instrumentation.newActivity()
触发生命周期
→ Instrumentation.callActivityOnXXX()
因此它既负责把启动请求发送给 AMS,又负责在应用进程内创建 Activity 并分发生命周期,是整个 Activity 生命周期中的核心调度者之一。


代码解读:

IBinder b = ServiceManager.getService("activity"); //拿到AMS在Binder驱动中的代理对象系统启动过程中,AMS 创建出来以后,会向 ServiceManager 注册:
ServiceManager.addService(
Context.ACTIVITY_SERVICE, // "activity"
this, // AMS
true
);
这里的 this 就是:
ActivityManagerService extends IActivityManager.Stub
也就是说:
AMS
↓
IActivityManager.Stub
↓
Binder
AMS 本身就是一个 Binder 服务
ServiceManager.getService("activity") 意思就是"把名字叫 activity 的 Binder 给我"

总结:返回向MangerService中注册的AMS(ActivityMangerService) Binder的代码对象
IActivityManager am = asInterface(b);
ActivityManagerNative.getDefault() 跨进程返回的是 ActivityManagerProxy(obj) 

发送
IActivityManager.Stub.Proxy(client)此时持了AMS的binder代理
mRemote.transact(START_ACTIVITIES_TRANSACTION, data, reply, 0); //进行IPC(跨进程通信)

对于Binder机制 transact是发送 onTransatct是接收
接收
也要用这个判断 START_ACTIVITIES_TRANSACTION

再重复记忆: ActivityManagerService,是一个Stub(server)
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {}
IApplicationThread 它也是一个Binder 系统私有API,用于与应用程序通信。应用程序启动时会向活动管理器(AMS)提供该信息,以便活动管理器告知应用需要完成的事情
public interface IApplicationThread extends IInterface {}
IApplicationThread 看上面标红的 IApplicationThread app = ApplicationThreadNative.asInterface(b) 它还是 ServiceManger.get("Activity") ; 上面说的 它是 AMS在Binder驱动中的代码对象

那 IBinder b = data.readStrongBinder(); 这里的b是什么
b = “调用方(Launcher / App)传过来的 ApplicationThread Binder”
看看调用方,到这里应该明白readStrongBinder了,因为调用方是write

类的关系
class ApplicationThread extends ApplicationThreadNative;
abstract class ApplicationThreadNative extends Binder implements IApplicationThread;
interface IApplicationThread extends IInterface
class ActivityManagerService extends IActivityManager.Stub
/**
用于与 Activity Manager Service(AMS)通信的系统内部 API。它提供了一组接口,
使应用进程能够回调到 Activity Manager(定义了AMS对处暴露的方法) 应用进程内并拿不到
AMS `ActivityManagerService ams = ??? // 不可能` ,因为AMS运行在SystemServer进程中,所以只能拿到 IActivityManager 这个Proxy
**/
interface IActivityManager extends IInterface
abstract class ActivityManagerNative extends Binder implements IActivityManager
// AMS继承自 IActivityManager.Stub, ActivityManagerNative 实现了 IActivityManager
IActivityManager 定义了AMS对处暴露的方法

Binder b = ServiceManager.getService("activity"); 返回的这个binder到底是什么?
ServiceManager.getService("activity") 返回的对象是一个远程服务的“代理”对象(Proxy),它实现了 IBinder 接口。它并不是 ActivityManagerService (AMS) 本身,而是你所在App进程与系统服务(AMS)进程进行跨进程通信(IPC)的信使和代表。具体来说,它是
BinderProxy 类型的对象,在Native层对应的是 BpBinder你可以把它理解为AMS在客户端进程中的一个“替身”或“遥控器”:通过它,你的App可以像调用本地方法一样,向远在 system_server 进程的AMS发送请求。ActivityManagerNative.getDefault()的返回值:类型是 IActivityManager
如果是同进程内:


进入到下面

android.app.ActivityManagerProxy#startActivity
通过上面的说明:这个mRemote就是我们说的 AMS的替身

上面的发送后会进入AMS的onTransact里处理
com.android.server.am.ActivityManagerService#onTransact

如何理解后续流程?
首先你要了解理解Binder机制,如果只看源码的话,这块多少会让人懵逼。
我们现在走到了ActivityMangerNative.ActivityMangerProxy() 你记着它是一个BinderProxy,在其StartActivity中的mRemote就通过ServiceManger.get("Activity") 获取的 AMS中的Binder驱动中的代码,通过这个Binder发送出去的信息会被ActivityManagerService接收

进入其Super,这个时候的this,已经是AMS了也就说虽然super.Transact 进入到了 ActivityMangerNative的onTransact,但些时和this指向是是AMS而非 ActivityManagerProxy了
android.app.ActivityManagerNative#onTransact

进入到到ActivityManagerServcice的startActivity
下面每一个方法只总结作用
com.android.server.am.ActivityManagerService#startActivity:
“入口转发 + 自动补 userId”,不做任何真正逻辑
com.android.server.am.ActivityManagerService#startActivityAsUser:
com.android.server.am.ActivityStackSupervisor#startActivityMayWait:
解析 Intent
PMS查询目标Activity
获取 ActivityInfo
创建 ActivityRecord
com.android.server.am.ActivityStackSupervisor#startActivityLocked
com.android.server.am.ActivityStackSupervisor#startActivityUncheckedLocked
这里是启动模式处理核心:
处理:
singleTask
singleTop
singleInstance
FLAG_ACTIVITY_NEW_TASK
任务栈复用等。


com.android.server.am.ActivityStack#resumeTopActivityInnerLocked
pause当前Activity
切换Task
准备启动目标Activity
com.android.server.am.ActivityStackSupervisor#startSpecificActivityLocked
判断目标进程是否存在,分为不同的启动方式
ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); (如果app不等于null)说明当前应用已经被启动,也就是普通的App内StartActivity,不是启动当前应用的laucherActivityr.task.stack.setLaunchTime(r);性能中埋点使用的。我们既然是看Activity的启动流程 那就看这个
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
会调用入到
com.android.server.am.ActivityManagerService#startProcessLocked(com.android.server.am.ProcessRecord, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])

race.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName); // 这个追踪就是我们在 Perfetto上看到的setup时间轴
android.os.Process#startViaZygote
android.os.Process#zygoteSendArgsAndGetResult _它会把参数通过 Socket向Zygote 发送命令,同时会把参数写入到本地,只所以写入本地因为 ZygoteInit.readArgumentList()有读取 _
--runtime-args
--setuid=10086
--setgid=10086
--nice-name=com.demo.app
android.app.ActivityThread
com.android.internal.os.ZygoteInit
Zygote 进程的启动类
通过上面的Process.start把Zygote进程打开
预先初始化部分类,然后在一个 UNIX 域套接字上等待命令。
根据这些命令,派生(fork)出继承 VM 初始状态的子进程。


com.android.internal.os.ZygoteConnection#runOnce
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(
new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
parsedArgs = new Arguments(args);
if (parsedArgs.abiListQuery) {
return handleAbiListQuery();
}
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
}
applyUidSecurityPolicy(parsedArgs, peer);
applyInvokeWithSecurityPolicy(parsedArgs, peer);
applyDebuggerSystemProperty(parsedArgs);
applyInvokeWithSystemProperty(parsedArgs);
int[][] rlimits = null;
if (parsedArgs.rlimits != null) {
rlimits = parsedArgs.rlimits.toArray(intArray2d);
}
if (parsedArgs.invokeWith != null) {
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
Os.fcntlInt(childPipeFd, F_SETFD, 0);
}
/**
* In order to avoid leaking descriptors to the Zygote child,
* the native code must close the two Zygote socket descriptors
* in the child process before it switches from Zygote-root to
* the UID and privileges of the application being launched.
*
* In order to avoid "bad file descriptor" errors when the
* two LocalSocket objects are closed, the Posix file
* descriptors are released via a dup2() call which closes
* the socket and substitutes an open descriptor to /dev/null.
*/
int [] fdsToClose = { -1, -1 };
FileDescriptor fd = mSocket.getFileDescriptor();
if (fd != null) {
fdsToClose[0] = fd.getInt$();
}
fd = ZygoteInit.getServerSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();
}
fd = null;
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
args = readArgumentList(); 读取Process.start传过来的args
从Zygote进程孵化出一个新的子进程,并根据传入的参数进行一些设置,使其具备特定身份和运行环境的应用进程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); 处理这个孵化出来的新的子进程

com.android.internal.os.RuntimeInit#zygoteInit
com.android.internal.os.RuntimeInit#applicationInit

com.android.internal.os.RuntimeInit#invokeStaticMain
通过反射执行
ActivityThread.main方法
为什么说是ActivityThread.main 怎么佐证?


可以一下Process的write和 com.android.internal.os.RuntimeInit.Arguments#parseArgs读


Process.start中 Zygote参数最后一个是 processClass 它就是ActivityThread

所以在RuntimeInit中


至此比较难懂的流程是结束了,下面总结一下:
1、从Launcher程序的首页 具体是 launcher3/Launcher.java(它是一个Activity)
它有Inflate也有布局容器,通过 ItemInfo的类型创建对应的View,并绑定点击事件,然后通过一系列的调用链最终调用到context.startActivity
android.app.Instrumentation#execStartActivities
(Instrumentation,是ActivityThread与Activity之间的执行代理)负责Activity的启动、创建、生命周期的回调,它负责把启动请求发送给AMS,它是Activity使命周期的核心调度者之一。
IBinder b = ServiceManager.getService("activity"); 获取 AMS 在Binder驱动中的代理对象
ActivityMangerProxy 实现了 IActivityManager (可以看到AMS在了变身)因为 ActivityMangerProxy 实现了 IActivityManager (操作Activity的模板)
IApplicationThread:它也是一个Binder是系统的私有Api,用于与应用程序通信。应用启动时会向AMS提供该信息,以更AMS完成任务时通过 IApplicationThread告知当前应用
android.app.ActivityManagerProxy#startActivity 向AMS发送 启动Activity事务(这里发生了IPC)
com.android.server.am.ActivityManagerService#onTransact (通过 uper.onTransact(code, data, reply, flags))又回去了
com.android.server.am.ActivityManagerService#startActivity
com.android.server.am.ActivityManagerService#startActivityAsUser
com.android.server.am.ActivityStackSupervisor#startActivityMayWait (解析intent 获取ActivityInfo创建 ActivityRecord 等)
com.android.server.am.ActivityStackSupervisor#startActivityLocked
com.android.server.am.ActivityStackSupervisor#startActivityUncheckedLocked(获取启动模式并封装成bundle)
com.android.server.am.ActivityStack#resumeTopActivityLocked(ActivityRecord, android.os.Bundle)
com.android.server.am.ActivityStack#resumeTopActivityInnerLocked (暂停当前前Activity 是否切换Task 准备Activity)

com.android.server.am.ActivityStackSupervisor#startSpecificActivityLocked

mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true);
com.android.server.am.ActivityManagerService#startProcessLocked(java.lang.String, android.content.pm.ApplicationInfo, boolean, int, java.lang.String, android.content.ComponentName, boolean, boolean, int, boolean, java.lang.String, java.lang.String, java.lang.String[], java.lang.Runnable)
startProcessLocked( app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs);
android.os.Process#start
android.os.Process#startViaZygote
android.os.Process#zygoteSendArgsAndGetResult (打开socket连接与zygote进程通信)
android.os.Process#openZygoteSocketIfNeeded (打开socket连接)
然后进入到了 com.android.internal.os.ZygoteInit#main
com.android.internal.os.ZygoteInit#runSelectLoop
com.android.internal.os.ZygoteConnection#runOnce (Zygote进程 fork出一个新的子进程也就是我们当前需要启动的应用,)
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
}
com.android.internal.os.ZygoteConnection#handleChildProc
if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); } 这就是设置我们Debug的时候进程的名字
com.android.internal.os.RuntimeInit#zygoteInit
com.android.internal.os.RuntimeInit#applicationInit
com.android.internal.os.RuntimeInit#invokeStaticMain
com.android.internal.os.ZygoteInit.MethodAndArgsCaller#run (最终会执行到了ActivityThread.main)