Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android Frameworks系列(二) 彻底弄懂startActivity

$
0
0

  在Android Frameworks系列(一) startService启动 一文中我们介绍了startService启动的过程,和startService一样,startActivity也是以ActivityManagerService为核心工作的。原理也差不多,不过要比startService复杂,因为Activity的生命周期更多,并且还涉及UI方面的工作。为了分析包括zygote孵化目标进程,创建task等过程,就以Launcher启动应用来说明,另外因为在startService中分析过其详细的调用链,本篇不会再详细的贴出其调用链的代码,只会分析核心方法的功能和程序执行的流程,具体代码大家可以参照流程图具体去分析。
  我们知道当用户点击Launcher应用中对应的应用程序(TargetApp)图标时,Launcher将启动TargetApp。具体过程为Launcher通知ActivityManagerService启动TargetApp的入口Activity,ActivityManagerService发现TargetApp进程还没有创建,所以通知Zygote去fork一个进程,然后在该进程中经过一系列的操作执行ActivityThread的main方法。在TargetApp准备就绪后会通知ActivityManagerService应用已经启动,ActivityManagerService会持有TargetApp的一个代理对象,通过这个代理对象ActivityManagerService可以通知TargetApp创建Activity实例,开始Activity的生命周期。
  
  从上图可以看出整个过程涉及4个进程间通信。通信方式其实在Android Frameworks系列(一) startService启动 一文中已经分析过。用到了Binder和Socket两种方式。具体可以看一下上文。
  我们上边说了,当用户点击Launcher上的应用图标,Launcher会通知ActivityManagerService启动TargetApp,也就是说,当用户点击图标后,经常一系列的操作,会进入到ActivityManagerService的startActivity方法。我们就从startActivity开始分析吧!
  在此之前,先认识一下下面几个看起来非常相似的方法:
  ActivityManagerService:startActivityAsUser()
  ActivityStarter:startActivityMayWait()
  ActivityStarter:startActivityLocked()
  ActivityStarter:startActivityUnchecked()
  这几个方法会依次调用,并且都有明确的分工。接下来分析他们都做了什么,和我们平时的开发有什么关系。
  

@Override
    public 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());
    }

  ActivityManagerService的startActivityAsUser()方法只比startActivity多了最后一个UserId参数。这个UserId可以通过Binder机制的getCallingUid()方法获取到。有了这个UserId,ActivityManagerService就可以检查调用者的权限检查:
  

@Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false, ALLOW_FULL_ONLY, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, null);
    }

  enforceNotIsolatedCaller(“startActivity”)检查调用者是否被隔离,如果被隔离会抛出SecurityException。handleIncomingUser()检查调用者是否有权限执行call操作。
  
        
        
  总得来说startActivityAsUser就是做一下调用者的基本权限的检查。通过之后,就交个ActivityStarter的startActivityMayWait()方法。这个方法很长,但是其功能还是非常清晰的:
  
        
        
  相信我们开发Android应用都清楚,start一个Activity时,Intent可以是显式的,也可以是隐式的。当Intent是显式的时候,Intent已经有了目标Activity的信息,但是如果是隐式的话,就需要通过ActivityStackSupervisor的resolveActivity来匹配了:
  

ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

  接着会判断目标进程是不是重量级进程(heavy-weight),判断是否已经存在不同于目标进程的重量级进程,如果存在则会重新赋值intent。之后会继续执行startActivityLocked方法。最后会根据传入的outResult是否为空来等待是否将执行的结果写入到outResult中,在源码中是一个while循环来达到等待的目的,我们在startActivity时传入的outResult为null,所在在这个过程中不会执行while循序。不过这或许是startActivityMayWait名字中有一个mayWait的原因。接着分析startActivityLocked()方法。
  在分析startService一文说过方法名后带有Locked的一般都是要求同步的,因为方法中会操作临界资源。

        
  
  startActivityLocked()方法做的第一件事就是判断caller进程是否还存在,因为caller进程在发出启动请求后,被系统kill掉了,所以这里还是要判断一下,如果已经kill掉了,直接返回。如果还存活着,接着处理launchFlags,判断是不是Intent.FLAG_ACTIVITY_FORWARD_RESULT,那么这个标记是什么意思呢?举个开发中可能遇到的需求:
  这里写图片描述

  ActivityA启动ActivityB,ActivityB再启动ActivityC,当C处理完成后直接返回ActivityA而不是ActivityB,也就是跨越式传递。Intent.FLAG_ACTIVITY_FORWARD_RESULT就是达到这个目的而存在的。
  当启动ActivityC的launchFlags是Intent.FLAG_ACTIVITY_FORWARD_RESULT时,需要将caller由ActivityB修改为ActivityA,这样才能达到ActivityC在处理完事件后setResult时返回到ActivityA。也正是由于这个原因,ActivityB不能以startActivityForResult启动ActivityC,否则会报
ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT的异常而退出。
  接着会判断是否匹配到Intent的目标,如果没有匹配到,那么将不会再处理该Intent,程序直接返回。如果匹配到,那么接着校验caller是否有权限启动目标Activity。权限必须全部校验通过,否则将报SecurityException。以上的判断结果都将存放在ActivityRecord类型的变量r中,然后继续执行startActivityUnchecked()方法,该方法涉及一系列的启动模式,可以结合Android启动模式之lunchMode 来加深印象。
  在startActivityUnchecked方法中开始会执行以下两个方法:
  

setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);

        computeLaunchingTaskFlags();

  setInitialState()方法校验intent的flag是否是特定的flag

         

  FLAG_ACTIVITY_NO_USER_ACTION这个flag表示不是用户触发的启动Activity,而是由于像来电,闹铃等触发的Activity启动。
  START_FLAG_ONLY_IF_NEEDED表示只有在需要的时候才启动目标Activity。也就是说如果调用者和被启动的是一个,那么就没有必要去进行重复的步骤了。
  FLAG_ACTIVITY_NO_ANIMATION表示是否有Animation。
  
  computeLaunchingTaskFlags()主要就是判断是否需要启动新的task。其核心代码如下:
  

if (mSourceRecord == null) {
                // This activity is not being started from another...  in this
                // case we -always- start a new task.
                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
                // The original activity who is starting us is running as a single
                // instance...  this new activity it is starting must go on its
                // own task.
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            } else if (mLaunchSingleInstance || mLaunchSingleTask) {
                // The activity being started is a single instance...  it always
                // gets launched into its own task.
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            }

  如果mSourceRecord为null,表明我们应该新开一个task,也就是会设置mLaunchFlags 为FLAG_ACTIVITY_NEW_TASK。如果mSourceRecord的launchMode为LAUNCH_SINGLE_INSTANCE或者目标Activity的启动模式为singleTask或者singleInstance都需要从新创建一个task。
  回到startActivityUnchecked中会继续处理flag,由于flag有很多,我将挑几个我们平时可能遇到的讲。
  1.如果同时设置了FLAG_ACTIVITY_NEW_TASK 和FLAG_ACTIVITY_CLEAR_TASK 那么将清理整个的task,然后将目标Activity放入task中,如图ActivityB启动ActivityC时设置这两个标志,那么最后ActivityA和ActivityB都将被清理。
  这里写图片描述

  2.如果设置了FLAG_ACTIVITY_CLEAR_TOP或者launchMode为singleTask、singleInstance。那么将会清理目标Activity之上的元素。否则如果top元素就是目标Activity那么将整个task放到最前边。并且会执行Activity的onNewIntent()方法,这一点相信大家都熟悉。和Android启动模式之lunchMode 的内容是对应的。
  
        

  处理这些之后,会执行ActivityStack的startActivityLocked方法。这个方法很关键,处理和WindowManagerService之间的交互,保证Activity对应的UI能在屏幕上显示出来。
  首先判断目标Activity所在的task是不是新建的task,如果不是在新task,那么还得查询是哪一个task。接着将目标Activity放到整个stack的最顶层,这样才能和用户产生交互。之后还会根据之前是否有FLAG_ACTIVITY_NO_ANIMATION标记来决定是否需要执行切换动画。我们知道一个Activity的UI能在屏幕上显示出来,必须通过appToken在WindowManagerService中已经注册。这个注册的过程就是在startActivity流程中注册的,源码很简单:  

void addConfigOverride(ActivityRecord r, TaskRecord task) {
        final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
        // TODO: VI deal with activity
        mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
                task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
                task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),
                r.appInfo.targetSdkVersion);
        r.taskConfigOverride = task.mOverrideConfig;
    }

  ActivityStack的startActivityLocked方法通过调用addConfigOverride方法完成这一操作。执行完这些操作之后,startActivity的一系列准备工作基本完成了,这时会回到ActivityStarter的startActivityUnchecked()方法中继续进行启动流程的处理。
  ActivityManagerService在以上步骤准备就绪后就要开始resume目标Activity了,不过在这之前需要先pause当前运行的Activity,这里其实也是涉及到Binder通信的,不过原理和之前startService中ActivityManagerService同应用通信的原理是一样的,这里不要赘述。在应用pause Activity成功之后,会通知ActivityManagerService,其接收到通知后,会有两个情况(同startService原理其实类似,可以参考):
  第一,如果目标Activity所在的进程已经存在,那么通过一系列的调用,利用ClassLoader将目标Activity,加载到内存中来,然后在开始调用其onCreate(),onResume()等一系列生命周期方法。这样Activity就真正的被启动起来了。
  第二,如果目标Activity所在的进程不存在,那么ActivityManagerService还得通知Zygote进程fork一个进程,之后初始化新进程,之后在加载目标Activity到内存中来,最终调用到目标Activity的onCreate(),onResume()等一系列生命周期方法,这和第一种情况是一样的。
  总结:startActivity整个流程代码调用链很长,涉及好几个跨进程通信。但是其核心的流程还是很清晰的。
  1.校验caller进程是否存在,caller进程是否有权限启动目标
  2.校验Intent参数是否合法,匹配Intent所对应的目标进程。
  3.根据各种flag和启动模式,处理源task,目标Activity所在task。
  4.向WindowManagerService添加AppToken,处理和WIndowManagerService的通信
  5.根据目标进程是否存在,决定是否通知Zygote孵化进程。之后将目标Activity加载到内存中,并执行其生命周期方法。

作者:chenkai19920410 发表于2017/1/13 14:47:35 原文链接
阅读:4 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>