启动activity分析 startActivity

startActivity()的细节过程可分为七步,首先从用户单击图标开始。

当用户单击某个应用图标后,执行程序会在该图标的onClick事件中调用startActivity()方法,该方法属于Activity类的内部方法,然后该方法会调用startActivityForResult(),调用时自动把第二个参数设为-1。所以,startActivity()和startActivityForResult()两者没有本质区别。

在forResult方法内部,会调用Instrumentation对象的executeStartActivity()方法,该对象是在ActivityThread创建Activity时创建的,一个应用程序中只有一个Instrumentation对象,每个Activity内部都有一个该对象的引用。Instrumentation可以理解为应用进程的管家,ActivityThread要创建或者暂停某个Activity时,是通过这个"管家"进行的,设置这个管家的好处是可以统计所有的"开销",开销的信息保存在"管家"那里。其实正如其名称所示,Instrumentation就是为了"测量"、"统计",因为管家掌握了所有的"开销",自然也就具有了统计的功能。当然,Instrumentation类和ActivityThread的分工是有本质区别的,后者就像是这个家里的主人,负责创建这个"家庭",并负责和外界打交道,比如接收AmS的通知等。

在Instrumentation中,则继续调用AmS的startActivity()方法,从而真正把启动的意图传递给AmS。Android中IPC调用是同步的,只有当AmS执行完startActivity()方法后才返回,而在此期间,Instrumentation会一直处于线程等待状态。由于一个应用进程一般只有一个主线程,这就意味着应用主线程在这期间会一直等待,所幸的是AmS执行startActivity()是异步的,基本上会在很短的时间内返回,然后使用异步通知机制,回调caller的onActivityResult()方法并返回执行结果。这就是为什么在AmS的startActivity()参数中需要包含一个IBinder类型的token。该参数来源于Activity对象内部的mToken变量,而这个变量对应的是AmS中的一个HistoryRecord,AmS正是通过HistoryRecord来识别客户进程中的Activity的。当AmS执行完指定的Activity后,如果caller需要forResult,AmS就会从HistoryRecord中取出HistoryRecord.app.thread变量,该变量的类型是ActivityThread.ApplicationThread子类,并调用该类的scheduleSendResult(r, list)方法,该方法中的r代表了HistoryRecord。ActivityThread收到这个调用后,将根据参数r判断属于哪个Activity,然后调用相应Activity的onActivityResult()方法。

运行环境检查

当AmS收到启动某个Activity的意图(intent)后,首先要检查一下环境,比如Caller是否有启动的权限、意图是否对应存在的Activity等。这就相当于去医院看病时的导医一样,先进行一些简单的检查,如果不满足条件,则立即返回。

这些具体的检查请参照图10-4所示。包括如下:

首先,根据intent的信息找到匹配的Component信息,这就是为什么intent中可直接指定Activity名称的原因,AmS会调用系统内部的PackageManager查询具体的Component名称。当然,如果没有Component匹配该intent,则返回失败。

接着调用startActivityLocked(caller, intent, ...)方法,AmS中有两个同名该方法,注意其使用的时机和作用,以下篇幅以不同的参数类型表示不同的方法。该方法的后缀是Locked,意思是该方法必须是线程安全的,或者叫不可重入的,即该函数体只能由一个线程调用,另一个线程也要调用该函数时,必须等待前一个线程执行完。

在startActivityLocked(caller, intent, ...)的包含调用中,主要做了以下几件事情。

检查当前正在运行的Activity是否和目标Activity一致,如果一致,则直接返回。这就是说,程序员在Activity使用startActivity启动自己,不会达到重启当前Activity的目的。

处理INTENT_FLAG_FORWARD_RESULT标志。从代码的角度来看,这个标志有一个特殊的作用,就是能够跨Activity传递Result。比如A1→A2,此时如果从A2中启动A3,并且设置的启动标志为FORWARD_RESULT,那么A3运行时,可以在A3中调用setResult,然后finish(),其结果会从A3直接返回到A1,并且A1会得到A3所set的result。要满足这种调用,必须使用以下方式启动。

A1(startActivityForResult) →A2(StartActivity) →A3。注意A2不能使用forResult的方式启动A3,否则会发生冲突START_FORWARD_AND_REQUEST_CONFLICT。

在此检查Caller的app是否存在,这种情况发生在发出startActivity命令后,Caller所在的进程被意外杀死,如果是这样,AmS拒绝继续往下执行。

检查Caller是否具备启动指定Activity的权限。

如果AmS中有IActivityController对象,则通知该Controller进行相应的控制。在一般情况下,mController始终不存在,可能是Android为了某种系统测试而设置的debug开关。

创建一个临时的HistoryRecord对象,该对象只是为了后面调用过程中的各种对比,不一定会最终被加入到mHistory列表中。

检查当前是否允许切换Activity,不允许切换的情况一般发生在当前已经暂停的正在执行的Activity,正在等待下一个Activity的启动时,此时不能进行Activity切换。如果是这样,则把指定的Activity临时添加到mPendingActivityLaunches列表中,等待系统恢复后再继续执行。

判断mPendingActivityLaunches列表中是否有等待的Activity要启动,如果有的话,应当先运行这行等待的Activity。

如果等待序列为空,则调用startActivityUncheckedLocked()方法。此时,要启动Activity已经通过重重检验,被确认为是一个"正当"的启动请求。接下来,AmS就会开始判断以何种方式来启动指定的Activity。

找到或创建合适的Task

前面说过,Task的作用是确保Activity按照指定的方式退出,并当用户按"Back"键时按照指定的方式执行下一个Activity。在真正启动目标Activity之前,AmS先要确定是否需要为目标Activity创建一个新的Task,过程如下。

处理FLAG_ACTIVITY_NO_USER_ACTION标识。在一般情况下,启动一个Activity时都不使用该标识,如果不包含该标识,AmS会判断一定的时间内是否有用户交互。如果没有用户交互的话,AmS会通知Activity回调onUserLeaving()方法,然后再回调onPause()方法,如果使用了该标识,说明目标Activity不和用户交互,所以也就不需要回调onUserLeaving()方法。

确定是否现在就启动,在一般情况下都是立即启动。如果立即启动,就把r.delay Resume为true,意思是不要延迟启动。

处理FLAG_ACTIVITY_PREVIOUS_IS_TOP标志,这个标识基本无用。接着再处理onlyIfNeed参数,这个参数会和该标志一起使用,不过都是基本无用。

为目标Activity赋予合法的权限。目标Activity在安装时会指定所需要的权限,此处正是把这些允许的权限添加到缓存中。

根据launchMode变量设置局部变量launchFlags,因为接下来要根据launchFlags决定启动Activity的方式。注意launchMode和launchFlags是两码事,mode是Activity自己声明的运行方式,它是在AndroidManifest.xml中指定的;而flag是调用者希望以何种方式运行指定的Activity, 是在调用startActivity()时参数intent中指定的。

对NEW_TASK和SINGLE_TASK标识以及SINGLE_INSTANCE模式进行处理。请注意,这两个标志必须在r.resultTo==0的条件中,即这两个标识只能用在startActivity()的方法中,而不能用在startActivityForResult()方法中。因为从Task的角度来看,Android认为不同Task之间的Activity是不能传递数据的,所以不能使用NEW_TASK标识,但是还要调用forResult()方法。

原文地址:https://www.cnblogs.com/itpepe/p/4756328.html