Android Framework-管理Activity和组件运行状态的系统进程—— ActivityManagerService(AMS)

news2024/9/25 1:16:47

ActivityManagerService(AMS)是Android提供的一个用于管理Activity(和其他组件)运行状态的系统进程

AMS功能概述

和WMS一样,AMS也是寄存于systemServer中的。它会在系统启动时,创建一个线程来循环处理客户的请求。值得一提的是,AMS会向ServiceManager登记多种Binder Server如“activity” “meminfo” “cpuinfo”等——不过只有第一个“activity”才是AMS的“主业”,并由Activity ManagerService实现;剩余服务的功能则是由其他类提供的
先来看看AMS的启动过程。如下所示:

/*frameworks/base/services/java/com/android/server/SystemServer
.java*/
public void run() {
 …
 Slog.i(TAG, "Activity Manager");
 context = ActivityManagerService.main(factoryTest); //启动AMS
 …
 ActivityManagerService.setSystemProcess(); //向ServiceManager注册}

ActivityManagerService提供了一个静态的main函数,通过它可以轻松地启动AMS。然后还需要调用setSystemProcess来把这个重要系统服务注册到ServiceManager。由此可见它和WMS一样,都是“实名”
的Binder Server:

/*frameworks/base/services/java/com/android/server/am/ActivityM
anagerService.java*/
 public static final Context main(int factoryTest) {
 AThread thr = new AThread(); //创建AMS线程
 thr.start(); //启动AMS线程
 synchronized (thr) {
 while (thr.mService == null) {/*注意,这段代码是运行在SystemServer所在线程中的。
 所以通过mService是否为空来判断AMS成功启动与否:如果是的话就可以返回SystemServer 继续执行,否则就一直等待。Android在处理“系统级进程”出错时的普遍态度是“既然系统都出错 了,任何补救都是无力回天的”,所以它的异常处理部分经常是空的
*/
 try {
 thr.wait();
 } catch (InterruptedException e) {
 }
 }
 }
 …
 m.mMainStack = new ActivityStack(m, context, true); /*创建一个ActivityStack对象,  这是AMS的核心,很多工作都是围绕它展开的*/return context;
 }

对于SystemServer所在线程来说,它需要等到AThread(即上述的变量thr)成功启动后才能继续往下执行。所以当thr.start()后,就通过thr.wait()进入等待。那么,什么时候唤醒呢?答案就在AThread内部:

 public void run() {synchronized (this) {
 mService = m;
 mLooper = Looper.myLooper();
 notifyAll();
 }

上面的notifyAll会唤醒所有在thr这个object所在等待队列上的目标,自然也就包括了SystemServer所属线程。这么做的原因是SystemServer的后续运行将依赖于AMS,所以如果在AMS还未就绪的情况下就贸然返回,很可能会造成系统宕机。
将AMS注册到ServiceManager很简单,唯一要注意的是它不只注册了自己一个Server,而是一系列与进程管理相关的服务。如下所示:

public static void setSystemProcess() {
 try {
 ActivityManagerService m = mSelf; 
 ServiceManager.addService("activity", m,
true);//AMS的主业
 ServiceManager.addService("meminfo", new
MemBinder(m));//内存使用情况//其他服务省略
 }

管理当前系统中Activity状态——ActivityStack

以下内容是从ActivityStack.java中提取出来的。

  • 1.ActivityState
    描述了一个Activity所可能经历的所有状态。其定义如下:
    INITIALIZING, //正在初始化
    RESUMED, //恢复
    PAUSING, //正在暂停
    PAUSED, //已经暂停
    STOPPING, //正在停止
    STOPPED, //已经停止
    FINISHING, //正在完成
    DESTROYING, //正在销毁
    DESTROYED //已经销毁
    }
  • 2.ArrayList
    除了状态管理外,ActivityStack中还有一系列不同功能的ArrayList成员变量。它们的共同点在于列表元素都是ActivityRecord——这个类负责记录每个Activity的运行时信息。因而也可以看出,ActivityStack确实是AMS中管理Activity的“大仓库”。
    在这里插入图片描述
  • 3.记录特殊状态下的Activity
    除了上面的ArrayList用来描述各种状态下的Activity集合外,ActivityStack还通过以下多个变量来专门记录一些特殊状态下的Activity实例,具体如表8-2所示。
    在这里插入图片描述
    以上所述的3类变量构成了ActivityStack的主框架。如果用一句话来简单概述AMS的功能,就是:
    “AMS是通过ActivityStack(和其他数据结构)来记录、管理系统中的Activity(和其他组件)状态,并提供查询功能的一个系统服务。”这句话包含了以下几个重点。
  • 1.AMS的主要工作就是管理、记录、查询
    打个比方,AMS就像户籍登记处。所有新加入或者注销的家庭都需要到这里办理业务;而且它还提供对外的查询功能——这点类似于公安局开具的“户籍证明”,用于表明办证者当前的户口状态。
  • 2.AMS是系统进程的一部分(确切地说它运行于一个独立的线程中)
    从内核的角度来说,AMS其实也是普通进程中的一部分,只不过它提供的是全局性的系统服务。接着上面打的比方,户籍登记处和家庭一样,也是在一个“房子”(进程)里运行的。它有一套严格的办事规程(线程),来处理户主的各种请求(登记、注销、查询等)。值得一提的是,AMS的任务只是负责保管Activity(及其他组件)的状态信息,而像Activity中描述的UI界面如何在物理屏幕上显示等工作则是由WindowManagerService和SurfaceFlinger来完成的

startActivity流程

相信大家对startActivity(Intent)的功能不会陌生。它用于启动一个目标Activity——具体是哪个Activity则是AMS通过对系统中安装的所有程序包进行“Intent匹配”得到的,并不局限于调用者本身所在的package范围。换句话说,startActivity()最终很可能启动的是其他进程中的组件。当系统匹配到某个目标Activity后分为两种情况。

  • 如果通过Intent匹配到的目标对象,其所属程序包中已经有其他元 素在运行(意味着该程序进程已启动),那么AMS就会通知这个 进程来加载运行我们指定的目标Activity。
  • 如果当前Activity所属程序没有进程在运行,AMS就会先启动它的一个实例,然后让其运行目Activity。
    先大致讲解一下startActivity()所经历的函数调用流程,从调用方(Activity1)开始:Activity1→
    startActivity@ContextImpl.java→execStartActivity@Instrumentation.java→startActivity@ActivityManager Service.java。
    因而经过层层中转后,调用者发起的startActivity最终还是在AMS中实现的。接下来的问题就转化为:AMS内部对startActivity是如何处理的?
    看似简单的一个功能,实际上AMS要做的工作还是很多的——首先来辨别下AMS中5个“长相”类似的
    startActivity函数,以防后期混淆。统一列出如下:
startActivity@ActivityManagerService.java
startActivityAsUser@ ActivityManagerService.java
startActivityMayWait@ActivityStack.java
startActivityLocked@ActivityStack.java
startActivityUncheckedLocked@ActivityStack.java

这5个函数存在先后调用的关系。源代码如下:

/*frameworks/base/services/java/com/android/server/am/ActivityMa
nagerService.java*/
 public final int startActivity(IApplicationThread caller,
String callingPackage,
 Intent intent, String resolvedType, IBinder resultTo,
 String resultWho, int requestCode, int startFlags,
 String profileFile, ParcelFileDescriptor profileFd,
Bundle options) {
 return startActivityAsUser(caller, callingPackage,
intent, resolvedType,
 result To, resultWho, requestCode,
startFlags, 
 profileFile, profileFd,
options,UserHandle.getCallingUserId());
 }

可以看到,startActivityAsUser与startActivity只多了最后一个参数userId,它表示调用者的用户ID值,因而可以通过Binder机制的getCallingUid获得:

public final int startActivityAsUser(IApplicationThread
caller, String calling Package,
 Intent intent, String resolvedType, IBinder resultTo,
 String resultWho, int requestCode, int startFlags,
String profileFile,
 ParcelFileDescriptor profileFd, Bundle options, int
userId {
 enforceNotIsolatedCaller("startActivity");
 userId = handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId,
 false, true, "startActivity", null);
 return
mMainStack.startActivityMayWait(caller,-1,callingPackage,intent,
resolvedType,
 resultTo, resultWho, requestCode, startFlags,
profileFile, profileFd,
 null, null, options, userId);/*这个函数是ActivityStack提供的*/
 }

函数startActivityAsUser的一大重点就是做权限检查,包括:

  • enforceNotIsolatedCaller
    检查调用者是否属于被隔离的对象。
  • handleIncomingUser
    调用者是否有权力执行这一操作
    5个“startActivityXX”其实是5个执行步骤,而且一旦其中一步出现错误就会中止整个流程。接着往下分析startActivityMayWait这个函数。因为代码很长,我们直接把其中的核心工作提取出来,如图8-2所示。
    在这里插入图片描述
    根据图8-2中的描述,在startActivityMayWait中:
  • 要启动某个符合Intent要求的Activity,那么首先就应确定这个目标Activity:如果是显式的Intent,问题很好解决,因为Intent信息中已经带有目标Activity的相关信息;否则就调用resolveActivity()进行查找
  • 判断目标Activity所属进程是不是重量级(heavy-weight)的。如果当前系统中已经存在的重量级进程(mService.mHeavyWeightProcess)不是即将要启动的这个,那么就要给Intent重新赋值。
  • 调用startActivityLocked来进一步执行启动工作。
  • 如果outResult不为空,还需要将函数的结果写入这个变量中。

这个函数的名称表明它有可能会“wait”——具体就表现在对outResult的处理上。
在这里插入图片描述
AMS中startActivity的过程如图8-5所示。
在这里插入图片描述

完成同一任务的“集合”——Activity Task

Android系统中应用程序的一大特色,就是它们不仅可以“装载”众多系统组件,而且可以把这些组件跨进程地组成ActivityTask。这个特性使得每个应用都不是孤立的,从而能最大限度地实现资源复用。举个例子,一个短信应用程序至少会有“已收短信列表”、“阅读短信”和“编辑短信”3个子功能——在Android体系中它们分别对应3个Activity。

从程序包(Package)的组织角度来说,这3个元素只属于“短信”这个程序。但事实上,Activity的设计意图已经超越了单一的进程概念。换句话说,这几个Activity不仅在“短信”这一程序可以非常方便地互相调用(比如用户在“已收短信列表”中点击任何一条短信就可以进入“短信阅读”),其他需要使用“短信”功能的进程也能通过startActivity(Intent)来复用它们。比如我们在浏览电话本时,可以将某个人的联系方式通过短信发送出去。电话本程序并不需要专门实现短信的编辑和发送,只需填写这一请求(Intent),然后利用startActivity(Intent)告诉系统,余下的事情就会有相应的“志愿者”去帮它完成。

在这一过程中,系统先后启动了“联系人详情”和“短信编辑”两个不同进程中的Activity,来共同完成“通过短信发送联系人信息”的任务,这就是ActivityTask概念的直观体现。从数据结构的角度来讲,Task有先后之分,所以源码实现上采用了Stack栈的方式。在本例中,“联系人详情”这个Activity是首先启动并被压栈的,随后“栈顶”的位置被“短信编辑”所取代——直到短信发送完成后被销毁,此时就又会“显示”出原来的那个“联系人详情”的Activity了。

ActivityTask机制打破了应用程序的常规使用界限,从而增强了用户体验。同时,也给程序的管理和实现增加了一定难度。上面已经说过,Task运用的是“栈”管理方式,那么在AMS中具体是如何实现的呢?当前系统应该不仅有一个Task,而是众多Task的集合,这些Task间又有什么联系?用户是否可以控制和调整这些Task间的联系呢?

回答是肯定的。Android系统提供了一系列Flag标志来允许用户对Task进行实时调整,正确理解和使用这些flag无疑会让应用程序更贴近用户的使用习惯。
1.1 “后进先出”——Last In,First Out
传统意义上“栈”的思想就是“Last In, First Out”。通俗地讲,就是“后进先出”——先入栈的元素会被压在栈底,而后续元素不断往上堆栈。因而出栈时自然是最后的那个元素在先,然后才是下面的元素,直到栈底。

从上述“栈”的概念来衡量,ActivityTask并不能算是严格意义的Stack——它在默认情况下和栈是一致的,但比栈提供了更多的操作方式,因而可以理解为“栈的一种变异”。
1.2 管理Activity Task
前一小节所述的Activity Task已经能满足开发者的一般需求,但是在某些情况下我们还希望拥有更多的灵活性。比如在启动一个新的Activity时,我们可能不希望它和当前的Activity处于同一个Task中;或者我们希望当新的Activity运行时,系统可以先清空当前的Task等。Android系统提供了丰富的接口方法来满足程序员的类似需求
应用程序可以通过两种方法来影响Activity Task的默认行为。
方法1:在< activity>标签中指定属性

  • android:taskAffinity
    Affinity即“喜好,倾向”,它代表这个Activity所希望归属的Task。在默认情况下,同一个应用程序中的所有Activity拥有共同的Affinity,即<AndroidManifest.xml>中声明的Package Name。当然也可以主动在< application>中使用taskAffinity标签属性来指定整个应用程序的Affinity。
    一个Activity Task的Affinity属性取决于它的根Activity。

那么,taskAffinity在什么情况下产生效果?
(1)当启动Activity的Intent中带有FLAG_ACTIVITY_NEW_TASK标志时
在默认情况下,目标Activity将与startActivity的调用者处于同一Task中。但如果用户特别指定了FLAG_ACTIVITY_NEW_TASK,表明它希望为Activity重新开设一个Task。这时就有两种情况:假如当前已经有一个Task,它的affinity与新Activity是一样的,那么系统会直接用此Task来完成操作,而不是另外创建一个Task;否则系统需要重启一个Task。
(2)当Activity中的allowTaskReparenting属性设置为true时。在这种情况下,Activity具有“动态转移”的能力。举个前面的“短信”例子,在默认情况下该应用程序中的所有Activity具有相同的affinity。当另一个程序启动了“短信编辑”时,一开始这个Activity和启动它的Activity处于同样的Task中。但如果“短信编辑”Activity指定了allowTaskReparenting,且后期“短信”程序的Task转为前台,此时“短信编辑”这一Activity会被“挪”到与它更亲近的“短信”Task中。

  • android:launchMode

用于指定Activity被启动的方式。主要包括两方面的内容:即Activity是否为单实例以及Activity归属的Task。不论是何种方式,最终被启动的Activity通常情况下都要位于ActivityTask的栈顶(因为只有在栈顶的Activity才是可以直接与用户交互的)。一共有4种launchMode,如表8-3所示。
在这里插入图片描述
在这里插入图片描述
关于启动模式,看一下官网的介绍

android:launchMode
有关应如何启动 activity 的指令。共有五种模式可与 Intent 对象中的 activity 标记(FLAG_ACTIVITY_* 常量)协同工作,以确定在调用 activity 处理 intent 时应执行的操作。它们是:
“standard”
“singleTop”
“singleTask”
“singleInstance”
“singleInstancePerTask”
默认模式为“standard”。
如下表所示,这些模式可分为两大类:“standard”和“singleTop”activity 为一类,“singleTask”“singleInstance”和“singleInstancePerTask”activity 为另一类。启动模式为“standard”或“singleTop”的 activity 可以多次实例化。实例可归属任何任务,并且可位于 activity 任务中的任何位置。通常,它们会启动到名为 startActivity() 的任务中(除非 intent 对象包含 FLAG_ACTIVITY_NEW_TASK 指令,在此情况下会选择其他任务 - 请参阅 taskAffinity 属性)。
相比之下,“singleTask” “singleInstance” 和 “singleInstancePerTask” activity 的行为有所不同。“singleInstancePerTask”始终位于 activity 任务的根位置。此外,设备一次只能保留一个 “singleInstance” activity 实例,而 “singleInstancePerTask” activity 在 FLAG_ACTIVITY_MULTIPLE_TASK 或 FLAG_ACTIVITY_NEW_DOCUMENT 已设置的情况下,在不同的任务中可以多次实例化。启动模式为 “singleTask” 的 activity 结合了 “singleInstance” 和 “singleInstancePerTask” 的行为:activity 可以多次实例化,并且可以位于具有相同 taskAffinity 的任务中的任意位置。同时,设备只能保留一个用于在 activity 任务的根位置查找 “singleTask” activity 的任务。
“standard”和“singleTop”模式只有一个不同之处:每次“standard”activity 有一个新的 intent 时,系统都会创建类的新实例来响应该 intent。每个实例处理单个 Intent。同样地,您也可以创建新的“singleTop”activity 实例来处理新的 intent。不过,如果目标任务的 activity 堆栈顶部已有一个 activity 实例,则该实例会(通过调用 onNewIntent())接收新的 intent;此时不会创建新实例。在其他情况下(例如,如果“singleTop”activity 的某个现有实例虽在目标任务内,但未处于堆栈顶部,或者虽然位于堆栈顶部,但不在目标任务中),系统会创建新实例并将其送入堆栈。
同样地,如果您向上导航到当前堆栈上的某个 activity,则该行为由父 activity 的启动模式决定。如果父 activity 有启动模式 singleTop(或者 up intent 包含 FLAG_ACTIVITY_CLEAR_TOP),则系统会将该父项置于堆栈顶部,并保留其状态。导航 Intent 由父 Activity 的 onNewIntent() 方法接收。如果父 activity 有启动模式 standard(并且 up intent 不包含 FLAG_ACTIVITY_CLEAR_TOP),则系统会将当前 activity 及其父项同时送出堆栈,并创建新的父 activity 实例来接收导航 intent。
“singleInstance”模式也与“singleTask”和“singleInstancePerTask”有一个不同之处:具有“singleTask”或“singleInstancePerTask”启动模式的 activity 允许其他 activity(必须是“standard”和“singleTop”activity)成为其任务的一部分。另一方面,“singleInstance”activity 不允许任何其他 activity 成为其任务的一部分;它必须是任务中唯一的 activity。如果它启动另一个 activity,则系统会将该 activity 分配给其他任务,就如同 intent 中包含 FLAG_ACTIVITY_NEW_TASK 一样。
在这里插入图片描述
如上表所示,standard 是默认模式,适用于大多数类型的 activity。对众多类型的 activity 而言,SingleTop 也是常见且有用的启动模式。其他模式(singleTask、singleInstance 和 singleInstancePerTask)不适用于大多数应用,因为它们所形成的交互模式可能让用户感到陌生,并且与大多数其他应用差别较大。

关于启动模式,进行一些补充,来源于《Android开发艺术探索》

( 1) standard:标准模式,这也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下Activity 的生命周期,它的 onCreate、onStart、onResume都会被调用。这是一种典型的多实例实现,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个 Activity,那么这个 Activity就运行在启动它的那个Activity所在的栈中。比如 Activity A启动了Activity B(B是标准模式),那么B就会进入到A所在的栈中。不知道读者是否注意到,当我们用ApplicationContext去启动standard模式的Activity的时候会报错,错误如下:
在这里插入图片描述
相信这句话读者一定不陌生,这是因为 standard模式的Activity 默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如 ApplicationContext)并没有所谓的任务栈,所以这就有问题了。解决这个向题的方法是为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候待启动Activity实际上是以 singleTask模式启动的。
(2) singleTop:栈顶复用模式。在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。需要注意的造,这个Activity 的 onCreate、onStart不会被系统调用,因为它并没有发生改变。如果新Activity 的实例已存在但不是位于栈顶,那么新Activity仍然会重新重建。举个例子,假设目前栈内的情况为ABCD,其中 ABCD为四个 Activity,A位于栈底,D位于栈顶,这个时候假设要再次启动D,如果D的启动模式为singleTop,那么栈内的情况仍然为ABCD;如果D的启动模式为standard,那么由于D被重新创建,导致栈内的情况就变为ABCDD。
(3 ) singleTask:栈内复用模式。这是一种单实例模式,在这种模式下,只要 Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和 singleTop一样,系统也会回调其 onNewIntent。具体一点,当一个具有singleTask模式的Activity请求启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后把A放到栈中。如果存在A所需的任务栈,这时要看A是否在栈中有实例存在,如果有实例存在,那么系统就会把A调到栈顶并调用它的
onNewIntent方法,如果实例不存在,就创建A的实例并把A压入栈中。举几个例子:

  • 比如目前任务栈S1中的情况为ABC,这个时候Activity D以singleTask模式请求启动,其所需要的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,然后再创建D的实例并将其入栈到S2。
  • 另外一种情况,假设D所需的任务栈为S1,其他情况如上面例子1所示,那么由于S1已经存在,所以系统会直接创建D的实例并将其入栈到S1。
  • 如果D所需的任务栈为S1,并且当前任务栈S1的情况为ADBC,根据栈内复用的原则,此时D不会重新创建,系统会把D切换到栈顶并调用其 onNewIntent方法,同时由于singleTask默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈,于是最终S1中的情况为AD。
    ( 4)singleInstance:单实例模式。这是一种加强的singleTask模式,它除了具有singleTask模式的所有特性外,还加强了一点,那就是具有此种模式的Activity只能单独地位于一个任务栈中,换句话说,比如Activity A是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内复用的特性,后续的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。
  • android:clearTaskOnLaunch
    清除Task中所有除root activity的元素。可想而知这个属性只对root activity设置有效,task中其他activity设置此属性是无效的。
  • android:alwaysRetainTaskState
    如果用户在一定时间内不再访问Task,比如说30分钟,那么系统就有可能会清除task中的状态(只保留root activity)。设置此属性为“true”可以避免这种情况。
  • android:finishOnTaskLaunch
    当Task被再次启动时,activity是否需要销毁。这个属性比allowTaskReparenting优先级高。也就是说,这种情况下activity不
    会被重新指定task,而是直接销毁。

方法2:使用Intent标志
除了在标签中声明task属性外,我们也可以在启动一个Activity(startActivity)时通过Intent来动态指定所需的task属性值。Activity中静态标注的属性和后面startActivity所指定的Intent有冲突则以Intent为准。

  • FLAG_ACTIVITY_NEW_TASK
    这个和前面的singleTask启动模式的作用是一样的。
  • FLAG_ACTIVITY_SINGLE_TOP
    这个和前面的singleTop启动模式的作用是一样的。
  • FLAG_ACTIVITY_CLEAR_TOP
    和上面两个不同,launchMode中没有与此对应的模式。它所代表的含义是:如果要启动的Activity已经在当前task中运行,那么所有在它之上的Activity都将被销毁,并且Intent通过onNewIntent传给它(这时它会被resumed)。

另外还有几个Intent标志对我们分析AMS有帮助,一并列出如下。

  • FLAG_ACTIVITY_NO_HISTORY
    这个Activity将不会被保存在History Stack中。同样的效果也可以通过在AndroidManifest.xml中添加“android:noHistory”来实现。
  • FLAG_ACTIVITY_MULTIPLE_TASK
    这个标志需要和FLAG_ACTIVITY_NEW_TASK一同使用,否则没有效果。它将阻止系统恢复一个现有的task(比如我们要启动的Activity已经在这个Task中)。换句话说,系统总是启动一个新的task来容纳要启动的Activity。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    如果设置了这个标志,则Activity不会被放在系统“最近启动的Activity列表”中。
  • FLAG_ACTIVITY_BROUGHT_TO_FRONT
    在launchMode中使用了singleTask后,系统会自动加上这个标志。
  • FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    使用此标志,当Activity在新task中启动或者在已有task中启动,都会处于task的上端。
  • FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
    系统自动设置的,说明这个Activity是从历史记录中启动的(长按HOME键可以调出)。
  • FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    一般情况下,当从Launcher启动应用程序Activity时都带有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,这样Task中所有此Activity上面的Activity都将被finish。这个标志就是辅助完成这个功能的。
  • FLAG_ACTIVITY_NO_USER_ACTION
    Activity中的onUserLeaveHint回调用于指示用户将要离开,它会退出前台。某些情况下这并不是用户主动选择造成的。比如当系统有来电(Incoming Call)或者闹钟事件,由此弹出的Activity都不是用户主动去点击启动的,因而带上这个标志可以使前述的回调函数不得到执行。
  • FLAG_ACTIVITY_REORDER_TO_FRONT
    设置此标志后,如果将要启动的Activity已经在History Stack中运行,那么我们只是调整其中的顺序将其放到最前端。
  • FLAG_ACTIVITY_NO_ANIMATION
    此标志表示启动的Activity不需要应用动画效果。
  • FLAG_ACTIVITY_CLEAR_TASK
    此标志将清除与启动的Activity相关task中的其他元素,只能与FLAG_ACTIVITY_NEW_TASK一起用。
  • FLAG_ACTIVITY_TASK_ON_HOME
    新启动的Activity将被放在task中Launcher的上面(如果存在的话),这样它返回时就会是Launcher了。此标志只能与FLAG_ACTIVITY_NEW_TASK一起用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/400022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uni-app中使用vue3语法详解

全局创建 app.use(createPina()).mount 全局方法 通过app.config.globalProperties.xxx可以创建 这里我们写了一个字符串翻转的全局方法 main.js里面添加一个全局方法 不要忘了加$ 否则会报错 // #ifdef VUE3 //导入创建app import { createSSRApp } from vue //导入创建ap…

数据结构刷题(二十一):131分割回文串、78子集

1.分割回文串题目链接思路&#xff1a;回溯算法的组合方法&#xff08;分割问题类似组合问题&#xff09;。流程图&#xff1a;红色竖杠就是startIndex。 for循环是横向走&#xff0c;递归是纵向走。回溯三部曲&#xff1a;递归函数参数&#xff1a;字符串s和startIndex&#…

Vue(10-20)

1Vue赋值方式 Object.defineProperty <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conten…

如何从Outlook向Teams共享电子邮件

从 Outlook 或 web 上的 Outlook,你可以将电子邮件共享到Teams中的聊天和频道。 注意: 你必须具有“共享到Teams”的Outlook”加载项。它为Teams用户自动安装。此外,移动设备当前不支持该功能。外接程序当前不支持共享邮箱。如果你安装了Teams桌面客户端,并且使用的是Outl…

精、稳、敏、融,步入人民金融时代 | 易观银行业数字化转型年度趋势报告

易观分析&#xff1a;2022年&#xff0c;在深化金融供给侧结构性改革和高质量增长要求的指引下&#xff0c;赋能实体、公平普惠、审慎经营成为银行业转型发展的关键词。一方面面临内外部复杂的经济环境和不确定性风险&#xff0c;银行主打稳健策略&#xff0c;数字化转型仍在持…

软件测试用例篇(5)

测试是否运行代码去划分&#xff1f; 1)静态测试: 不运行代码&#xff0c;检查代码的风格&#xff0c;格式是否符合公司的标准规范&#xff0c;检查代码的逻辑结构是否满足需求要实现的功能 看代码&#xff0c;不运行代码&#xff0c;通过静态分析代码的语法&#xff0c;编写规…

谷歌Google Pixel6Pro/7pro手机刷入ROOT权限-开通Volte+破解5G网络高速刷机教程

谷歌pixel6和pixel7系列&#xff0c;出厂并不带volte功能&#xff0c;也不支持完美的5G效果&#xff0c;对于我们这种身处大陆&#xff0c;又想体验高清通话的小伙伴来说&#xff0c;并不友好&#xff0c;所以第三方破解实现就显得非常重要了。通过实际测试&#xff0c;两款机型…

Java 文件上传 MultipartFile RequestPart 方式上传 HttpClient

Java springboot/springCloud项目&#xff0c;后端接口是用RequestPart 注解MultipartFile类型和实体类型的参数&#xff0c;目的是同时提交文件和表单参数。前端调用方式&#xff1a;需要使用表单&#xff08;form-data&#xff09;方式进行提交&#xff0c;content-type设置为…

Hive排序,窗口函数

数据排序1&#xff0c;全局排序&#xff08;order by&#xff09;&#xff1a;类似于标准SQL&#xff0c;只使用一个Reducer执行全局数据排序&#xff1b;速度慢,应提前做好数据过滤 &#xff1b;支持使用case when或表达式&#xff1b;支持按位置编号排序desc升序&#xff0c;…

拉链表

每日的用户更新表获取的三种方式&#xff1a;一是监听mysql库数据的变化&#xff0c;比如用canal合并每日的变化&#xff0c;获取到最后的一个状态二是每天获得一份切片数据&#xff0c;可以通过去两天切片数据的不同来作为每日更新表&#xff0c;可以对所有字段先进性concat&a…

Pixhawk RPi CM4 Baseboard 树莓派CM4安装Ubuntu20.04 server 配置ros mavros mavsdk

文章目录硬件安装Ubuntu Server20.04下载rpiboot工具下载imager刷写系统配置USB配置WIFI开机安装桌面配置wifi配置串口安装ROS安装mavros安装MAVSDK-PythonInternet设置最后参考&#xff1a; https://docs.holybro.com/autopilot/pixhawk-baseboards/pixhawk-rpi-cm4-baseboard…

Linux下C/C++ 网络扫描(主机扫描技术)

主机扫描是网络扫描的基础&#xff0c;通过对目标网络中主机IP地址的扫描&#xff0c;从一堆主机中扫描出存活的主机&#xff0c;然后以他们为目标进行后续的攻击。一般会借助于ICMP、TCP、UDP等协议的工作机制&#xff0c;检查打开的进程&#xff0c;开放的端口号等等。 主机…

SpringBoot使用缓存

目录 简介 Spring 的缓存主要有如下几个注解 红色标注的注解为最常用的注解&#xff0c;必须熟练掌握 Cacheable/CachePut/CacheEvict 主要的参数 SpEL 提供的运算符 实现步骤 补充 注意 整合 EHCACHE 简介 Spring 框架已经具备了缓存机制&#xff0c;虽然我们可以使用 …

Locust框架从0到1入门

Locust介绍 Locust是使用Python语言编写实现的开源性能测试工具&#xff0c;可以用来测试Web应用程序、API、数据库等各种应用程序的性能&#xff0c;使用起来简洁、轻量、高效&#xff0c;并发机制基于gevent协程&#xff0c;可以实现单机模拟生成较高的并发压力。中文意为&a…

【目标检测】正负样本分配策略

YOLO v3 目标的中心点在哪个网格内由该网格负责预测该目标&#xff0c;max-iou matching策略 &#xff08;1&#xff09;计算GT的中心点在哪个网格&#xff1b; &#xff08;2&#xff09;计算该网格内的所有anchor与GT的IOU&#xff0c;选择IOU最大的anchor负责预测该GT&…

嵌入安防监控项目——BOA服务器的移植

目录 一、源码下载 1.1 boa简介&#xff1a; 1.2BOA再项目中的使用 二、解压源码&#xff1a; 三、进入源码目录下的src目录&#xff1a; 四、make 编译源码&#xff1a; 五、建立安装目录 六、返回boa的顶层目录 --- 修改配置文件 七、建立测试页面 八、查看/boa目…

43-Golang中的goroutine!!!

Golang中的goroutine进程和线程说明并发和并行并发并行Go协程和Go主线程案例小结goroutine的调度机制MPG模式基本介绍MPG模式运行的状态1MPG模式运行的状态2设置GOlang运行的CPU数不同 goroutine之间如何通讯使用全局变量加锁同步改进程序进程和线程说明 1.进程就是程序在操作…

robosuite添加无碰撞的模型

1 前言 最近在使用robosuite时,需要在仿真环境中可视化物体的目标位置,从而方便观察训练情况,可视化的物体有以下要求: 形状尺寸与操作的物体一样半透明只有visual,不与场景其他物体有碰撞可以在每次step后设置位置,且固定在设定的位置,不受重力影响 2 方法 找了半天,最终确…

javaWeb核心04-CookieSession

文章目录会话技术1&#xff0c;会话跟踪技术的概述2&#xff0c;Cookie2.1 Cookie的基本使用2.2 Cookie的原理分析2.3 Cookie的使用细节2.3.1 Cookie的存活时间2.3.2 Cookie存储中文3&#xff0c;Session3.1 Session的基本使用3.2 Session的原理分析3.3 Session的使用细节3.3.1…

2018年MathorCup数学建模D题公交移动支付问题的评估方案解题全过程文档及程序

2018年第八届MathorCup高校数学建模挑战赛 D题 公交移动支付问题的评估方案 原题再现&#xff1a; 随着智能手机的普及和移动支付技术的提高,越来越多的支付手段可以转移到手机端。现有的现金缴费和实体公交卡刷卡的付费方式存在缺点&#xff0c;如公交卡在使用过程中存在着充…