本篇源码分析基于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. */@Overridepublic 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 {
根据参数对号入座
@Overridepublic 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()); }@Overridepublic 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-->startActivityUncheckedActivityStackSupervisor.java -->resumeFocusedStackTopActivityLockedActivityStack.java -->resumeTopActivityUncheckedLocked-->resumeTopActivityInnerLockedActivityStackSupervisor.java -->startSpecificActivityLocked
下面看一下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.
进程不存在,启动新的进程。