【Android AMS】startActivity流程分析

文章目录

  • AMS
  • ActivityStack
  • startActivity流程
    • startActivityMayWait
      • startActivityUncheckedLocked
    • startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition)
      • resumeTopActivityLocked
  • 参考

AMS是个用于管理Activity和其它组件运行状态的系统进程。

AMS

AMS在系统启动的时候,创建一个线程循环处理客户端的请求。AMS会向ServiceManager注册多种Binder Server:“activity”、“meminfo”、“cpuinfo”等。

AMS启动过程:

/*frameworks/base/services/java/com/android/server/SystemServer.java*/public static void main(String[] args) {...// This used to be its own separate thread, but now it is// just the loop we run on the main thread.ServerThread thr = new ServerThread();thr.initAndLoop();}
/*frameworks/base/services/java/com/android/server/SystemServer.java*/
public void run() {Slog.i(TAG, "Activity Manager");context = ActivityManagerService.main(factoryTest); //启动AMSActivityManagerService.setSystemProcess(); //向Service Manager注册}

AMS提供了一个static main函数,通过它可以轻松启动AMS,通过setSystemProcess把这个重要系统服务注册到ServiceManager。

//frameworks/base/services/java/com/android/server/am/ActivityManagerService.javapublic static final Context main(int factoryTest) {AThread thr = new AThread();//AMS线程thr.start();//启动//这个线程执行在system server上,通过thr.mService判断AMS启动是否成功。如果成功,返回system server,否则一直等待。如果出错,就无力回天,空处理。synchronized (thr) {while (thr.mService == null) {try {thr.wait();} catch (InterruptedException e) {}}}ActivityManagerService m = thr.mService;mSelf = m;ActivityThread at = ActivityThread.systemMain();mSystemThread = at;Context context = at.getSystemContext();context.setTheme(android.R.style.Theme_Holo);m.mContext = context;m.mFactoryTest = factoryTest;m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper);m.mBatteryStatsService.publish(context);m.mUsageStatsService.publish(context);m.mAppOpsService.publish(context);//唤醒synchronized (thr) {thr.mReady = true;thr.notifyAll();}m.startRunning(null, null, null, null);return context;}

这里一个wait & notify配对使用,让system server确保AMS启动成功,它自己再接着执行。这么做的原因无它,就是system server需要依赖于AMS。

将AMS注册到ServiceManager之后,它还注册了一系列和进程管理相关的服务:

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

要了解AMS提供的所有功能,可以查看IActivityManager.java文件。
可以把这些接口进行分类:

  • 组件状态管理:例如startActivity、startService
  • 组件状态查询:例如getServices
  • Task相关:例如removeSubTask
  • 其它:查询运行时信息,例如getMemoryInfo

ActivityStack

/*frameworks/base/services/java/com/android/server/am/ActivityManagerService.java*/
public static final Context main(int factoryTest) {/*main()函数是启动AMS的入口*/ActivityManagerService m = thr.mService;…m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);/*生成ActivityStack对象*/}

ActivityStack是管理当前系统所有activity状态的一个数据结构。
它里面有个enum 叫做ActivityState :

enum ActivityState {INITIALIZING, //正在初始化RESUMED,     //恢复PAUSING,     //正在暂停PAUSED,      //已经暂停STOPPING,    //正在停止STOPPED,     //已经停止FINISHING,   //正在完成DESTROYING,  //正在销毁DESTROYED    //已经销毁}

ActivityStack除了管理状态,还有一系列不同功能的ArrayList成员变量,它们都是ActivityRecord,用来记录每个activity的runtime信息:
在这里插入图片描述

startActivity流程

startActivity()用来启动一个Activity,它有可能启动当前进程的Activity,也有可能启动其它进程的Activity。当通过Intent匹配到目标对象,如果目标对象的进程已经启动,那么AMS就会通知这个进程加载并运行这个activity。如果进程没有启动,AMS就会先启动进程,再让进程运行目标activity。

/*frameworks/base/services/java/com/android/server/am/ActivityManagerService.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的重点是做权限检查。

startActivityMayWait

startActivityMayWait的工作:
在这里插入图片描述
这个过程中,可能会“wait”,具体如流程图:
在这里插入图片描述
接着调用的是startActivityLocked,它有两个重载函数:

final int startActivityLocked(IApplicationThread caller,Intent intent, String resolvedType,Uri[] grantedUriPermissions,int grantedMode, ActivityInfo aInfo, IBinder resultTo,String resultWho, int requestCode,int callingPid, int callingUid, boolean onlyIfNeeded,boolean componentSpecified, ActivityRecord[] outActivity) {int err = START_SUCCESS;ProcessRecord callerApp = null;if (caller != null) {callerApp = mService.getRecordForAppLocked(caller);if (callerApp != null) {callingPid = callerApp.pid;callingUid = callerApp.info.uid;} else {Slog.w(TAG, "Unable to find app for caller " + caller+ " (pid=" + callingPid + ") when starting: "+ intent.toString());err = START_PERMISSION_DENIED;}}if (err == START_SUCCESS) {Slog.i(TAG, "START {" + intent.toShortString(true, true, true, false)+ "} from pid " + (callerApp != null ? callerApp.pid : callingPid));}ActivityRecord sourceRecord = null;ActivityRecord resultRecord = null;if (resultTo != null) {int index = indexOfTokenLocked(resultTo);if (DEBUG_RESULTS) Slog.v(TAG, "Will send result to " + resultTo + " (index " + index + ")");if (index >= 0) {sourceRecord = mHistory.get(index);if (requestCode >= 0 && !sourceRecord.finishing) {resultRecord = sourceRecord;}}}int launchFlags = intent.getFlags();if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0&& sourceRecord != null) {// Transfer the result target from the source activity to the new// one being started, including any failures.if (requestCode >= 0) {return START_FORWARD_AND_REQUEST_CONFLICT;}resultRecord = sourceRecord.resultTo;resultWho = sourceRecord.resultWho;requestCode = sourceRecord.requestCode;sourceRecord.resultTo = null;if (resultRecord != null) {resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);}}if (err == START_SUCCESS && intent.getComponent() == null) {// We couldn't find a class that can handle the given Intent.// That's the end of that!err = START_INTENT_NOT_RESOLVED;}if (err == START_SUCCESS && aInfo == null) {// We couldn't find the specific class specified in the Intent.// Also the end of the line.err = START_CLASS_NOT_FOUND;}if (err != START_SUCCESS) {if (resultRecord != null) {sendActivityResultLocked(-1,resultRecord, resultWho, requestCode,Activity.RESULT_CANCELED, null);}mDismissKeyguardOnNextActivity = false;return err;}final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,callingUid, aInfo.applicationInfo.uid, aInfo.exported);if (perm != PackageManager.PERMISSION_GRANTED) {if (resultRecord != null) {sendActivityResultLocked(-1,resultRecord, resultWho, requestCode,Activity.RESULT_CANCELED, null);}mDismissKeyguardOnNextActivity = false;String msg;if (!aInfo.exported) {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " not exported from uid " + aInfo.applicationInfo.uid;} else {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires " + aInfo.permission;}Slog.w(TAG, msg);throw new SecurityException(msg);}if (mMainStack) {if (mService.mController != null) {boolean abort = false;try {// The Intent we give to the watcher has the extra data// stripped off, since it can contain private information.Intent watchIntent = intent.cloneFilter();abort = !mService.mController.activityStarting(watchIntent,aInfo.applicationInfo.packageName);} catch (RemoteException e) {mService.mController = null;}if (abort) {if (resultRecord != null) {sendActivityResultLocked(-1,resultRecord, resultWho, requestCode,Activity.RESULT_CANCELED, null);}// We pretend to the caller that it was really started, but// they will just get a cancel result.mDismissKeyguardOnNextActivity = false;return START_SUCCESS;}}}ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,intent, resolvedType, aInfo, mService.mConfiguration,resultRecord, resultWho, requestCode, componentSpecified);if (outActivity != null) {outActivity[0] = r;}if (mMainStack) {if (mResumedActivity == null|| mResumedActivity.info.applicationInfo.uid != callingUid) {if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {PendingActivityLaunch pal = new PendingActivityLaunch();pal.r = r;pal.sourceRecord = sourceRecord;pal.grantedUriPermissions = grantedUriPermissions;pal.grantedMode = grantedMode;pal.onlyIfNeeded = onlyIfNeeded;mService.mPendingActivityLaunches.add(pal);mDismissKeyguardOnNextActivity = false;return START_SWITCHES_CANCELED;}}if (mService.mDidAppSwitch) {// This is the second allowed switch since we stopped switches,// so now just generally allow switches.  Use case: user presses// home (switches disabled, switch to home, mDidAppSwitch now true);// user taps a home icon (coming from home so allowed, we hit here// and now allow anyone to switch again).mService.mAppSwitchesAllowedTime = 0;} else {mService.mDidAppSwitch = true;}mService.doPendingActivityLaunchesLocked(false);}err = startActivityUncheckedLocked(r, sourceRecord,grantedUriPermissions, grantedMode, onlyIfNeeded, true);if (mDismissKeyguardOnNextActivity && mPausingActivity == null) {// Someone asked to have the keyguard dismissed on the next// activity start, but we are not actually doing an activity// switch...  just dismiss the keyguard now, because we// probably want to see whatever is behind it.mDismissKeyguardOnNextActivity = false;mService.mWindowManager.dismissKeyguard();}return err;}

这里确保调用者本身的进程是存在的,否则返回START_PERMISSION_DENIED。这种情况出现在调用者被系统杀死,crash等。

FLAG_ACTIVITY_FORWARD_RESULT这个FLAG有跨越传递的作用,比如Activity1正常启动了Activity2,而当Activity2启动Activity3时使用了这个标志,那么当Activity3调用setResult时,result并不会像一般情况中那样传递给Activity2,而是传递给最初的Activity1。

startActivityUncheckedLocked

这个方法先拿到Intent中的FLAG,然后处理FLAG_ACTIVITY_NO_USER_ACTION,这个FLAG表示来电、闹钟等非用户主动触发的Activity事件。
这里处理了很多FLAG,例如LAUNCH_SINGLE_INSTANCE、LAUNCH_SINGLE_TASK这些,这里暂时不展开。

startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition)

    private final void startActivityLocked(ActivityRecord r, boolean newTask,boolean doResume, boolean keepCurTransition) {...if (doResume) {resumeTopActivityLocked(null);}}

这里是启动activity的最后一站了,是AMS启动activity的关键。如果activity不是在新task中启动,那么程序要找出目标activity位于那个已有的task中。找到之后,如果它当前对用户不可见,就将它加入mHistory中,并在WMS中注册,但是不启动它。

接着将这个activity放在stack的最顶层:

mHistory.add(addPos, r);
r.putInHistory();
r.frontOfTask = newTask;

接下来,如果不是AMS的第一个activity,即mHistory > 0,则执行切换动画。
一个activity的UI能否显示,有个关键是WMS中必须有档案可查,就是appToken,它在startActivity中添加的:

mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen);

activity有affinity特性的,它们更亲近affinity相符的task,关键地方在于FLAG是否有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。

Android源码中有很多函数都有locked标志,提醒开发者必须保证它们的线程安全。

到这里startActivity分析完毕了,但是activity的启动流程还没完。

resumeTopActivityLocked

AMS会继续调用resumeTopActivityLocked来恢复最上层的Activity,并pause之前的Activity,并且在Activity切换的过程中还要首先展示切换动画,然后两个新旧Activity还会向WMS分别申请和释放Surface,最终将它们显示/不显示在屏幕上。

  int i = mHistory.size()-1;//所有Activity的数量while (i >= 0) {ActivityRecord r = mHistory.get(i);if (!r.finishing && r != notTop && okToShow(r)) {return r;}i--;}return null;

这里处理ActivityRecord。

resumeTopActivityLocked执行后面的时候,可以正式启动目标activity了,但是有两种情况,一种是目标activity所属的进程已经在运行,一种是没有运行。
前者我们可以通知WMS这个Activity已经具备显示条件了:

mService.mWindowManager.setAppVisibility(next.appToken, true);

更新一系列全局变量,如果有等待启动的对象,就会通过:

next.app.thread.scheduleResumeActivity(next.appToken, mService.isNextTransitionForward());

告知目标线程要resume指定的activity。

后者的情况复杂些,会通过startSpecificActivityLocked启动进程,接着调用一系列和startActivity长得差不多的函数,最终调用zygote来fork一个新的进程:

Process.ProcessStartResult startResult =Process.start("<strong>android.app.ActivityThread</strong>", app.processName, uid, uid, gids,debugFlags,app.info.targetSdkVersion, null, null);

可以看出,一个进程启动的时候,实际上会加载ActivityThread。

那么新启动的进程什么时候启动activity呢?
进程启动后要通知AMS,AMS会预留一段时间等待回调。这个在不同设备上有所差异,有的10s,有的300s。如果进程指定时间内没有完成attachApplication回调,那么AMS就认为异常了。如果进程完成了attachApplication回调,AMS就会判断当前是不是有Activity在等待这个进程启动。是的话,调用realStartActivityLocked继续之前的任务。
接着就是activity的生命周期了,onCreate,onStart,onResume等,并且在WMS和SurfaceFlinger的配合下,目标Activity描述的UI界面会显示在屏幕。

startActivity流程才算真正完成。

在这里插入图片描述

参考

《深入理解Android内核设计思想》

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

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

相关文章

贴片滚珠振动开关 / 振动传感器的用法

就是这种小东西&#xff1a; 上面的截图来自&#xff1a;https://item.szlcsc.com/3600130.html 以前写过一篇介绍这种东西内部的结构原理&#xff1a;贴片微型滚珠振动开关的结构原理。就是有个小滚珠会接通开关两边的电极&#xff0c;振动时滚珠会在内部蹦跳&#xff0c;开关…

手把手教你设计报表,轻松做出一份美观又实用的报表

你是不是在看着自己的呆板、没有特色的报表而深感苦恼&#xff0c;但同事却可以使用同样的数据制作并且展现出多样化的丰富美观的报表。要知道&#xff0c;除了要达成数据的准确度&#xff0c;基本的数据分析维度需求之外&#xff0c;报表的美观程度也有众多的隐藏“福利”与好…

自定义类似微信效果Preference

1. 为自定义Preference 添加背景&#xff1a;custom_preference_background.xml <?xml version"1.0" encoding"utf-8"?> <selector xmlns:android"http://schemas.android.com/apk/res/android"><item><shape android:s…

LRTimelapse for Mac:专业延时摄影视频制作利器

LRTimelapse for Mac是一款专为Mac用户设计的延时摄影视频制作软件&#xff0c;它以其出色的性能和丰富的功能&#xff0c;成为摄影爱好者和专业摄影师的得力助手。 LRTimelapse for Mac v6.5.4中文激活版下载 这款软件提供了直观易用的界面&#xff0c;用户可以轻松上手&#…

【从浅学到熟知Linux】进程控制下篇=>进程程序替换与简易Shell实现(含替换原理、execve、execvp等接口详解)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 进程程序替换什么是程序替换及其原理替换函数execlexeclpexecleexecvexecvpexecvpeexecve 替换函数总结实现…

宝剑锋从磨砺出,透视雀巢咖啡品牌焕新与产品升级的想象力

自1989年进入中国市场以来&#xff0c;陪伴着国内咖啡行业由启蒙期走向兴盛期的雀巢咖啡&#xff0c;始终坚持以消费者高品质、个性化需求为本位&#xff0c;在保有独特性的基础上持续创新&#xff0c;实现了从无到有的攻克与突破。 近日&#xff0c;深耕中国三十六载的雀巢咖…

康耐视visionpro-CogCreateLinePerpendicularTool操作操作工具详细说明

CogCreateLinePerpendicularTool]功能说明&#xff1a; 创建点到线的垂线 CogCreateLinePerpendicularTool操作说明&#xff1a; ①.打开工具栏&#xff0c;双击或点击扇标拖拽添加CogCreateLinePerpendicularTool ②.添加输入源&#xff1a;右键“链接到”或以连线拖 拽的方式…

Java代码基础算法练习-圆的面积-2024.04.17

任务描述&#xff1a; 已知半径r&#xff0c;求一个圆的面积(保留两位小数)&#xff0c;其中 0 < r < 5&#xff0c;PI 3.14&#xff0c;圆面积公式: PI * r * r 任务要求&#xff1a; 代码示例&#xff1a; package April_2024;import java.util.Scanner;// 已知半径…

【前端】1. HTML【万字长文】

HTML 基础 HTML 结构 认识 HTML 标签 HTML 代码是由 “标签” 构成的. 形如: <body>hello</body>标签名 (body) 放到 < > 中大部分标签成对出现. <body> 为开始标签, </body> 为结束标签.少数标签只有开始标签, 称为 “单标签”.开始标签和…

Linux-时间同步服务器

1. (问答题) 一.配置server主机要求如下&#xff1a; 1.server主机的主机名称为 ntp_server.example.com 编写脚本文件 #!/bin/bash hostnamectl hostname ntp_server.example.com cd /etc/NetworkManager/system-connections/ rm -fr * cat > eth0.nmconnection <&…

【编译原理】02词法分析(1)

接上篇 &#xff1a;【编译原理】01引论 词法分析是编译过程中将字符流转换成为符号流的一个工作阶段&#xff0c;是编译的第一步工作&#xff0c;其后续工作是语法分析。 词法分析的输入是源代码&#xff1b; 词法分析的输出是符号流&#xff1b; 词法分析需要识别词法错误&am…

STM32 软件I2C方式读取MT6701磁编码器获取角度例程

STM32 软件I2C方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》&#x1f33f;《Arduino通过I2C驱动MT6701磁编码器并读取角度数据》&#x1f530;MT6701芯片和AS5600从软件读取对比&#xff0c;只是读取的寄存器和…

代码随想录算法训练营第56天| 583. 两个字符串的删除操作|72. 编辑距离|编辑距离总结篇

代码随想录算法训练营第56天| 583. 两个字符串的删除操作|72. 编辑距离|编辑距离总结篇 详细布置 583. 两个字符串的删除操作 本题和动态规划&#xff1a;115.不同的子序列 相比&#xff0c;其实就是两个字符串都可以删除了&#xff0c;情况虽说复杂一些&#xff0c;但整体思…

【Redis 神秘大陆】009 案例实践进阶

九、案例实践&进阶方案 9.1 本地缓存组件选型 使用缓存组件时需要重点关注集群方式、集群、缓存命中率。 需要关注集群组建方式、缓存统计&#xff1b;还需要考虑缓存开发语言对缓存的影响&#xff0c;如对于JAVA开发的缓存需要考虑GC的影响&#xff1b;最后还要特别关注…

SQL优化——核心概念

文章目录 1、基数(数据分布)2、选择性3、直方图&#xff08;HISTOGRAM&#xff09;4、回表&#xff08;TABLE ACCESS BY INDEX ROWID&#xff09;5、集群因子&#xff08;CLUSTERING FACTOR&#xff09;6、表与表之间关系 1、基数(数据分布) 某个列唯一键&#xff08;Distinct…

springboot整合dubbo实现RPC服务远程调用

一、dubbo简介 1.什么是dubbo Apache Dubbo是一款微服务开发框架&#xff0c;他提供了RPC通信与微服务治理两大关键能力。有着远程发现与通信的能力&#xff0c;可以实现服务注册、负载均衡、流量调度等服务治理诉求。 2.dubbo基本工作原理 Contaniner:容器Provider&#xf…

[AI]-(第0期):认知深度学习

深度学习是一种人工智能&#xff08;AI&#xff09;方法&#xff0c;用于教计算机以受人脑启发的方式处理数据。 深度学习模型可以识别图片、文本、声音和其他数据中的复杂模式&#xff0c;从而生成准确的见解和预测。 您可以使用深度学习方法自动执行通常需要人工智能完成的…

【C++】set 类 和 map 类

1. 关联式容器 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其里面存储的是<key, value>结构的 键值对&#xff0c;在数据检索时比序列式容器效率更高 2. 键值对 用来表示具有一一对应关系的一种结构&#xff0c;该结构中一般只包含…

Pytorch(GPU版本)简介、安装与测试运行

目录 Pytorch简介Pytorch安装查看CUDA版本Pytorch命令安装Pytorch测试运行Pytorch简介 PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。PyTorch既可以看作加入了GPU支持的numpy,同时也可以看成一个拥有自动求导功能的强大的深度神经网络。 2…

Linux进阶篇:Centos7安装与配置mysql(rpm安装方式)

Linux服务搭建篇&#xff1a;Centos7安装与配置mysql&#xff08;rpm安装方式&#xff09; MySQL是一个开源的关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff0c;现在属于Oracle公司。MySQL是最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方面&am…