篇章目标要点

AMS(全称ActivityManagerService)作为四大组件的管理者,在Android系统工作流程当中起着非常重要的角色,四大组件当中Activity使用则又最为广泛,因为了解Activity的启动流程有助于加深自身对于Android系统工作原理的认知。本文是结合源码的理解和网上已经梳理的一些文章梳理了下自己对Activity启动过程的认识

源码路径

AMS主要代码位于两个路径下(以Android9.0为目标分析版本)

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/am/http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/

主要类说明

1. ActivityRecord

Activity信息的实体类,封装了进程名称,应用包名,所在的任务栈

2. TaskRecord

基于压栈和出栈操作顺序管理Activity启动的先后顺序,一个Activity退出时,接下来要显示哪一个Activity是通过这个栈信息获取

3. ActivityStack

管理TaskRecord栈信息

4. ActivityStackSupervisor

ActivityStack的管理者,管理者当前前台显示的Activity栈mFocusedStack, 上一次在前台显示的Activity的栈mLastFocusedStack, Launcher相关的Activity栈为mHomeStack。

5. Instrumentation

用来监控应用程序和系统的交互

6. ActivityManagerService

负责Android系统四大组件的启动,切换,调度和应用进程的管理工作。

7. ActivityThread

负责Activity主线程管理

Activity启动流程说明

Activity启动过程设计的类数量及代码量均非常大,在结合一些其他分享者的思路梳理和源码方面进行了自己的理解和梳理,目的主要是用于个人理解过程的梳理。

Activity启动过程主要代码

1.Instrumentation中execStartActivity (…)

方法中获取AMS代理对象,并执行拉起Activity,然后检查启动结果。

 public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,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);}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()) {result = am.onStartActivity(intent);}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;}}}}
//获取AMS代理对象并执行拉起Activitytry {intent.migrateExtraStreamToClipData();intent.prepareToLeaveProcess(who);int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);//检查启动过程,可能抛出异常checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;}

2.AMS当中startActivity代码

ActivityManagerService实现了IActivityManager.Stub类型可用于跨进程通信,中间有多个方法传递了startActivity任务,当中包括了会检查调用者权限。

/*** 执行会回到调用此处方法*/@Overridepublic final int startActivity(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());}

3.ActivityStarter当中代码

当中startActivityMayWait(…)方法,组装目标Intent所需的信息

 private int startActivityMayWait(IApplicationThread caller, int callingUid,String callingPackage, Intent intent, String resolvedType,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, WaitResult outResult,Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,int userId, TaskRecord inTask, String reason,boolean allowPendingRemoteAnimationRegistryLookup) {// Refuse possible leaked file descriptorsif (intent != null && intent.hasFileDescriptors()) {throw new IllegalArgumentException("File descriptors passed in Intent");}mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();. . .// Collect information about the target of the Intent.//组装目标Intent所需的信息ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);synchronized (mService) {final ActivityStack stack = mSupervisor.mFocusedStack;stack.mConfigWillChange = globalConfig != null&& mService.getGlobalConfiguration().diff(globalConfig) != 0;if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,"Starting activity when config will change = " + stack.mConfigWillChange);final long origId = Binder.clearCallingIdentity();if (aInfo != null &&(aInfo.applicationInfo.privateFlags& ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&mService.mHasHeavyWeightFeature) {// This may be a heavy-weight process!  Check to see if we already// have another, different heavy-weight process running.//以防应用有多进程的情况,检查进程名的一致性if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {final ProcessRecord heavy = mService.mHeavyWeightProcess;if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid|| !heavy.processName.equals(aInfo.processName))) {int appCallingUid = callingUid;if (caller != null) {ProcessRecord callerApp = mService.getRecordForAppLocked(caller);if (callerApp != null) {appCallingUid = callerApp.info.uid;} else {Slog.w(TAG, "Unable to find app for caller " + caller+ " (pid=" + callingPid + ") when starting: "+ intent.toString());SafeActivityOptions.abort(options);return ActivityManager.START_PERMISSION_DENIED;}}IIntentSender target = mService.getIntentSenderLocked(ActivityManager.INTENT_SENDER_ACTIVITY, "android",appCallingUid, userId, null, null, 0, new Intent[] { intent },new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT| PendingIntent.FLAG_ONE_SHOT, null);Intent newIntent = new Intent();if (requestCode >= 0) {// Caller is requesting a result.newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);}...final ActivityRecord[] outRecord = new ActivityRecord[1];int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,allowPendingRemoteAnimationRegistryLookup);Binder.restoreCallingIdentity(origId);if (stack.mConfigWillChange) {// If the caller also wants to switch to a new configuration,// do so now.  This allows a clean switch, as we are waiting// for the current activity to pause (so we will not destroy// it), and have not yet started the next activity.mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,"updateConfiguration()");stack.mConfigWillChange = false;if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,"Updating to new configuration after starting activity.");mService.updateConfigurationLocked(globalConfig, null, false);}if (outResult != null) {outResult.result = res;final ActivityRecord r = outRecord[0];switch(res) {//启动成功case START_SUCCESS: {mSupervisor.mWaitingActivityLaunched.add(outResult);do {try {mService.wait();} catch (InterruptedException e) {}} while (outResult.result != START_TASK_TO_FRONT&& !outResult.timeout && outResult.who == null);if (outResult.result == START_TASK_TO_FRONT) {res = START_TASK_TO_FRONT;}break;}//Activity添加到栈顶case START_DELIVERED_TO_TOP: {outResult.timeout = false;outResult.who = r.realActivity;outResult.totalTime = 0;outResult.thisTime = 0;break;}//Activity到前台显示case START_TASK_TO_FRONT: {// ActivityRecord may represent a different activity, but it should not be// in the resumed state.if (r.nowVisible && r.isState(RESUMED)) {outResult.timeout = false;outResult.who = r.realActivity;outResult.totalTime = 0;outResult.thisTime = 0;} else {outResult.thisTime = SystemClock.uptimeMillis();mSupervisor.waitActivityVisible(r.realActivity, outResult);// Note: the timeout variable is not currently not ever set.do {try {mService.wait();} catch (InterruptedException e) {}} while (!outResult.timeout && outResult.who == null);}break;}}}mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);return res;}}

执行流程走到了startActivityUnchecked (…)方法,负责处理栈的逻辑。方法中会根据LaunchMode类型,决定是否需要将Activity插入堆栈,并且设置默认的DisplayId(如需设置默认的虚拟屏id则在此处设置)

      private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,ActivityRecord[] outActivity) {setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,voiceInteractor);computeLaunchingTaskFlags();computeSourceStack();mIntent.setFlags(mLaunchFlags);//根据LaunchMode类型,决定是否需要将Activity插入堆栈ActivityRecord reusedActivity = getReusableIntentActivity();//设置DisplayId(虚拟屏id)int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;int preferredLaunchDisplayId = DEFAULT_DISPLAY;if (mOptions != null) {preferredWindowingMode = mOptions.getLaunchWindowingMode();preferredLaunchDisplayId = mOptions.getLaunchDisplayId();}// windowing mode and preferred launch display values from {@link LaunchParams} take// priority over those specified in {@link ActivityOptions}.if (!mLaunchParams.isEmpty()) {if (mLaunchParams.hasPreferredDisplay()) {preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;}if (mLaunchParams.hasWindowingMode()) {preferredWindowingMode = mLaunchParams.mWindowingMode;}}…// If the activity being launched is the same as the one currently at the top, then// we need to check if it should only be launched once.//确认目标Activity是否已经在栈顶,final ActivityStack topStack = mSupervisor.mFocusedStack;final ActivityRecord topFocused = topStack.getTopActivity();final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);final boolean dontStart = top != null && mStartActivity.resultTo == null&& top.realActivity.equals(mStartActivity.realActivity)&& top.userId == mStartActivity.userId&& top.app != null && top.app.thread != null&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));if (dontStart) {// For paranoia, make sure we have correctly resumed the top activity.topStack.mLastPausedActivity = null;if (mDoResume) {mSupervisor.resumeFocusedStackTopActivityLocked();}ActivityOptions.abort(mOptions);if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {// We don't need to start a new activity, and the client said not to do// anything if that is the case, so this is it!return START_RETURN_INTENT_TO_CALLER;}deliverNewIntent(top);// Don't use mStartActivity.task to show the toast. We're not starting a new activity// but reusing 'top'. Fields in mStartActivity may not be fully initialized.mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,preferredLaunchDisplayId, topStack);return START_DELIVERED_TO_TOP;}. . .mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,preferredLaunchDisplayId, mTargetStack);return START_SUCCESS;
}

4.ActivityStackSupervisor类中resumeFocusedStackTopActivityLocked (…)

走到了ActivityStackSupervisor类中resumeFocusedStackTopActivityLocked (…)方法,方法中会判断目标Activity是否在栈顶,确保处于栈顶的Activity处于onResume状态,如已在栈顶,则会调用WindowManager对象执行画面切换。

   boolean resumeFocusedStackTopActivityLocked(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {if (!readyToResume()) {return false;}if (targetStack != null && isFocusedStack(targetStack)) {return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}//获取要启动的Activity所在栈的栈顶final ActivityRecord r = mFocusedStack.topRunningActivityLocked();if (r == null || !r.isState(RESUMED)) {//如果未在堆栈中或者状态不在前台,确保Activity处于堆栈栈顶和前台mFocusedStack.resumeTopActivityUncheckedLocked(null, null);} else if (r.isState(RESUMED)) {//如果已经在栈顶,调用WindowManager对象执行画面切换// Kick off any lingering app transitions form the MoveTaskToFront operation.mFocusedStack.executeAppTransition(targetOptions);}return false;}

5. ActivityStack类中的resumeTopActivityUncheckedLocked(…)

关于如何处理原栈顶Activity和新Activity的状态的顺序是在ActivityStack类中的resumeTopActivityUncheckedLocked(…)方法中先将目标拉起的Activity设置进入onResumed()状态,然后将原栈顶Activity设置进入onPause()状态流程。

    @GuardedBy("mService")boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {if (mStackSupervisor.inResumeTopActivity) {// Don't even start recursing.return false;}boolean result = false;try {// Protect against recursion.mStackSupervisor.inResumeTopActivity = true;//执行目标Activity进入onResume状态result = resumeTopActivityInnerLocked(prev, options);// When resuming the top activity, it may be necessary to pause the top activity (for// example, returning to the lock screen. We suppress the normal pause logic in// {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the// end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here// to ensure any necessary pause logic occurs. In the case where the Activity will be// shown regardless of the lock screen, the call to// {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.//执行此前栈顶Activity进入onPause状态final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);if (next == null || !next.canTurnScreenOn()) {checkReadyForSleep();}} finally {mStackSupervisor.inResumeTopActivity = false;}return result;}

学习心得

AMS的内容非常庞大且复杂,通过梳理只是对其中的Activity启动流程有了初步认识,还需要通过日积月累增强自身能力强化对相关内容的理解。本文的最大作用是初步梳理的Activity启动流程(注明了源码的类和方法,及其含义)

Activity启动过程源码流程梳理和解读相关推荐

  1. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  2. Activity启动流程源码分析-浅析生命周期函数

    源码分析 接着上一篇 Activity启动流程源码分析-setContentView源码阅读 的讲解,本节介绍一下Activity的生命周期函数何时被调用 要看Activity的生命周期函数何时被调用 ...

  3. Activity启动过程源码分析

    老罗的Android系统源码分析讲的很不错,网上有很不同层面多源码分析.了解细节,还是自己看源码最直接.个人并没有透彻的研究过Android系统,这一系列的博客就当是读Android源码笔记了.有不对 ...

  4. Tomcat - Tomcat 8.5.55 启动过程源码分析阶段三_start阶段

    文章目录 启动流程分析 Pre Star阶段 start总览 start源码分析 StandardServer Start StandardService Start StandardEngine S ...

  5. Tomcat - Tomcat 8.5.55 启动过程源码分析阶段二_load加载初始化

    文章目录 启动流程分析 Pre load 加载初始化 总体预览 源码解析 load() Server初始化 Service初始化 Engine初始化 Connector 初始化 小结 启动流程分析 P ...

  6. Service通过onBind跨进程启动流程源码探究

    根据<Activity跨进程启动流程源码探究>我们可以清楚以下几点: 1)Context的通用实现是在ContextIml这个类中 2)Activity的启动过程需要借助ActivityM ...

  7. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  8. Doris FE启动流程源码详细解析

    Doris FE启动流程源码详细解析 一.简介 Apache Doris是一个现代化的MPP分析型数据库产品.仅需亚秒级响应时间即可获得查询结果,有效地支持实时数据分析.Apache Doris的分布 ...

  9. Android音频框架之二 用户录音启动流程源码走读

    前言 此篇是对<Android音频框架之一 详解audioPolicy流程及HAL驱动加载>的延续,此系列博文是记录在Android7.1系统即以后版本实现 内录音功能. 当用户使用 Au ...

最新文章

  1. SharePoint 2013 Word 转换PDF服务介绍及示例
  2. vue使用总结-生命周期篇
  3. 入门话题1. 在Web中控制图的显示外观?把一张500*800 的图, 显示成180*110 的小图....
  4. 2017蓝桥杯省赛---java---C---1(外星日历)
  5. Redis:02---安装Redis(Linux+Windows+Docker)
  6. (转载)spring配置hibernate 事务。
  7. 官网mysql安装目录_官网下载MySQL 并安装
  8. 遗传算法的简介与应用详细过程
  9. XP 远程连接window 2008 网络级别身份验证问题解决方法
  10. 众神的盛宴!阿里巴巴数学竞赛颁奖典礼,在江湖中一直流传许久的传说一一现身!...
  11. 机械硬盘计算机管理,机械硬盘怎么分区
  12. DataBinding详解
  13. Windows下Python无法正常卸载:There is a problem with this Windows Installer package.
  14. 关于股息、增发、回购的个人看法
  15. 安装awvs14.7
  16. 学习图像处理与模式识别一点体会
  17. UG NX 12 布尔求和
  18. 评程序员和会不会修电脑到底有几毛钱关系?
  19. 为什么用恒流电源驱动LED灯具
  20. java 通过 SmbFile 类操作共享文件夹(1)

热门文章

  1. FFMpeg编译之路
  2. 密码的自动生成器:密码由大写字母/小写字母/数字组成,生成12位随机密码
  3. 考研最卷的专业,我们替你查到了!
  4. 推荐一些前端小姐姐的公众号
  5. 雷神黑武士5代shark评测
  6. Windows系统图片不显示缩略图如何解决
  7. word文件太大怎么压缩到最小-word压缩教程
  8. 小程序审核出现的虚拟支付审核失败
  9. PDF文档免费转成Word文档,不限页数。
  10. 去除IDEA中代码的波浪线(黄色警示线)