以点击桌面图标启动为例;
前提知识:
桌面也是activity,即Launcher继承Activity
APP进程
Launcher源码摘取分析:
public void onClick(View v) {
...
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
//点击的view必须设置tag为 ShortcutInfo类型,调用
onClickAppShortcut(v);
} else if (tag instanceof FolderInfo) {
//点击文件夹
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
(v == mAllAppsButton && mAllAppsButton != null)) {
//点击进入grid列表式,老版手机的菜单界面
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
//通过点击事件调用, 点击的view必须设置tag为AppInfo
startAppShortcutOrInfoActivity(v);
} else if (tag instanceof LauncherAppWidgetInfo) {
//点击窗口小部件,时钟日历等
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v);
}
}
}
//其他点击不分析
protected void onClickAppShortcut(final View v) {
...
startAppShortcutOrInfoActivity(v);
}
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
Intent intent = item.getIntent();
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
//启动应用最终都会调到该方法
boolean success = startActivitySafely(v, intent, item);
}
*通过参数分析功能:多了intent 和 ItemInfo参数,证明Launch界面的方法的作用是取到了点击view图标对应的应用的信息数据,传递给AMS操作(可以想象的到,自己写一些简单的模拟launch的时候是这样干的)
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mApps = getPackageManager().queryIntentActivities(mainIntent, 0);//获取全部应用,具体分析PMS时候在研究
ResolveInfo info = mApps.get(position);
String pkg = info.activityInfo.packageName;//包名
String cls = info.activityInfo.name;//应用launch界面名
ComponentName componet = new ComponentName(pkg, cls);//包名和类名拼接处理的封装类
Intent i = new Intent();
i.setComponent(componet);
startActivity(i);//启动对应包类名信息的(主)界面
*
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
//切换动画
boolean useLaunchAnimation = (v != null) && !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
UserHandle user = item == null ? null : item.user;
判断...
//在新栈内启动
/**
* 为什么要在新建的栈内启动?因为当前的launcher界面是系统桌面,AS堆信息id为0,栈信息id为,小于1000,在ASS管理中应属于mHome中
* 而新启动的界面是应用界面,应该放到非系统管理集合中
* Stack #0: Task id #1
*/
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//调用activity的startActivity()
startActivity(intent, optsBundle);
}
*通过参数分析功能:少了item,多了optsBundle。item在方法中做了判断,判断是否是快捷入口然后进行一些其他的判断(不懂,暂时跳过把),bundle就是动画id等数据
额外补充方法说明:
//1.optsBundle为啥是界面切换动画
@TargetApi(Build.VERSION_CODES.M)
public Bundle getActivityLaunchOptions(View v) {
//版本判断
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
if (v instanceof TextView) {
Drawable icon = Workspace.getTextViewIcon((TextView) v);
if (icon != null) {
Rect bounds = icon.getBounds();
left = (width - bounds.width()) / 2;
top = v.getPaddingTop();
width = bounds.width();
height = bounds.height();
}
}
return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height).toBundle(); //切剪动画啥的
} else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
return ActivityOptions.makeCustomAnimation(
this, R.anim.task_open_enter, R.anim.no_anim).toBundle(); //进出动画
//ActivityOptions和bundle没有关系,注意有个.toBundle方法。。。。。
}
return null;
}
public static ActivityOptions makeClipRevealAnimation(View source,
int startX, int startY, int width, int height) {
ActivityOptions opts = new ActivityOptions();
opts.mAnimationType = ANIM_CLIP_REVEAL;
int[] pts = new int[2];
source.getLocationOnScreen(pts);
opts.mStartX = pts[0] + startX;
opts.mStartY = pts[1] + startY;
opts.mWidth = width;
opts.mHeight = height;
return opts;
}
public static ActivityOptions makeCustomAnimation(Context context,
int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = context.getPackageName();
opts.mAnimationType = ANIM_CUSTOM;
opts.mCustomEnterResId = enterResId;
opts.mCustomExitResId = exitResId;
opts.setOnAnimationStartedListener(handler, listener);
return opts;
}
//最终把包括切换动画的资源id在内的信息封装进了Bundle
//A mapping from String keys to various {@link Parcelable} values.
//Bundle是一个序列化的map封装工具类,提供了一些方便使用的方法,如put,get等
//mMap = capacity > 0 ? new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>(); BaseBundle构造时创建
*
Activity源码摘取分析:
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
//重载最终调用到
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
// Activity mParent; attach()方法中进行的赋值方法
/**
*
* attach()在ActivityThread的方法performLaunchActivity方法中被调用,该方法是启动新的activity的必经方法,确切的说是启动oncreate的方法,
* 而mParent = r.parent,r是ams机制中的activityRecord在应用层的实体
*/
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
//执行启动
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(this,
//重要参数, Binder (ApplicationThread extends IApplicationThread.Stub), 用作ams进程和应用进程之间通讯
mMainThread.getApplicationThread(),
//重要参数, Binder attach()方法中进行的赋值(界面创建时由AMS创建token,作为界面的标识,传给客户端进程,用于ams和指定的该activity交互的标识依据)
mToken,
this,
intent,
requestCode,
options);
if (ar != null) {
mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
}
if (requestCode >= 0) {
//带返回
mStartedActivity = true;
}
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
*通过参数分析功能:
mInstrumentation.execStartActivity(this, context
mMainThread.getApplicationThread(), ams和app之间通讯的binder mMainThread,在attach中赋值
mToken, ams和app和wms判定同一个界面的标识 mToken,在attach中赋值
this, target
intent, 要启动的界面的信息封装(包名,主界面class名,flag等)
requestCode, 是否要带回数据的请求标识code(startActivityForResult, setResult)
options 切换动画等资源id封装
);
带着当前界面的信息,要启动的界面的信息,进行启动请求*
Instrumentation源码摘取分析:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
...
int result = ActivityManager.getService() //binder通讯,代理ams调用接口
.startActivity(
whoThread,
who.getBasePackageName(),
intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token,
target,
requestCode,
0,
null,
options);
}
*通过参数分析功能:
int result = ActivityManager.getService()
.startActivity(
whoThread, ams和app之间通讯的binde
who.getBasePackageName(), 上下文获取包名
intent, 数据
intent.resolveTypeIfNeeded(who.getContentResolver()), 解析intent数据使用,可能是解码方式,注解提示不重要
token, 标识(当前launch界面)
target, target
requestCode, 请求code
0, startFlags本以为是启动方式,但是后面源码显示明显不是
null, 分解器?
options); bundle
可以猜想:
ams首先通过当前的标识token,从保存的AR里面去判断当前的界面存在不存在,是系统的还是应用的,是什么生命周期下的等等
然后ams,解析intent,获取到要启动的界面的包名,类名等信息,用来创建一个AR与之对应,同事AR存入TR,TR存入AS,AS存入ASS(中间涉及WMS和堆栈管理大致了解就行吧)
最后ams的管理工作完成,还要通知当前界面继续你的启动任务,就使用IApplicationThread这个binder通讯 *
AMS进程
通过binder通讯,调到ActivityManagerService
ActivityManagerService源码摘取分析:
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());
}
*通过参数分析功能:只多了一个UserHandle,这个在前面launch的地方也是起到了判断的作用
UserHandle user = item == null ? null : item.user;判断….
注解说:在设备上的用户表示。在ums的啥时候再说吧,目前他就是一个系统原来判定是不是同一个使用者的工具*
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);
//后加的api ActivityStarter
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false, userId, null, null,
"startActivityAsUser");
}
ActivityStarter源码摘取分析:(从4.2之前的AMS和AS直接调用,改为添加了一个starter,就是应为4.2以后,安卓系统支持了多用户功能,同时也新增了UMS机制,这也就是上面那个UserHandle的原理吧,不懂….)
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 globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask, String reason) {
//保存一个原封不动的intent
final Intent ephemeralIntent = new Intent(intent);
//解析
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
....
// 解析intent信息
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
//封装动画资源
ActivityOptions options = ActivityOptions.fromBundle(bOptions);
//native的方法,取pid和uid信息
final int realCallingPid = Binder.getCallingPid();
final int realCallingUid = Binder.getCallingUid();
int callingPid;
if (callingUid >= 0) {
callingPid = -1;
} else if (caller == null) {
callingPid = realCallingPid;
callingUid = realCallingUid;
} else {
callingPid = callingUid = -1;
}
//新启动界面,堆信息为当前交互的堆
final ActivityStack stack;
if (container == null || container.mStack.isOnHomeDisplay()) {
stack = mSupervisor.mFocusedStack;
} else...
//创建AR数组
final ActivityRecord[] outRecord = new ActivityRecord[1];
int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor,
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, outRecord, container,
inTask, reason);
}
int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, Strin