首页 > 其他分享 >深入理解Activity启动流程和AMS框架(三)

深入理解Activity启动流程和AMS框架(三)

时间:2024-07-29 20:43:02浏览次数:10  
标签:Task 启动 流程 next Activity AMS null ......

链接https://cloud.tencent.com/developer/article/1601480

续:

深入理解Activity启动流程和AMS框架(一)

深入理解Activity启动流程和AMS框架(二)

5、Task和LauncherMode

(1)、如何才能开始一个新的Task?

Intent中定义了一个标志FLAGACTIVITYNEW_TASK,在startActivity的Intent参数中加入该标志就能开启一个新的Task。但是,如果系统中已经有相同affinity的Task存在,这时候就不会再启动一个Task,而是将旧的Task带到前台。 Affinity的意思是“亲和度”、“密切关系”,它的类型是字符串,我们可以把它理解成Task的名称。Affinity字串在系统中是唯一的,AMS查找一个Task,最优先比较它的affinity。ActivityStack类中用来查找Task的方法是findTaskLocked()。

代码语言:javascript 复制
ActivityRecord findTaskLocked(ActivityRecord target) {
     ......
     for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
         final TaskRecord task = mTaskHistory.get(taskNdx);
         ......
         if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
             if (task.rootAffinity.equals(target.taskAffinity)) {
                 return r;
             }
         } else if (taskIntent != null && taskIntent.getComponent() != null &&
                 taskIntent.getComponent().compareTo(cls) == 0 &&
                 Objects.equals(documentData, taskDocumentData)) {
             return r;
         } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
                 affinityIntent.getComponent().compareTo(cls) == 0 &&
                 Objects.equals(documentData, taskDocumentData)) {
             return r;
         }
     }     return null;
}

findTaskLocked()方法首先遍历已有TaskRecord对象的affinity变量是否等于ActivityRecord的taskAffinity变量,如果相同就直接把旧的Task带回前台,而不是new一个新的TaskRecord。 既然一个Task的affinity这么重要,它是在哪里定义的呢?在AndroidManifest.xml文件中: - Activity标签的taskAffinity属性:当使用标志FLAGACTIVITYNEW_TASK启动一个Activity时才起作用 - Application标签的taskAffinity属性:没有指定activity标签的taskAffinity属性的,将会继承application标签的taskAffinity属性 - 应用的包名packageName:没有指定的情况,默认值

通常在开发中,很少应用会自定义一个taskAffinity属性,所以默认就是其包名。因此,在应用中如果启动本应用的另一个Activity,即便intent里添加了FLAGACTIVITYNEW_TASK也不一定会启动一个新的Task,除非这个Activity定义了不同的taskAffinity属性。

(2)、Activity对象的复用

启动一个Activity时,如果系统的后台Task已经有一个该Activity的实例存在,那么系统会再创建一个新的Activity实例,还是将已经存在的Activity实例切换到前台呢? 答案是:都有可能。有很多因素可以影响结果,包括Activity的属性值以及Intent中指定的标志。我们先看看Activity的属性launcherMode会有哪些影响:

  • standard模式:standard模式下的Activity每次启动时都会创建该Activity的实例对象;同一个Task中可以同时存在该Activity的多个实例;一个Activity的多个实例可以出现在多个Task栈中。
  • singleTop模式:如果设置为singleTop模式的Activity实例位于Task的栈顶,则不会创建一个新的对象,但是该Activity对象切换到前台时,它的onNewIntent()方法将会被调用,新的intent通过这种方式传递给实例对象。如果Activity不在其Task的栈顶,就和standard模式一样,会创建新的实例对象。
  • singleTask模式:设置为singleTask模式的Activity具有系统唯一性,只能在系统中创建该Activity的一个实例对象。启动设置为singleTask的Activity时,如果系统中已经存在该Activity的实例,则将其所在的Task排在它前面的Activity都出栈,将该Activity带到栈顶,并调用onNewIntent()方法,将新的intent传递给该实例。如果该Activity在系统中还没有实例对象,就会创建一个该Activity的实例对象,如果该Activity的taskAffinity属性值和当前Task的affinity值相同,它会加入到当前TAsk中,否则,即使启动该Activity的Intent中没有指定FLAG_ACTIVITYNEWTASK标志,也会启动新的Task,将Activity置于其中。
  • singleInstance模式:设置为singleInstance模式的Activity同样具有系统唯一性,系统中只有该Activity的一个实例对象。同时Activity位于一个单独的Task中,该Task中也只有一个Activity。
  • allowTaskReparenting属性:通常情况下,一个Activity创建出来后,会停留在某个Task中,直到它被销毁。但是如果Activity的allowTaskReparenting属性设置为true,则该Activity可以在不同的Task之间转移。但是,这个属性只有在启动Activity的Intent中设置了FLAGACTIVITYRESETTASKIF_NEEDED标志时才起作用。
  • allowRetainTaskState属性:默认情况下,如果一个Task位于后台的时间太长,系统会清理该Task中的Activity,除了最初启动的Task的Activity以外,其他的Activity都会被系统销毁。如果应用希望保留这些Activity,可以将启动Task的Activity的allowRetainTaskState属性设置为true。
  • clearTaskOnLaunch属性:前面介绍了,当使用带有标志FLAG_ACTIVITYNEWTASK的Intent启动一个Activity时,如果该Acitivty位于一个Task中,会将Task整体带到前台,其中Activity保持不变。但是如果该Activity启动的是Task的根Activity(root Activity),同时该Activity的属性clearTaskOnLaunch设置为true,那么系统出了将Task带到前台外,还会清除除了root Activity以外的所有Activity。因此,这个属性的作用相当于每次销毁Task,然后重新开始一个。
  • finishOnTaskLaunch属性:设置为true,系统将会销毁该Activity,然后重新再启动一个。

除了FLAGACTIVITYNEWTASK标志以外,Intent中还定义几个和Activity相关的标志: - FLAGACTIVITYCLEARTOP:如果启动的Activity已经存在,则把该Activity带到前台,并把它前面的Activity都出栈。 - FLAGACTIVITYBROUGHTTOFRONT:如果启动的Activity已经存在,则把该Activity带到前台,但是不关闭它前面的Activity。 - FLAG_ACTIVITYSINGLETOP:如果启动的Activity已经位于Task的栈顶,则不会创建一个新的Activity,而是把该Activity带到前台。

Android开发——Intent中的各种FLAG http://blog.csdn.net/javensun/article/details/8700265

6、Activity的启动流程

(1)、startActivityMayWait()方法

AMS中提供了几个接口来启动Activity,但是,它们都会调用ActivityStackSupervisor类的startActivityMayWait()方法。

代码语言:javascript 复制
public final class ActivityStackSupervisor implements DisplayListener {
   ......
   final 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 config,
               Bundle options, boolean ignoreTargetSecurity, int userId,
               IActivityContainer iContainer, TaskRecord inTask) {
       ......
       intent = new Intent(intent);  // 创建一个新的Intent对象,方便改动
       ActivityInfo aInfo =      // 获取即将启动的Activity的信息
               resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
       ......
       // 调用startActivityLocked方法
       int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                   voiceSession, voiceInteractor, resultTo, resultWho,
                   requestCode, callingPid, callingUid, callingPackage,
                   realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                   componentSpecified, null, container, inTask);
       ...
       if (outResult != null) {
            outResult.result = res;
            if (res == ActivityManager.START_SUCCESS) {
                mWaitingActivityLaunched.add(outResult);
                do {
                    try {
                        mService.wait();  // 等待应用进程中Activity的启动完成
                    } catch (InterruptedException e) {
                    }
                } while (!outResult.timeout && outResult.who == null);
            }
           ......    
       }
       ......
       return res;
   }
}

startActivityMayWait()首先调用resolveActivity()方法获取需要启动的Activity的信息,resolveActivity()方法通过调用PackageManagerService的resolveIntent()方法来获取Activity的信息,得到Activity的信息后,继续调用startActivityLocked()方法来继续启动Activity。如果启动Activity的应用需要返回结果,则调用mService对象的wait()方法挂起线程等待启动的结果。

(2)、startActivityLocked()方法
代码语言:javascript 复制
final int startActivityLocked(IApplicationThread caller,
           Intent intent, String resolvedType, ActivityInfo aInfo,
           IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
           IBinder resultTo, String resultWho, int requestCode,
           int callingPid, int callingUid, String callingPackage,
           int realCallingPid, int realCallingUid, int startFlags, Bundle options,
           boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
           ActivityContainer container, TaskRecord inTask) {
   ......
   int err = ActivityManager.START_SUCCESS;
   ProcessRecord callerApp = null;
   if (caller != null) {
       callerApp = mService.getRecordForAppLocked(caller);  // 得到调用进程的信息
       ...
   }
   ...... // 错误检查
   final int startAnyPerm = mService.checkPermission(
               START_ANY_ACTIVITY, callingPid, callingUid);   // 检查调用者权限
   ......
   // 创建ActivityRecord对象
   ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
               intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
               requestCode, componentSpecified, voiceSession != null, this, container, options);
  ......
  // 继续调用startActivityUncheckedLocked启动当前的Activity
  err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
               startFlags, true, options, inTask);  
  ......
  return err;                    
}

startActivityLocked()方法首先进行了各种错误检查,接着检查调用者的权限,以及Intent防火墙是否屏蔽了该Intent(规则是通过/data/system/ifw目录下的文件设置的)。完成所有检查后,穿件一个ActivityRecord对象,并调用getFocusedStack()方法来获取当前具有用户输入焦点的ActivityStack。

(3)、startActivityUncheckedLocked()方法
代码语言:javascript 复制
final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
           IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
           boolean doResume, Bundle options, TaskRecord inTask) {
   // r:即将启动的Activity
   // sourceRecord:启动r的源Activity,有可能为null
   ......
   targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
   if (!launchTaskBehind) {
       mService.setFocusedActivityLocked(r, "startedActivity");
   }
   return ActivityManager.START_SUCCESS;
}

startActivityUncheckedLocked()方法的代码非常长,主要是通过判断Intent的标志和Activity的属性来确定Activity的Task,对于处理的细节我们就不分析了。有兴趣的童鞋可以结合上一届的内容自行分析。方法中找到包含Activity的Task后,调用ActivityStack的startActivityLocked()方法继续启动。

(4)、startActivityLocked()方法
代码语言:javascript 复制
final void startActivityLocked(ActivityRecord r, boolean newTask,
           boolean doResume, boolean keepCurTransition, Bundle options) {
     TaskRecord rTask = r.task;
     final int taskId = rTask.taskId;
     if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
         insertTaskAtTop(rTask, r);  // 如果是新的Task,就把它放在顶部
         mWindowManager.moveTaskToTop(taskId);
     }
     TaskRecord task = null;
     if (!newTask) {   // 如果不需要启动新的Task
         boolean startIt = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             task = mTaskHistory.get(taskNdx);
             if (task.getTopActivity() == null) {
                 continue;
             }
             if (task == r.task) {
                 ......
                 break;  // 找到了Task,跳出循环
             } ...
         }
     }     if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
         mStackSupervisor.mUserLeaving = false;
     }
     task = r.task;
     task.addActivityToTop(r);  // 把Activity放到已找到Task的顶部
     task.setFrontOfTask();
     r.putInHistory();     // 调用WindowManagerService中的方法准备绘制Activity以及切换Activity动画
     if (!isHomeStack() || numActivities() > 0) {
        // 如果不是Home应用的Stack或者Stack中有Activity,
        ......
     } else {
         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                 (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
                 r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
         ActivityOptions.abort(options);
         options = null;
     }
     if (VALIDATE_TOKENS) {
         validateAppTokensLocked();
     }     if (doResume) {
         // 启动Activity
         mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
     }
 }

这里调用了WindowManagerService的方法处理Activity的显示和切换动画。WMS以后由可视化组的同事讲解哈。

ActivityStack类的startActivityLocked()方法相对比较简单,就是讲ActivityRecord对象加入到Task的顶部,同时把Task也放到mHistoryStack列表的顶部。方法的最后通过mStackSupervisor对象的resumeTopActivitiesLocked方法来显示位于Task栈顶的Activity,这个方法在AMS中经常会被调用,主要作用是将位于栈顶的Activity显示出来。这时,当前的Activity(mResumedActivity对象引用)还显示在屏幕上。它最终会调用ActivityStack类的resumeTopActivityInnerLocked()方法,下来我们直接分析它的执行过程。

(5)、resumeTopActivityInnerLocked()方法
代码语言:javascript 复制
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
   ......
   final ActivityRecord next = topRunningActivityLocked(null); // next表示即将启动的Activity
   if (next == null) {
       ...... // 如果当前Task没有Activity,显示Home Activity
       return isOnHomeDisplay() &&  
                   mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
   }
   ...
   if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                   mStackSupervisor.allResumedActivitiesComplete()) {
      ...
      return false;  // 如果当前的Activity就是要启动的Activity,直接返回
   }
   ......
   if (mService.isSleepingOrShuttingDown()
               && mLastPausedActivity == next
               && mStackSupervisor.allPausedActivitiesComplete()) {
       ...
       return false;  // 如果系统正在睡眠或关闭,直接退出
   }
   ......
   if (prev != null && prev != next) {
      if (!mStackSupervisor.mWaitingVisibleActivities.contains(prev)
               && next != null && !next.nowVisible) {
           // 如果Activity还不可见,把前一个Activity加入mWaitingVisibleActivities列表
           mStackSupervisor.mWaitingVisibleActivities.add(prev);
       } else {
           if (prev.finishing) {  // 如果Activity已经是visible状态,把前一个Activity隐藏起来
               mWindowManager.setAppVisibility(prev.appToken, false);
           }
       }
   }
   ......
   boolean anim = true;
   ......  // 调用WindowManagerService的方法处理Activity的显示
   ActivityStack lastStack = mStackSupervisor.getLastStack();
   if (next.app != null && next.app.thread != null) {
       // 如果Activity的应用进程已存在,只需要把Activity显示出来即可
       mWindowManager.setAppVisibility(next.appToken, true);
       ......
       try {
           ArrayList<ResultInfo> a = next.results;
           if (a != null) {  // 如果Activity中海油等待返回的结果,先发送结果
               final int N = a.size();
               if (!next.finishing && N > 0) {
                   next.app.thread.scheduleSendResult(next.appToken, a);
               }
           }
           if (next.newIntents != null) {  
               // 如果满足前面介绍的几种重启Activity的情况,调用onNewIntent方法
               next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
           }
           ...
           // 调用应用进程Activity的onResume流程
           next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                       mService.isNextTransitionForward(), resumeAnimOptions);
           mStackSupervisor.checkReadyForSleepLocked();
       } catch (Exception e) {
           ......
           return true;
       }
   } else {
       // 如果Activity所在的应用进程还没有启动,就先启动应用
       ......
       mStackSupervisor.startSpecificActivityLocked(next, true, true);
   }
   return true;
}

resumeTopActivityInnerLocked()方法虽然很长,但是去掉log和一些不太重要的分支代码后,整个逻辑还是比较清晰的,上面注释已经很详细了,这里不再重复。如果Activity所在的应用已经启动,这里将会调用应用进程的scheduleResumeActivity()方法,最终会回调ActivityThread执行Activity的onResume()方法。如果应用还没有启动,或者刚启动,则调用startSpecificActivityLocked()方法继续处理。

(6)、startSpecificActivityLocked()方法
代码语言:javascript 复制
void startSpecificActivityLocked(ActivityRecord r,
           boolean andResume, boolean checkConfig) {
   ProcessRecord app = mService.getProcessRecordLocked(r.processName,
           r.info.applicationInfo.uid, true);
   r.task.stack.setLaunchTime(r);
   if (app != null && app.thread != null) { // 判断应用进程是否已启动
       try {
           if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                   || !"android".equals(r.info.packageName)) {
               app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                       mService.mProcessStats);
           }
           realStartActivityLocked(r, app, andResume, checkConfig);
           return;
       } catch (RemoteException e) {
           Slog.w(TAG, "Exception when starting activity "
                   + r.intent.getComponent().flattenToShortString(), e);
       }
   }
   // 启动应用进程
   mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
           "activity", r.intent.getComponent(), false, false, true);
}

startSpecificActivityLocked()方法中如果发现应用进程没有启动,则调用AMS的startProcessLocked()方法来启动进程(前面章节已经介绍过),否者调用realStartActivityLocked()方法继续执行。realStartActivityLocked()比较长,这里就不分析了,它最后调用到应用进程中的scheduleLaunchActivity()方法,从而回调Activity的onCreate流程。

7、框架图

通过上面AMS启动Activity所调用到的方法分析,这样Activity启动中涉及的所有回调接口我们都找到哦啊了调用的地方,整个流程也就非常清晰。

分析Activity启动流程,无非就是牢牢把握住两条主线:应用进程(ActivityThread)和服务端进程(AMS)

 

标签:Task,启动,流程,next,Activity,AMS,null,......
From: https://www.cnblogs.com/zhanyaowang/p/18331020

相关文章

  • 深入理解Activity启动流程和AMS框架(一)
    链接https://mp.weixin.qq.com/s?__biz=MzIwNjQ1NzQxNA==&mid=2247484149&idx=1&sn=fea623b475af3f05c657c1e55e3c478f&chksm=97201ddca05794cab15fa098ffb0ce4b5ca7791e1023e0e87969d89e0dbcfce28327ac9221e8&scene=21#wechat_redirect一、前言一个App是怎么启动......
  • 深入理解Activity启动流程和AMS框架(二)
    链接https://mp.weixin.qq.com/s?__biz=MzIwNjQ1NzQxNA==&mid=2247484150&idx=1&sn=4007a9cff85df88941e73869b89b1ed4&chksm=97201ddfa05794c9e33c8d0d9e83f407922d24282dc9bd8d67c72debf9713abb1b0586f00bc9&scene=21#wechat_redirect续 深入理解Activity启动流程......
  • Android 10.0 Launcher 启动流程
    在前面SystemUI启动流程中说到,在SystemServer中会去启动各种系统服务,这里的launcher也是启动的其中一个服务ActivityManagerService去启动的。在android10之前,系统四大组件的启动都是在ActivityManagerService中,在android10中,单独抽出了一个ActivityTaskManagerService,主要......
  • (六)Redis 消息队列 List、Streams
    Redis适合做消息队列吗?有什么解决方案?首先要明白消息队列的消息存取需求和工作流程。1、消息队列我们一般把消息队列中发送消息的组件称为生产者,把接收消息的组件称为消费者,下图是一个通用的消息队列的架构模型:消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重......
  • 发布npm包流程
    npm发布包流程背景:假设你写了一些js方法想发布参考:https://www.youtube.com/watch?v=xnfdm-s8adI、1.初始化环境mkdirmethodjscdmethodjsnpminit-ynpmi-Dtsup@5typescript@4#node<14时的兼容版本,tsup用于编译打包ts代码#确保用的是npm地址npmconfig......
  • SpringMVC请求执行流程
    SpringMVC是一个基于Spring框架的MVC(Model-View-Controller)框架,它简化了Web应用程序的开发。在SpringMVC中,请求的执行流程是一个精心设计的过程,涉及多个核心组件的协同工作。以下是对SpringMVC请求执行流程的详细解析,内容不少于2000字。一、SpringMVC请求执行流程概述Spri......
  • 流程控制语句结构
    流程控制语句结构目录流程控制语句结构1.顺序结构定义与特点示例2.分支结构2.1.if-else语句2.2.switch语句3.循环结构1.for循环2.while循环3.do-while循环使用场景和注意事项4.跳转语句5.绘制程序流程图基本步骤常用符号示例工具1.顺序结构定义与特点定义:顺序结构是指程......
  • 计算机毕业设计实现流程,看这篇就够了(1.2w字超详细流程)
    计算机专业毕业设计实现流程 目录如何完成毕业设计选题提交毕业设计任务书完成系统分析和系统设计完成项目编码工作撰写论文项目答辩 1.完成毕业设计选题选题的类型计算机毕业相关的设计最近几年类型比较多的题目为:XX管理系统、XX网站建设、XX小程序设计与实现、XX公......
  • 【版本控制系统Git】Git的基本操作与工作流程
    版本控制系统GitGit的基本操作与工作流程目录引言Git概述Git的基本操作安装Git配置Git创建仓库常用命令Git的工作流程克隆仓库分支管理提交更改合并分支解决冲突最佳实践结论引言在现代软件开发中,版本控制系统(VCS)是不可或缺的工具。它能够帮助开发团队管理代码的......
  • 干货!全网最权威最全面介绍网络短剧上线资质以及备案流程
    今天,小编给大家详细梳理汇总一个全网最权威最全面的介绍网络微短剧上线所需要的资质,以及短剧备案流程到底是怎么样的?100%内行人实战总结的干货,希望对广大短剧从业者有所帮助。一、从事网络微短剧需要哪些资质?1、广播电视节目制作经营许可证(必备)根据《广播电视节目制作经营管......