前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解Activity启动流程和AMS框架(三)

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

作者头像
open
发布2020-03-19 17:06:30
1.1K0
发布2020-03-19 17:06:30
举报

续:

深入理解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)

本文由原作者彭小川独家授权Open软件开发小组发布,著作权归原作者所有。如需转载请申请授权。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-08-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Open软件开发小组 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 深入理解Activity启动流程和AMS框架(一)
  • 5、Task和LauncherMode
    • (1)、如何才能开始一个新的Task?
      • (2)、Activity对象的复用
      • 6、Activity的启动流程
        • (1)、startActivityMayWait()方法
          • (2)、startActivityLocked()方法
            • (3)、startActivityUncheckedLocked()方法
              • (4)、startActivityLocked()方法
                • (5)、resumeTopActivityInnerLocked()方法
                  • (6)、startSpecificActivityLocked()方法
                  • 7、框架图
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档