您的位置:首页 > 移动开发

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应用程序启动过程源代码分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息