第二次启动android app的过程分析

网上关于在Launcher上第一次启动app的过程分析已经很多了,今天吃饭的时候在思考一个问题:我在Launcher上打开淘宝,切换到购物车,按Home键回到Launcher,再次点开淘宝的时候仍然是购物车页面。按说Launcher每次点开app都是启动同一个activityACTION_MAIN , CATEGORY_LAUNCHER),那么这步转换是在哪里完成的呢?

回来翻了翻代码,找到了答案:(有点长,后面会一步一步分析)

final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
      IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
        boolean doResume, Bundle options, TaskRecord inTask) {
    ... ...
    boolean addingToTask = false;
    TaskRecord reuseTask = null;
    ... ...
    if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
            (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
        if (inTask == null && r.resultTo == null) {
            ActivityRecord intentActivity = !launchSingleInstance ?
                    findTaskLocked(r) : findActivityLocked(intent, r.info);
            if (intentActivity != null) {
                ... ...
                if (r.task == null) {
                    r.task = intentActivity.task;
                }
                if (intentActivity.task.intent == null) {
                    intentActivity.task.setIntent(r);
                }
                targetStack = intentActivity.task.stack;
                targetStack.mLastPausedActivity = null;
                final ActivityStack focusStack = getFocusedStack();
                ActivityRecord curTop = (focusStack == null)
                        ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
                boolean movedToFront = false;
                if (curTop != null && (curTop.task != intentActivity.task ||
                    curTop.task != focusStack.topTask())) {
                    r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                    if (sourceRecord == null || (sourceStack.topActivity() != null &&
                        sourceStack.topActivity().task == sourceRecord.task)) {
                        ... ...
                        movedHome = true;
                        targetStack.moveTaskToFrontLocked(intentActivity.task,
                                       noAnimation, options, r.appTimeTracker, 
                                       "bringingFoundTaskToFront");
                        movedToFront = true;
                        ... ...
                    }
                ... ...
                }
                ... ...
                if (!addingToTask && reuseTask == null) {
                    // We didn't do anything...  but it was needed (a.k.a., client
                    // don't use that intent!)  And for paranoia, make
                    // sure we have correctly resumed the top activity.
                    if (doResume) {
                        targetStack.resumeTopActivityLocked(null, options);
                        if (!movedToFront) {
                            // Make sure to notify Keyguard as well if we are not running 
                            // an app transition later.
                            notifyActivityDrawnForKeyguard();
                        }
                    } else {
                        ActivityOptions.abort(options);
                    }
                    return ActivityManager.START_TASK_TO_FRONT;
                }
            }
        }
        ... ...
}
}

首先从Launcher启动的activity都会带NEW_TASKflag,因此进到这个条件中。

传进来的inTaskr.resultTo都是null,因此调用findTaskLocked()task history里查找是否存在这个activity或者它的affinity(姻亲),如果存在的话就返回当前栈顶的activity。由于是第二次启动app,因此肯定是能查得到的,这样说比较抽象,举个栗子吧:

01-18 23:13:55.870   736  1233 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.taobao.taobao/com.taobao.tao.welcome.Welcome bnds=[276,276][540,573] (has extras)} from uid 10008 on display 0
01-18 23:19:23.345   736   906 D ActivityManager: Comparing existing cls=com.taobao.taobao/com.taobao.tao.welcome.Welcome/aff=com.taobao.taobao to new cls=com.taobao.taobao/com.taobao.tao.welcome.Welcome/aff=com.taobao.taobao
01-18 23:19:23.345   736   906 D ActivityManager: Found matching affinity!

从这个log可以看出,点击淘宝图标后启动的是com.taobao.tao.welcome.Welcome这个activity,提示找到了它的affinity!看一下findTaskLocked()的代码:

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)) {
            if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
                return r;
            }
        } else if (taskIntent != null && taskIntent.getComponent() != null &&
            taskIntent.getComponent().compareTo(cls) == 0 &&
            Objects.equals(documentData, taskDocumentData)) {
            if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
            return r;
        } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
            affinityIntent.getComponent().compareTo(cls) == 0 &&
            Objects.equals(documentData, taskDocumentData)) {
            if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
            return r;
        } else if (DEBUG_TASKS) {
            Slog.d(TAG, "Not a match: " + task);
        }
    }
}
return null;
}

显然,这里命中了第一个条件判断,存在它的affinity。如果某些情况下不满足这个条件,那就会直接判断component是否相同,也就是是否存在这个activity

如果没有打开AMSlog,从dumpsys上也可以看出来:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
  Stack #1:
    Task id #45
    * TaskRecord{c15fe7c #45 A=com.taobao.taobao U=0 sz=1}
      userId=0 effectiveUid=u0a53 mCallingUid=u0a53 mCallingPackage=com.taobao.taobao
      affinity=com.taobao.taobao
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.taobao.taobao/com.taobao.tao.welcome.Welcome}
      realActivity=com.taobao.taobao/com.taobao.tao.welcome.Welcome
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1
      rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{f507b27 u0 com.taobao.taobao/com.taobao.tao.homepage.MainActivity3 t45}]
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=android.graphics.Bitmap@aa8b708 lastThumbnailFile=/data/system/recent_images/45_task_thumbnail.png
      stackId=1
      hasBeenVisible=true mResizeable=false firstActiveTime=1453130178152 lastActiveTime=1453130178152 (inactive for 14s)
      * Hist #0: ActivityRecord{f507b27 u0 com.taobao.taobao/com.taobao.tao.homepage.MainActivity3 t45}
          packageName=com.taobao.taobao processName=com.taobao.taobao
          launchedFromUid=10053 launchedFromPackage=com.taobao.taobao userId=0
          app=ProcessRecord{6e6642e 18539:com.taobao.taobao/u0a53}
          Intent { act=android.intent.action.VIEW dat=http://m.taobao.com/index.htm flg=0x14000000 pkg=com.taobao.taobao cmp=com.taobao.taobao/com.taobao.tao.homepage.MainActivity3 bnds=[318,302][498,482] (has extras) }
          frontOfTask=true task=TaskRecord{c15fe7c #45 A=com.taobao.taobao U=0 sz=1}
          taskAffinity=com.taobao.taobao
          realActivity=com.taobao.taobao/com.taobao.tao.homepage.MainActivity3
          baseDir=/data/app/com.taobao.taobao-1/base.apk
          dataDir=/data/user/0/com.taobao.taobao
          stateNotNeeded=false componentSpecified=true mActivityType=0
          compat={480dpi} labelRes=0x7f070259 icon=0x7f020133 theme=0x7f0b0017
          config={1.0 ?mcc?mnc en_US ldltr sw360dp w360dp h568dp 480dpi nrml port finger -keyb/v/h -nav/h s.6}
          stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
          taskDescription: iconFilename=null label="null" color=ffe6e6e6
          launchFailed=false launchCount=0 lastLaunchTime=-19s881ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false
          keysPaused=false inHistory=true visible=true sleeping=false idle=true
          fullscreen=true noDisplay=false immersive=false launchMode=2
          frozenBeforeDestroy=false forceNewConfig=false
          mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-14s134ms

看到没?Task history里有一个MainActivity3,也就是淘宝的主界面,它的taskAffinity和要启动的Welcome activity是一样的,都是com.taobao.taobao

在回到文章开头的代码继续分析,得到这个“intentActivity”之后,获取它的targetStack,也就是上面log里的stack 1

下面是获取curTop,也就是当前前台的activity。现在这里curTop就是LaunchercurTop.task肯定不等于intentActivity.task,进入这个条件里。

传进来的sourceRecordnull,因此进入到这个条件里调用targetStackmoveTaskToFrontLocked()方法。这样之前的task就移到stack 1的栈顶了。

再往下就是一马平川了,一路往下看一直到targetStack.resumeTopActivityLocked()。所以我们看到的现象就是:Launcher请求启动一个叫Welcomeactivity,最终执行的操作是resume stack 1栈顶的MainActivity3

多问一个问题:一般我们写app的时候很少用到taskAffinity属性,默认的taskAffinity是什么?搜一下发现在PackageParser.java里面:

private boolean parseBaseApplication(Package owner, Resources res,
        XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
... ...
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
          str, outError);
... ...
}

private static String buildCompoundName(String pkg,
CharSequence procSeq, String type, String[] outError) {
String proc = procSeq.toString();
char c = proc.charAt(0);
if (pkg != null && c == ':') {
    ... ...
    }
    return proc.intern();
}

很清楚了,默认的taskAffinity就是packageName,也就是com.taobao.taobao

这个Stringintern()方法又是个知识点,目的是节省空间:
调用s.intern()方法的时候,会将共享池中的字符串与外部的字符串(s)进行比较,如果共享池中有与之相等的字符串,则不会将外部的字符串放到共享池中的,返回的只是共享池中的字符串,如果不同则将外部字符串放入共享池中,并返回其字符串的句柄(引用)。

猜你喜欢

转载自blog.csdn.net/turkeycock/article/details/50540007