以下内容为原创,欢迎转载,转载请注明
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html
从Launcher开始启动App流程
com.android.launcher.Launcher
就是我们的Launcher页面了,可以看到Launcher其实也是一个Activity
:
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener { // ...}
既然是Activity
,那当然也会有onCreate
、onResume
等生命周期了,按照逻辑,应该会去加载所有App,以网格的布局显示在页面上,果然,在onResume
看到了这个方法:
@Overrideprotected void onResume() { super.onResume(); if (mRestoring) { startLoaders(); } // ...}
看方法名就可以猜到这个方法就是用来加载所有App信息的,进入这个方法:
private void startLoaders() { boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged); sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications); mRestoring = false;}
这里调用sModel
(LauncherModel
类型)的loadUserItems
方法去加载数据了,sModel
明显属于Model
层,进入loadUserItems
方法:
void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged, boolean loadApplications) { // ... mDesktopItemsLoaded = false; mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications, isLaunching); mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader"); mDesktopLoaderThread.start(); // ...}
然后使用DesktopItemsLoader
在mDesktopLoaderThread
线程中加载,:
private class DesktopItemsLoader implements Runnable { // ... public void run() { // ... final Cursor c = contentResolver.query(LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); // ... final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE); final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID); final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); // ... // 通过launcher回调返回数据 launcher.onDesktopItemsLoaded(uiDesktopItems, uiDesktopWidgets); // ... } // ...}
然后我们回到Launcher
的onDesktopItemsLoaded
方法:
void onDesktopItemsLoaded(ArrayList<ItemInfo> shortcuts, ArrayList<LauncherAppWidgetInfo> appWidgets) { // ... bindDesktopItems(shortcuts, appWidgets);}
继续进入bindDesktopItems
方法:
private void bindDesktopItems(ArrayList<ItemInfo> shortcuts, ArrayList<LauncherAppWidgetInfo> appWidgets) { // ... mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter); mBinder.startBindingItems();}
进入startBindingItems
方法:
public void startBindingItems() { // ... obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget();}
这里使用了Handler
发送消息,进入Handler
的handleMessage
方法:
@Overridepublic void handleMessage(Message msg) { // ... switch (msg.what) { // ... case MESSAGE_BIND_ITEMS: { launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2); break; } // ... }}
接收到消息之后调用bindItems
方法:
private void bindItems(Launcher.DesktopBinder binder, ArrayList<ItemInfo> shortcuts, int start, int count) { // ... case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: final View shortcut = createShortcut((ApplicationInfo) item); workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1, !desktopLocked); // ...}
这里我们只考虑app或者app快捷方式的情况,文件夹和widgets暂时不考虑。app或者app快捷方式实质上都是进入了这个逻辑中,调用createShortcut
方法:
// 重载方法,最终都会调用这个方法View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) { // ... TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false); // ... favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null); favorite.setText(info.title); favorite.setTag(info); favorite.setOnClickListener(this); // ...}
这里首先inflater出item的布局,然后设置text
和OnClickListener
,还有tag,这个tag是ApplicationInfo
,里面包含了各种App信息,是从App的AndroidManifest.xml
的<application>
标签中解析出来的。既然设置了点击事件,显然,点击后应该会打开对应的App才对。所以继续看onClick
方法:
public void onClick(View v) { Object tag = v.getTag(); if (tag instanceof ApplicationInfo) { // Open shortcut final Intent intent = ((ApplicationInfo) tag).intent; startActivitySafely(intent); } else if (tag instanceof FolderInfo) { handleFolderClick((FolderInfo) tag); }}
点击App就会通过startActivitySafely
方法使用刚才设置的tag,也就是ApplicationInfo
中的intent进行跳转:
void startActivitySafely(Intent intent) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // ... startActivity(intent); // ...}
然后我们来看看打开某个app的时候整个流程是怎么走的。接着上面的的startActivity()
方法走:
public void startActivity(Intent intent, @Nullable Bundle options) { // ... if (options != null) { startActivityForResult(intent, -1, options); } else { startActivityForResult(intent, -1); } // ...}
可以看到,不管你是调用了startActivity
还是startActivityForResult
方法,startActivityForResult
方法,并且如果是调用的startActivity
,则默认requestCode
就是-1,所以如果你想调用startActivityForResult
的时候,注意不能把requestCode
设置为-1,否则它的效果就跟startActivity
一样了,不会再回调onActivityResult
!,再看看startActivityForResult
的实现:
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); // ...}
可以看到,Activity内部是使用mInstrumentation
(Instrumentation
类型)执行execStartActivity
方法来实现Activity
跳转的,执行完毕后会返回一个Instrumentation.ActivityResult
。
然后查看Instrumentation::execStartActivity
:
public ActivityResult execStartActivity( // ... int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); // ...}
首先通过ActivityManagerNative.getDefault()
获得一个IActivityManager
的实现类:
static public IActivityManager getDefault() { return gDefault.get();}private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { // 通过Binder IPC获取ActivityManager(IBinder) IBinder b = ServiceManager.getService("activity"); // ... IActivityManager am = asInterface(b); // ... return am; }};static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ActivityManagerProxy(obj);}
先通过Binder IPC的方式从服务端获取一个Activity Manager
,然后通过ActivityManagernative
封装成一个代理ActivityManagerProxy
对象,然后调用startActivity
也是使用了Binder IPC进行与服务器端的通信,(整个Android系统的通信机制使用了大量的Binder IPC,这个以后再专门讨论这个吧),接着,我们进入到了com.android.server.am.ActivityManagerService
的startActivity
方法:
@Overridepublic final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options) { return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, options, UserHandle.getCallingUserId());}
接下来的调用链:
-> startActivityAsUser
-> startActivityMayWait
-> startActivityLocked
-> startActivityUncheckedLocked
-> targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options)
-> mStackSupervisor.resumeTopActivitiesLocked(this, r, options)
-> mStackSupervisor.startSpecificActivityLocked(next, true, true)
startSpecificActivityLocked
方法如下:
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); // ... if (app != null && app.thread != null) { // ... realStartActivityLocked(r, app, andResume, checkConfig); return; } // ... mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true);}
首先从mService
找出对应需要启动Activity的进程(通过进程名字和uid,进程名字可以在AndroidManifest.xml
中配置)如果可以获取到,说明这个Activity所属的进程已经存在了,也就是说app已经在运行了,那就会调用realStartActivityLocked
,否则,如果该Activity所在的App是第一次启动,则会调用mService.startProcessLocked
方法来创建一个进程:
final ProcessRecord startProcessLocked(/*...*/){ // ... startProcessLocked(app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs); // ...}private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { // ... // entryPoint表示一个类,它用来作为新创建的进程的主入口,会调用这个类的静态main方法,这个参数在startProcessLocked方法中会被检查重置,如果是null的话,就默认是android.app.ActivityThread。 if (entryPoint == null) entryPoint = "android.app.ActivityThread"; // ... Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs); // ... }
再看Process::start
的实现:
public static final ProcessStartResult start(/*...*/){ // ... return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, zygoteArgs); // ...}
接下来就是通过Zygote
进程fork
一个新的进程作为app的进程。这里要需要讲的一个参数是processClass
,这个参数表示一个类,它用来作为新创建的进程的主入口,会调用这个类的静态main
方法,这个参数在startProcessLocked
方法中会被检查重置,如果是null的话,就默认是android.app.ActivityThread
。
现在App的进程也创建成功了,就会进入android.app.ActivityThread
的静态的main
中:
public static void main(String[] args) { // ... // 初始化主线程Looper Looper.prepareMainLooper(); // ... ActivityThread thread = new ActivityThread(); thread.attach(false); // ... // 启动消息循环 Looper.loop() // ...}
然后创建了一个ActivityThread
,说明每当一个新的app进程被创建,都会对应一个新的ActivityThread
实例,然后调用它的attach
方法:
private void attach(boolean system) { // ... if (!system) { // ... final IActivityManager mgr = ActivityManagerNative.getDefault(); // ... mgr.attachApplication(mAppThread); // ... }else{ // ... } // ...}
然后再次通过Binder IPC调用ActivityManagerProxy
的attachApplication
,传入的ApplicationThread
(Binder)参数用于在服务端进行回调通信。最后进入ActivityManagerService::attachApplication
,再调用attachApplicationLocked(thread, callingPid)
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { // ... app = mPidsSelfLocked.get(pid); // ... app.makeActive(thread, mProcessStats); app.curAdj = app.setAdj= -100; app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT; app.forcingToForeground = null; updateProcessForegroundLocked(app, false, false); app.hasShownUi = false; app.debugging = false; app.cached = false; app.killedByAm = false; // ... thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked()); // ... if (normalMode) { try { if (mStackSupervisor.attachApplicationLocked(app)) { didSomething = true; } } catch (Exception e) { Slog.wtf(TAG, "Exception thrown launching activities in " + app, e); badApp = true; } } // ...}
首先,通过pid获取刚刚创建的进程,然后对app进行一些初始化工作,然后调用bindApplication
远程调用客户端ActivityThread::bindApplication
,再通过Handler
调用到ActivityThread::handleBindApplication
方法:
private void handleBindApplication(AppBindData data) { // ... final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); if (data.instrumentationName != null) { // ... mInstrumentationPackageName = ii.packageName; mInstrumentationAppDir = ii.sourceDir; mInstrumentationSplitAppDirs = ii.splitSourceDirs; mInstrumentationLibDir = ii.nativeLibraryDir; mInstrumentedAppDir = data.info.getAppDir(); mInstrumentedSplitAppDirs = data.info.getSplitAppDirs(); mInstrumentedLibDir = data.info.getLibDir(); ApplicationInfo instrApp = new ApplicationInfo(); instrApp.packageName = ii.packageName; instrApp.sourceDir = ii.sourceDir; instrApp.publicSourceDir = ii.publicSourceDir; instrApp.splitSourceDirs = ii.splitSourceDirs; instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs; instrApp.dataDir = ii.dataDir; instrApp.nativeLibraryDir = ii.nativeLibraryDir; LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, appContext.getClassLoader(), false, true, false); ContextImpl instrContext = ContextImpl.createAppContext(this, pi); // ...java.lang.ClassLoader cl = instrContext.getClassLoader(); mInstrumentation = (Instrumentation) cl.loadClass(data.instrumentationName.getClassName()).newInstance(); mInstrumentation.init(this, instrContext, appContext, new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher, data.instrumentationUiAutomationConnection); // ... }else{ mInstrumentation = new Instrumentation(); } // ... Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // ... mInstrumentation.callApplicationOnCreate(app); // ...}
首先,创建一个当前App的Context
,然后如果data.instrumentationName != null
,则初始化Instrumentation
相关的变量,并创建Instrumentation
的ApplicationInfo
等对象来创建Instrumentation
的Context
,然后创建Instrumentation
对象,并调用它的init
方法进行初始化。如果data.instrumentationName == null
,则new一个Instrumentation
(在一个进程中只会有一个Instrumentation
实例)然后创建Application
对象,并调用它的onCreate
方法,这样Application
就会被回调了。
然后我们回到ActivityManagerService::attachApplicationLocked
方法,远程执行完thread.bindApplication
方法之后,接下来会调用mStackSupervisor.attachApplicationLocked(app)
方法:
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException { // ... ActivityRecord hr = stack.topRunningActivityLocked(null); // ... realStartActivityLocked(hr, app, true, true) // ...}
先通过topRunningActivityLocked
从堆栈顶端获取要启动的Activity,然后realStartActivityLocked(hr, app, true, true)
:
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { // ... app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); // ...}
继续通过Binder IPC远程调用scheduleLaunchActivity
方法,然后进入ActivityThread
的scheduleLaunchActivity
方法中,然后通过Handler
进入handleLaunchActivity
方法:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent){ // ... Activity a = performLaunchActivity(r, customIntent); handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); // ...}
先调用performLaunchActivity
方法返回一个Activity
,然后调用handleResumeActivity
方法让该Activity
进入onResume
状态。所以很显然在performLaunchActivity
中肯定是生成了Activity
实例,并调用了onCreate
方法了,来看下代码:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // ... ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } // ... java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); // ... Context appContext = createBaseContextForActivity(r, activity); // ... 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 (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } // ... mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); // ... mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState); // ...}
首先,初始化LoadedApk
,然后通过Instrumentation
来创建一个Activity
实例,通过createBaseContextForActivity
方法创建一个Activity Context
,调用activity
的attach
方法,然后依次触发该Activity
的onCreate
、onRestoreInstanceState
、onPostCreate
等生命周期方法。
createBaseContextForActivity
方法如下:
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { // ... ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, displayId, r.overrideConfig); appContext.setOuterContext(activity); Context baseContext = appContext; // ...}
通过ContextImpl::createActivityContext
创建的Context
对象,可以发现,不论是System Context/App Context/Activity Context
,这些Context
都是通过ContextImpl
生成的,具体这里再挖个坑先。
再继续进入Activity::attach
方法:
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor) { // ... mMainThread = aThread; mInstrumentation = instr; mToken = token; mIdent = ident; mApplication = application; mIntent = intent; mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; mParent = parent; mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; // ...}
上面对Activity
与ActivityThread
、Instrumentation
等进行了绑定,所以说每个Activity
都含有一个ActivityThread
引用和一个Instrumentation
引用,而ActivityThread
实例和Instrumentation
实例在一个进程中都只有一个实例,因为ActivityThread
是在进程被创建成功后,进入ActivityThread
的static main()
时才会被创建,而Instrumentation
则是在ActivityThread
被创建后进行attach
的之后被创建。
- 2楼Bello
- 同上问。。。
- 1楼KillU
- 高手,请问你看这个源代码用的是什么工具?,有什么好的方法学习到您这种程度?
- Re: 天天_byconan
- @KillU,clone Android源码https://android.googlesource.com/,然后用android studio看。