一 前言
一般像这种源码调用流程会比较长和绕,我们在分析的时候要有一定学习目标(比如这个实现的功能,为什么这样实现),有目标就不会迷失;先熟悉大致流程;抓住关键地方;某些细节的东西需要的时候再去研究。个人觉得既然研究的对象是源码,那么分析的大部分将还是以源码的形式展示较好。要想在哪方面长进,那么就在那里翻滚;要想懂得深渊,那么请先凝视深渊,这样深渊才会凝视你。
学习目标
(1) Launcher组件的认识;
(2) 点击桌面上的图标(Launcher.apk的界面),进入另一个App的流程;
(3) ActivityManagerServices(AMS)、ActivityStack、ActivityThread、Instrumentation等Android framework中重要的基础类的作用及相互间的关系;
(4) AMS和ActivityThread在控制Activity生命周期起到的作用和相互之间的关系;
(5) Activity相关的framework层。
二 图示调用流程
在这部分可以熟悉大致流程。
三 代码具体流程
通过对代码具体流程的分析可以进一步抓住关键地方,并注意某些细节。
相关源码位置
frameworks/base/core/java/android/app/
Activity.java
Instrumentation.java
IActivityManager.java
ActivityManager.java
IApplicationThread.java
ActivityThread.java (内含ApplicationThread)
ClientTransactionHandler.java
util/Singleton.java
servertransaction/ClientTransaction.java
servertransaction\TransactionExecutor.java
servertransaction\ClientTransactionItem.java
servertransaction\LaunchActivityItem.java
frameworks/base/services/core/java/com/android/server/am/
ActivityManagerService.java
ActivityStartController.java
ActivityStarter.java
ActivityStackSupervisor.java
ActivityStack.java
ClientLifecycleManager.java
Activity的启动有两种情况,一种是点击桌面的应用图标启动,另一种是在应用内部启动。不管是从桌面进入应用主界面,还是在应用里进入一个新的Activity,最终都会调用Activity$startActivity方法。我们先分析点击桌面图标启动的这种情况。
当我们点击手机桌面上的图标的时候,App就由Launcher.apk开始启动了,Launcher.apk本质上也是一个应用程序,运行在自己的进程中,我们看到的桌面就是 Launcher 中的一个 Activity。Launcher实现了点击、长按等回调接口,来接收用户的输入。
1 packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
/**
* Default launcher application.
*/
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener {
当监听到Launcher这个Activity上的图标被点击会触发相关函数。
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
return;
}
if (!mWorkspace.isFinishedSwitchingState()) {
return;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
boolean success = startActivitySafely(v, intent, tag);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
} else if (v == mAllAppsButton) {
if (isAllAppsVisible()) {
showWorkspace(true);
} else {
onClickAllAppsButton(v);
}
}
}
接着启动应用图标对应的应用的Activity。
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
看 startActivity(v, intent, tag)。
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
LauncherApps launcherApps = (LauncherApps)
this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (useLaunchAnimation) {
ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
if (user == null || user.equals(android.os.Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, opts.toBundle());
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(),
opts.toBundle());
}
} else {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
startActivity(intent);
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(), null);
}
}
return true;
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity. "
+ "tag="+ tag + " intent=" + intent, e);
}
return false;
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),将Activity组件的启动标志位Intent.FLAG_ACTIVITY_NEW_TASK设置为1,以便它可以在一个新的任务中启动。
2 frameworks\base\core\java\android\app\Activity.java
/**
* Same as {@link #startActivity(Intent, Bundle)} with no options
* specified.
*
* @param intent The intent to start.
*
* @throws android.content.ActivityNotFoundException
*
* @see #startActivity(Intent, Bundle)
* @see #startActivityForResult
*/
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
看this.startActivity(intent, null)。
/**
* Launch a new activity. You will not receive any information about when
* the activity exits. This implementation overrides the base version,
* providing information about
* the activity performing the launch. Because of this additional
* information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
* required; if not specified, the new activity will be added to the
* task of the caller.
*
* <p>This method throws {@link android.content.ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
*
* @param intent The intent to start.
* @param options Additional options for how the Activity should be started.
* See {@link android.content.Context#startActivity(Intent, Bundle)}
* Context.startActivity(Intent, Bundle)} for more details.
*
* @throws android.content.ActivityNotFoundException
*
* @see #startActivity(Intent)
* @see #startActivityForResult
*/
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
先看startActivityForResult(intent, -1),其中第二个参数设置为-1表示Launcher组件不需要知道即将启动的Activity组件的执行结果。
/**
* Same as calling {@link #startActivityForResult(Intent, int, Bundle)}
* with no options.
*
* @param intent The intent to start.
* @param requestCode If >= 0, this code will be returned in
* onActivityResult() when the activity exits.
*
* @throws android.content.ActivityNotFoundException
*
* @see #startActivity
*/
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
发现最后还是会调用到三个参数的startActivityForResult方法。
/**
* Launch an activity for which you would like a result when it finished.
* When this activity exits, your
* onActivityResult() method will be called with the given requestCode.
* Using a negative requestCode is the same as calling
* {@link #startActivity} (the activity is not launched as a sub-activity).
*
* <p>Note that this method should only be used with Intent protocols
* that are defined to return a result. In other protocols (such as
* {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
* not get the result when you expect. For example, if the activity you
* are launching uses {@link Intent#FLAG_ACTIVITY_NEW_TASK}, it will not
* run in your task and thus you will immediately receive a cancel result.
*
* <p>As a special case, if you call startActivityForResult() with a requestCode
* >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your
* activity, then your window will not be displayed until a result is
* returned back from the started activity. This is to avoid visible
* flickering when redirecting to another activity.
*
* <p>This method throws {@link android.content.ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
*
* @param intent The intent to start.
* @param requestCode If >= 0, this code will be returned in
* onActivityResult() when the activity exits.
* @param options Additional options for how the Activity should be started.
* See {@link android.content.Context#startActivity(Intent, Bundle)}
* Context.startActivity(Intent, Bundle)} for more details.
*
* @throws android.content.ActivityNotFoundException
*
* @see #startActivity
*/
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} 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, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options)。
3 frameworks\base\core\java\android\app\Instrumentation.java
Instrumentation,能够允许你监视应用程序和系统的所有交互。每一个应用程序只有一个Instrumen