Android APP启动关键流程分析
2017-05-29 18:56
645 查看
前言
本文简要分析一下Android APP启动关键流程,APP从被点击到启动中间流程很多,可以参考老罗的《Android应用程序启动过程源代码分析》。APP的启动的最终结果是将我们注册的launcher Activity启动起来,在我们点击桌面那一刻,就围绕这件事情不断做处理。1、Launcher做了哪些事
Launcher本质是一个独立的APP,有自己的Activity,名为Launcher;有自己的页面容器,名为AllApps2D的容器;有自己的Application;名为 LauncherApplication; 在Launcher上面可以看到手机安装的APP,并且可以删除、拖动APP,每个APP Icon都是一个独立的View显示。(1)Launcher内部存有手机内APP安装信息
在Launcher启动时会注册LauncherModel,当手机启动完毕,会通过loadAllAppsByBatch()方法获取获取安装APP的信息和APP分组信息ItemInfo。temInfo内部存有APP的名称、图标以及APP自己的Launcher Activity信息。LauncherModel ——>loadAllAppsByBatch()
这个方法干了一件事情,就是通过PackageManager获取所有APP的Launcher Activity信息。
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); final PackageManager packageManager = mContext.getPackageManager(); List<ResolveInfo> apps = null; …………………………………… while (i < N && !mStopped) { if (i == 0) { mAllAppsList.clear(); final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; apps = packageManager.queryIntentActivities(mainIntent, 0); …………………………………… //在packageManager里面取CATEGORY_LAUNCHER } } …………………………………… final ArrayList<ApplicationInfo> added = mAllAppsList.added; mAllAppsList.added = new ArrayList<ApplicationInfo>(); mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (first) { callbacks.bindAllApplications(added); //将apps信息给Launcher activity,进行显示 } else { callbacks.bindAppsAdded(added); } if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - t) + "ms"); } } else { Log.i(TAG, "not binding apps: no Launcher activity"); } } });
(2)Launcher相应点击事件
Launcher 根据APP的信息,将APP图标显示在桌面上,当点击APP图标时,开始启动对应的Activity。看代码可以知道,启动信息存储在图标(View)的tag上…… 大家可以考虑有没有更好的处理方法,启动的Activity为什么标识:Intent.FLAG_ACTIVITY_NEW_TASK?这一点在我们平常启动Activity时应该考虑。public void onClick(View v) { 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())); startActivitySafely(intent, tag); } else if (tag instanceof FolderInfo) { handleFolderClick((FolderInfo) tag); } else if (v == mHandleView) { if (isAllAppsVisible()) { closeAllApps(true); } else { showAllApps(true); } } } void startActivitySafely(Intent intent, Object tag) { //启动APP应该自一个新的Task中启动,所以必须加Intent.FLAG_ACTIVITY_NEW_TASK标识 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { startActivity(intent); } 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); } 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); } }
Instrumentation与ActivityManagerProxy
无论是Launcher还是其他需要启动Activity的地方,都必须通过Instrumentation,Instrumentation并没有干实事,而是将这一启动行为传递给ActivityManagerProxy,ActivityManagerProxy内部也没干什么实事,它的关键在于将启动信息通过IPC通信,传递给了AMS。Instrumentation.execStartActivity ——> ActivityManagerProxy.startActivity
public class Instrumentation { 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); if (am.match(who, null, intent)) { am.mHits++; if (am.isBlocking()) { return requestCode >= 0 ? am.getResult() : null; } break; } } } } try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .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; } }
ActivityManagerProxy.startActivity,内部持有IBinder对象,通过赋值内存的方式,将启动信息传递给AMS,IBinder对象怎么获取呢,可以参考这个类的源码:IBinder b = ServiceManager.getService(“activity”);我们也可以自己尝试一下不用Instrumentation这种默认启动方法,自定义启动Activity方式……
class ActivityManagerProxy implements IActivityManager { public ActivityManagerProxy(IBinder remote) { mRemote = remote; } public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); data.writeString(callingPackage); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo); data.writeString(resultWho); data.writeInt(requestCode); data.writeInt(startFlags); if (profilerInfo != null) { data.writeInt(1); profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { data.writeInt(0); } if (options != null) { data.writeInt(1); options.writeToParcel(data, 0); } else { data.writeInt(0); } mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); reply.readException(); int result = reply.readInt(); reply.recycle(); data.recycle(); return result; } }
AMS内部如何处理
AMS内部处理过程非常复杂,这里先省略,以后补充。从流程的角度看AMS:1、区别启动APP的动作和启动普通Activity的动作;2、如果ActivityRecord中已经有了这个Activity并且有自己独立的Task,可以将APP 的Activity显示到前台;3、如果没有先启动fork一个新的进程,创建ActivityThread,然后通过ActivityThread启动Launcher Activity。启动ActivityThread
最关键的静态main方法启动主线程 loop,创建新的ActivityThread。
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); ……………… Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
将ThreadActivity赋值给sCurrentActivityThread。
private void attach(boolean system) { sCurrentActivityThread = this; mSystemThread = system; if (!system) { ViewRootImpl.addFirstDrawHandler(new Runnable() { @Override public void run() { ensureJitEnabled(); } }); android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", UserHandle.myUserId()); //mAppThread是ApplicationThread对象,将mAppThread传递个AMS用于,用于以后AMS与ActivityThread通信。 RuntimeInit.setApplicationObject(mAppThread.asBinder()); final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { // Ignore } // Watch for getting close to heap limit. BinderInternal.addGcWatcher(new Runnable() { @Override public void run() { if (!mSomeActivitiesChanged) { return; } Runtime runtime = Runtime.getRuntime(); long dalvikMax = runtime.maxMemory(); long dalvikUsed = runtime.totalMemory() - runtime.freeMemory(); if (dalvikUsed > ((3*dalvikMax)/4)) { if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024) + " total=" + (runtime.totalMemory()/1024) + " used=" + (dalvikUsed/1024)); mSomeActivitiesChanged = false; try { mgr.releaseSomeActivities(mAppThread); } catch (RemoteException e) { } } } }); } else { // Don't set application object here -- if the system crashes, // we can't display an alert, we just want to die die die. android.ddm.DdmHandleAppName.setAppName("system_process", UserHandle.myUserId()); try { mInstrumentation = new Instrumentation(); ContextImpl context = ContextImpl.createAppContext( this, getSystemContext().mPackageInfo); mInitialApplication = context.mPackageInfo.makeApplication(true, null); mInitialApplication.onCreate(); } catch (Exception e) { throw new RuntimeException( "Unable to instantiate Application():" + e.toString(), e); } } …………………… }
Application何时被创建的?
有一点可以肯定的是Application创建的时间应该在ActivityThread创建与MainActivity创建之间创建的。分析源码可以看到有两个地方可以创建Application:(1)ApplicationThread的bindApplication方法,通过BIND_APPLICATION消息,告诉H(handler)创建Application,最后调用ActivityThread的handleBindApplication方法,通过loadapk类反射创建Application,然后通过instrument调用application的OnCreate方法。
在handleBindApplication做了很多事情,参考[2]:初始化mConfiguratio、设置进程名、本地语言设置、设置包名称、设置应用程序根路径、设置应用程序data路径、设置activity的context。
private void handleBindApplication(AppBindData data) { ……………………………… try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. //创建Application,如果loadedapk对象已经有Application,直接返回 Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (!data.restrictedBackupMode) { List<ProviderInfo> providers = data.providers; if (providers != null) { installContentProviders(app, providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } // Do this after providers, since instrumentation tests generally start their // test thread at this point, and we don't want that racing. try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ": " + e.toString(), e); } try { //调用Application的onCreate方法 mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { if (!mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } } finally { StrictMode.setThreadPolicy(savedPolicy); } }
(2)ActivityThread.performLaunchActivity方法,当创建Activity时,如果Application还没有创建,则创建新的Application。其实当启动MainActivity时肯定会走这步流程,但是loadedapk如果已经有Application,则直接返回,并且在performLaunchActivity并没有调用Application的onCreate方法,所以可以认为这步不其实并不是真正启动一个Application。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { …………………… try { //packageInfo就是loadedapk对象,调用loadedapk创建Application,如果loadedapk内部有Application对象,直接返回 //需要注意的是,这里并没有调用Application的onCreate方法! Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; if (!r.activity.mFinished) { activity.performStart(); r.stopped = false; } if (!r.activity.mFinished) { if (r.isPersistable()) { if (r.state != null || r.persistentState != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnPostCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()"); } } } r.paused = true; mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }
(3)到底在哪启动的?Crash堆栈给了我们答案
首先,在Application的构造函数里添加NPE代码,启动之后,crash堆栈log如下,显然,是在handleBindApplication中创建的Application:
at android.app.LoadedApk.makeApplication(LoadedApk.java:580) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4716) at android.app.ActivityThread.access$1600(ActivityThread.java:153) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1421) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5459) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
然后,把构造函数的NPE移到OnCreate方法中去,crash log如下:
也是handleBindApplication执行的OnCreate方法。
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4746) at android.app.ActivityThread.access$1600(ActivityThread.java:153) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1421) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5459) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
很显然,在启动一个APP时,Application的创建时机位于ActivityThread和MainActivity创建之间,通过ActivityThread的handleBindApplication方法完成的。但是performLaunchActivity为什么也可以创建Application呢?并且不调用它的OnCreate方法:我怀疑可能是当其他APP启动它的某个暴露在外的Activity时,这时这个Application还没有创建,但是Activity也要启动,会默认创建一个Application,但是只是有了Application对象而已,这点猜测以后还需证明。
未完待续……
参考资料
[1]Android FrameWork——Activity启动过程详解[2]Android应用程序启动过程源代码分析
相关文章推荐
- [Android]从Launcher开始启动App流程源码分析
- Android App启动流程分析
- Android Launcher分析和修改9——Launcher启动APP流程
- Android APP启动方式、启动流程及启动优化分析
- [Android]从Launcher开始启动App流程源码分析
- [android2.3]GPS启动流程及数据流向分析
- Android的APP启动过程分析
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- [android源码分析]bluetoothd service的启动的总体流程分析
- AppWidget启动流程部分 Launcher分析
- Android的应用(APP)启动详细流程
- Android Framework启动流程分析
- 【Android】GPS启动流程及数据流向分析(基于2.3.5)
- Android app启动一个新进程流程
- android系统启动流程启动画面学习之init和init.rc分析
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- Android 框架研究:(一)框架概要 —— 1.3 启动流程的分析
- Android 框架研究:(一)框架概要 —— 1.3 启动流程的分析
- [android2.3]GPS启动流程及数据流向分析
- Android中ICS4.0源码Launcher启动流程分析