Android APP启动关键流程分析

前言

本文简要分析一下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应用程序启动过程源代码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值