关于app启动流程,网上有很多分析都很好,我这里就是总结一下,以便于下次看的时候可以更快的理解,如果能顺带帮到其他人,我也很快乐。阅读之前请先对AIDL有个大致的了解,最起码你要知道,有个类叫Stub类,它是Aidl编译生成的,继承于Binder类并实现aidl接口,是远端服务Binder对象的一个中间者,用来和客户端进行交互的。
本篇源码分析基于Android8.0 API 26
关于Android源码查看:
你可以去看这里:https://android.googlesource.com/platform/frameworks/base/ 在线查看,也可以在Android Studio里面关联源码,也可以完全下载自己去编译
前言
其实app启动就是调用了一个startActivity,但是这个startActivity方法并没有我们想像中的那么简单,这其中牵涉到了三个进程:Zygote进程,system_server进程,还有被启动的app进程,其中还牵涉到Binder通信,AIDL,Handler机制等等,如果你把这几位搞明白再去看App启动源码,也许理解的更快一些,假如这几位你不是很理解,也是可以看app启动流程的,没准你会对他们更加感兴趣。
1,桌面图标点击
当我们点击Android系统桌面的应用图标的时候,app被启动,然后进入我们的MainActivity,呈现到用户面前,这就是App被启动了。其实桌面也只不过是一个app,它的名字叫Launcher,只不过是系统把它启动起来的,这里不关心Launcher的启动流程,只关心它里面的图标点击事件。
这里需要去看Launcher的源码,感兴趣的可以去看这里 https://android.googlesource.com/platform/packages/apps/
Launcher中的App列表使用的RecyclerView
//public class AllAppsRecyclerView extends BaseRecyclerView
//public abstract class BaseRecyclerView extends RecyclerView
//AllAppsRecyclerView最终继承自RecyclerView
<com.android.launcher3.allapps.AllAppsRecyclerView
android:id="@+id/apps_list_view"
android:layout_below="@id/search_container_all_apps"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|top"
android:clipToPadding="false"
android:overScrollMode="never"
android:descendantFocusability="afterDescendants"
android:focusable="true" />
这个Activity叫做Launcher.java,既然是RecyclerView,我们很自然的想到了item点击事件,item点击事件其实是对item设置的View.OnClickListener事件,恰巧Launcher实现了这个事件
/**
* Default launcher application.
*/
public class Launcher extends BaseActivity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
AccessibilityManager.AccessibilityStateChangeListener,
WallpaperColorInfo.OnThemeChangeListener {
那么是不是这里就是用来处理RecyclerView应用图标的点击事件呢?答案是yes
给AllAppsRecyclerView设置adapter被封装到了AllAppsContainerView里面,然后AllAppsContainerView里面
mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
AllAppsGridAdapter
/**
* The grid view adapter of all the apps.
*/
public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
...
//AllAppsGridAdapter构造函数传入iconClickListener
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
iconClickListener, View.OnLongClickListener iconLongClickListener) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = LayoutInflater.from(launcher);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
if (FeatureFlags.LAUNCHER3_PHYSICS) {
mSpringAnimationHandler = new SpringAnimationHandler<>(
SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
}
}
...
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
case VIEW_TYPE_PREDICTION_ICON:
//icon点击事件
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setOnFocusChangeListener(mIconFocusListener);
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
return new ViewHolder(icon);
...
}
}
...
}
可见这个点击事件被设置上了,现在我们回去看看Launcher.java这个Activity里面的onClick事件
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
...
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
onClickAppShortcut(v);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if ((v instanceof PageIndicator) ||
(v == mAllAppsButton && mAllAppsButton != null)) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
//执行这里
startAppShortcutOrInfoActivity(v);
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v);
}
}
}
//拿到应用信息,执行startActivitySafely(v, intent, item)
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
Intent intent;
if (item instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
intent = promiseAppInfo.getMarketIntent();
} else {
intent = item.getIntent();
}
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
boolean success = startActivitySafely(v, intent, item);
getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
}
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
...
// 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);
Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
UserHandle user = item == null ? null : item.user;
// Prepare intent
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (v != null) {
intent.setSourceBounds(getViewBounds(v));
}
try {
if (Utilities.ATLEAST_MARSHMALLOW
&& (item instanceof ShortcutInfo)
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
|| item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
&& !((ShortcutInfo) item).isPromise()) {
// Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
} else {
LauncherAppsCompat.getInstance(this).startActivityForProfile(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
}
return true;
} catch (ActivityNotFoundException|SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
}
return false;
}
可以看到最后执行的是startActivity方法,startActivity是Activity的方法,关于app图标点击事件先讲到这。
app启动
从上面桌面的图标点击事件,来到了Activity中的startActivity方法,这里第二个参数 Bundle options我也不知道是空还是非空,但是,没关系,他们最终执行的是startActivityForResult方法
/**
* @param intent The intent to start.
* @param options 给Activity的启动设置一些额外的属性
* See {@link android.content.Context#startActivity(Intent, Bundle)}
* Context.startActivity(Intent, Bundle)} for more details.
*/
@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);
}
}
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);
}
}
}
startActivityForResult方法先判断if (mParent == null),这里的mParent是指的Activity的父Activity,一般的Activity中mParent都是空的。所以执行的是往下执行的是execStartActivity方法,注意到这里execStartActivity的第二个参数传入的是mMainThread.getApplicationThread(),mMainThread是ActivityThread,mMainThread.getApplicationThread()获取的是ApplicationThread,这两个东西都很重要,后面回讲到。继续往下看
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
result = am.onStartActivity(intent);
}
if (result != null) {
am.mHits++;
return result;
} else if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
你如果在网上看以前别人的源码ActivityManager.getService().startActivity这个位置是ActivityManagerNative.getDefault().startActivity,在Android API26以后源码发生了改变,不使用以前的那种形式了。不过不影响,原理都是相通的。ActivityManager.getService()拿到的是IActivityManager,IActivityManager是一个aidl文件,现在用到aidl了。ActivityManagerService圈内简称AMS,是一个很重要的系统服务类,存在于system_server进程,ActivityManagerService继承自IActivityManager.Stub,所以上面ActivityManager.getService().startActivity调用的其实是ActivityManagerService里面的方法
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
根据参数对号入座
@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());
}
@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,
"startActivityAsUser");
}
下面就是各种调用,我翻过一道道山川,却迷失在你的芦苇荡。这里面有非常多的调用,注意不要跟丢
ActivityStarter.java
-->startActivityMayWait
-->startActivityLocked
-->startActivity
-->startActivity
-->startActivityUnchecked
ActivityStackSupervisor.java
-->resumeFocusedStackTopActivityLocked
ActivityStack.java
-->resumeTopActivityUncheckedLocked
-->resumeTopActivityInnerLocked
ActivityStackSupervisor.java
-->startSpecificActivityLocked
在上面的ActivityStarter的startActivityUnchecked方法里面做了大量的工作,包括获取activity的启动模式,actvity 的栈管理,后面启动Activity的时候,假如activity所在的app启动那么可以直接启动它,执行ApplicationThread里面的scheduleLaunchActivity方法调用activity的生命周期来启动Activity,假如这个app进程还未启动,在这个方法里面先把activity放入栈中,记录一下,然后去启动app进程,等会app进程起来的时候,回去查看栈并且启动这里记录的activity。
下面看一下startSpecificActivityLocked这个方法,ProcessRecord app = mService.getProcessRecordLocked(r.processName,r.info.applicationInfo.uid, true);拿到这个app进程纪录。先判断这个进程是不是已经启动过,假如启动过执行realStartActivityLocked,假如没有启动过,执行 mService.startProcessLocked启动新的进程。
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.getStack().setLaunchTime(r);
if (app != null && app.thread != null) {
//假如app进程已经在运行,直接去启动
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
//否则开进程启动
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
由于代码太多,关于app应用启动流程分为两篇,第一篇先到这里,下一篇,接着这一篇,分开讲讲
- 进程已经启动过,并且还存在没被杀死,那么去打开Activity.
- 进程不存在,启动新的进程。