您的位置:首页 > 移动开发

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

2016-01-19 00:35 495 查看
网上关于在Launcher上第一次启动app的过程分析已经很多了,今天吃饭的时候在思考一个问题:我在Launcher上打开淘宝,切换到购物车,按Home键回到Launcher,再次点开淘宝的时候仍然是购物车页面。按说Launcher每次点开app都是启动同一个activity(ACTION_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_TASK的flag,因此进到这个条件中。

传进来的inTask和r.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。

如果没有打开AMS的log,从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就是Launcher,curTop.task肯定不等于intentActivity.task,进入这个条件里。

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

再往下就是一马平川了,一路往下看一直到targetStack.resumeTopActivityLocked()。所以我们看到的现象就是:Launcher请求启动一个叫Welcome的activity,最终执行的操作是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。

这个String的intern()方法又是个知识点,目的是节省空间:

调用s.intern()方法的时候,会将共享池中的字符串与外部的字符串(s)进行比较,如果共享池中有与之相等的字符串,则不会将外部的字符串放到共享池中的,返回的只是共享池中的字符串,如果不同则将外部字符串放入共享池中,并返回其字符串的句柄(引用)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: