Activity的启动流程(AndroidU)

devtools/2024/10/18 3:21:59/

启动有冷热温三种。

冷启动:从无到有走完整个启动流程。一般是应用第一次启动。

热启动:有应用进程无相关数据,需要重新加载,比如冻结。一般是从多任务进入。

温启动:有进程有数据。一般是退出后再次进入。

启动流程如下:

1.启动准备

应用启动是从应用Activity的this.startActivity开始,调用父类Acitivity.startActivity.

xref: /frameworks/base/core/java/android/app/Activity.java

1.1Acitivity#startActivity.

这两个方法都是为了启动新的 Activity,但它们的差别在于第二个方法允许传入额外的启动选项。第一个方法是第二个方法的简化版本,默认将 options 传为 null,以便于兼容性处理。

@Override
public void startActivity(Intent intent) {this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {getAutofillClientController().onStartActivity(intent, mIntent);if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}
}

Activity#startActvityForResult

这两个方法用于启动新的 Activity,并在之后等待它返回结果。第一个方法是第二个方法的简化版本,默认将 options 传为 null。第二个方法通过更复杂的逻辑来处理启动,包括处理顶层 Activity 和子 Activity 的不同情况,并且可以传递更多的启动选项。

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {startActivityForResult(intent, requestCode, null);
}public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {mStartedActivity = true;}cancelInputsAndStartExitTransition(options);} else {if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {mParent.startActivityFromChild(this, intent, requestCode);}}
}
参数:
  • intent:要启动的新 Activity 的意图(Intent 对象)。
  • requestCode:用于标识启动的 Activity 的请求码。这里是-1,表示不期待返回结果,不执行onActivityResult方法。(应用启动的是第一个界面,应该没有接受者关注启动结果)
详细逻辑:
  1. 检查 mParent 是否为 null
    • 如果 mParent 为 null,表示这是一个顶层 Activity:

      • 将 options 参数进行处理,可能是为了适配或者转换选项。
      • 通过 Instrumentation 类的 execStartActivity 方法来启动新的 Activity,传递必要的参数,包括当前上下文、应用线程、启动 Token、Intent、请求码和选项。
      • 如果启动结果 ar 不为 null,则通过 mMainThread 发送启动结果。正常启动的ar值是为null的,不用空时一般用作测试或启动失败。
      • 如果请求码 requestCode 大于等于 0,设置 mStartedActivity 为 true,这通常表示启动的 Activity 会返回结果,期间将当前的 Activity 保持隐藏状态。
      • 调用 cancelInputsAndStartExitTransition 方法来取消输入并启动退出过渡动画。(本文分析的是应用启动,调用startActivityForResult重载时,传入为null,没有退出动画。可能从launcher启动时,上个界面即launcher不需要退出动画。应用内部启动调用这个方法的参数可能不为null,需要上个界面的动画)
    • 如果 mParent 不为 null,表示这是一个子 Activity:

      • 根据 options 是否为 null,调用 mParent 的相应方法 startActivityFromChild 来启动子 Activity。传递必要的参数,包括当前的 Activity、Intent、请求码以及选项。
    • 顶层Activity不依赖其他activty,可作为程序主入口。子Activity依赖父Activity,通过父activity管理和启动。例如:在一个电商应用中,一个主 Activity (如 CategoryActivity) 可能启动一个子 Activity (如 ProductDetailActivity) 来显示一个特定类型商品的详细信息。

1.2Instrumentation#execStartActivity

Instrumentation主要负责调用跟踪Activity和Application的生命周期。execStartActivty有3个重载方法,根据参数类型及个数不同,分别是从Actvity启动Activity、从Fragment启动Activity、从特定用户上下文启动一个Activity。上面方法中传入的this为Activity,此处分析从Activity启动Activity流程。

public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {

参数解释

  • Context who: 当前启动活动的上下文。
  • IBinder contextThread: 上下文的主线程。
  • IBinder token: 用于标识谁启动了活动,可以为 null
  • Activity target: 执行启动操作的活动组件。
  • Intent intent: 用于启动活动的 Intent 对象。
  • int requestCode: 请求结果的标识符;如果小于零表示调用者不期待结果。
  • Bundle options: 传递给新活动的选项

执行逻辑

声明主线程和处理引用者
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
  • 获取主线程。
  • 检查并获取 referrer,如果 target 不为 null,则调用 target.onProvideReferrer()
  • 如果 referrer 不为 null,将其放入 Intent 的额外数据中。
检查活动监视器
if (mActivityMonitors != null) {synchronized (mSync) {final int N = mActivityMonitors.size();for (int i=0; i<N; i++) {final ActivityMonitor am = mActivityMonitors.get(i);ActivityResult result = null;if (am.ignoreMatchingSpecificIntents()) {if (options == null) {options = ActivityOptions.makeBasic().toBundle();}result = am.onStartActivity(who, intent, options);}if (result != null) {am.mHits++;return result;} else if (am.match(who, null, intent)) {am.mHits++;if (am.isBlocking()) {return requestCode >= 0 ? am.getResult() : null;}break;}}}
}

如果有活动监视器,则遍历这些监视器:

  • 检查是否忽略特定 Intent 的匹配。
    • 如果是且 options 为 null,则创建基本的 ActivityOptions
    • 调用监视器的 onStartActivity 方法,并检查是否返回了 ActivityResult
    • 如果返回了 ActivityResult,增加监视器命中计数并返回结果。
    • 否则,检查监视器是否匹配当前的 Context 和 Intent
      • 如果匹配且监视器是阻塞的,返回监视器的结果(如果 requestCode 大于等于 0);否则退出循环。
启动活动
try {intent.migrateExtraStreamToClipData(who);intent.prepareToLeaveProcess(who);int result = ActivityTaskManager.getService().startActivity(whoThread,who.getOpPackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);notifyStartActivityResult(result, options);checkStartActivityResult(result, intent);
} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);
}
  • 调整 intent 中的 ClipData
  • 为即将离开进程做准备。
  • 调用 ActivityTaskManager 服务的 startActivity 方法启动活动。
  • 通知启动结果,并进行检查。
  • 捕获 RemoteException 并抛出运行时异常。

总结

这个方法提供了从活动(Activity)启动另外一个活动的功能,它执行以下主要任务:

  1. 检查并处理可能存在的活动引用(referrer)。
  2. 检查并处理可能存在的活动监视器。
  3. 调整 Intent 对象,并准备启动活动。
  4. 调用底层的 ActivityTaskManager 服务启动活动。

这个方法在处理启动活动时结合了监视器匹配和阻塞机制,从而提供了安全且灵活的启动活动方式。


http://www.ppmy.cn/devtools/99521.html

相关文章

Linux之信号(下)

目录 sigset_t类型 信号集操作函数 sigprocmask 函数 sigpending 函数 代码实现 信号捕捉 sigaction volatile关键字 上节课我们主要学习了进程的产生前与进程产生中的相关内容&#xff0c;学习了进程的产生方式有哪些&#xff0c;学习了进程收到信号之后&#xff0…

多线程(5)——锁策略、CAS、JUC常见类

1. 常见锁策略 1.1 乐观锁 & 悲观锁 乐观锁 & 悲观锁 也不是指具体某个锁&#xff0c;而是 “锁的一种特点”&#xff0c;描述了 “一类锁” 乐观锁&#xff1a;加锁的时候&#xff0c;假设出现锁冲突的概率不大 > 接下来围绕加锁要做的工作就会更少悲观锁&#…

代码随想录算法训练营第六天|454.四数相加II;383. 赎金信;15. 三数之和;18. 四数之和

今日任务 ● 454.四数相加II ● 383. 赎金信 ● 15. 三数之和 ● 18. 四数之和 详细布置 454.四数相加II 建议&#xff1a;本题是 使用map 巧妙解决的问题&#xff0c;好好体会一下 哈希法 如何提高程序执行效率&#xff0c;降低时间复杂度&#xff0c;当然使用哈希法 会提…

【玩转python】入门篇day19-继承、多态以及单例模式

在Python中&#xff0c;继承和多态是面向对象编程的两个核心概念&#xff0c;它们允许我们创建基于已存在类的更复杂或更具体的类。下面我将详细讲解这两个概念&#xff0c;并提供相应的代码示例。 注意&#xff1a;python中是支持多继承的&#xff0c;我们分别来讲解 单继承 …

589. N 叉树的前序遍历(递归法)

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示&#xff0c;每…

【已上线】C++ mysql连接池

目录 1 为什么需要链接池2 实现原理3 代码4 编译5 参考 1 为什么需要链接池 可以复用已经建立好的链接&#xff0c;节约数据库建立链接的时间。原理上&#xff0c;和线程池类似。我们项目中的一个实际需求&#xff0c;同时可能有多个线程同时访问数据库。这样每个线程都需要和…

js跳出循环方法

跳出一层循环&#xff0c;用return&#xff0c;break。continue&#xff0c;结束当前迭代 注意 foreach forEach() 和 map() 用不了 break/continue 原因 forEach 接受一个 回调函数(callback) 作为必要的参数 &#xff1b; 而 回调函数 又会接受以下三个参数&#xff1a;curre…

C++ 模板

模版收尾 模版的声明和定义不能分离&#xff0c;否则会报错. 写下面三个文件&#xff1a; Stack.h#pragma once #include<iostream> using namespace std; template <class T> T Add(const T& left, const T& right);Stack.cpp#include"Stack.h&qu…