Android面试Android进阶(十三)-APP启动流程

问:简单描述一下APP启动流程与Activity的启动流程

答:APP的启动是从用户点击桌面的icon开始的。Android的桌面应用叫Launcher,可以把桌面也看作是Android系统的一个应用,桌面只是这个应用的其中一个页面而已。Android中每个应用就是一个进程,那一个应用启动另一个应用并通信,肯定会有进程间通信,也就是IPC通信,在Android中的IPC大部分都是通过Binder实现的。这样分析的话,APP启动流程其实就是Activity的启动流程,只不过中间会有判断要启动Activity所在的进程是否存在,如果不存在则先通知Zygote进程fork()一个新的进程,然后启动目标页面。如果要只看总结,翻到最后面,总结了七个点:
1、从桌面应用的LauncherActivity点击开始:

    //LauncherActivity继承子ListActivity,其实就是用于存放很多个应用的桌面
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Intent intent = intentForPosition(position);
        startActivity(intent);    //执行到Activity的startActivity
    }

    //接着看Activity的startActivity ---又执行到了startActivityForResult
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {    //mParent  是用于判断是否有父Activity,嵌套Activity中的,基本不会有,else就不看了
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(    //mMainThread就是ActivityThread的实例,应用程序入口main方法所在类。
                //这个时候应用都还没启动哪里来的实例?其实这里是Launcher的,也就是当前的。
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            //....
        } else {
           //....
        }
    }

上面的代码Activity. startActivityForResult方法通过mInstrumentation对象调用execStartActivity,Instrumentation主要用来监控应用程序和系统的交互。mMainThread其实是一个ActivityThread对象,实际上就是Launcher这个应用的ActivityThread,在Launcher启动时初始化。

  //Instrumentation.java类 
  public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
        //....
        try {
            //....
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
                    requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
     
    //ActivityTaskManager.getService() 看看获取的是什么:
    /**  ActivityTaskManager.java*/
    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    //通过ServiceManager 获取一个服务,这里先不管什么服务,ServiceManager是管理系统中所有服务的类
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    //获取一个什么样的服务呢?这里看到XXX.Stub.asInterface(),这不是经常见到AIDL的东西么?那看看谁实现了IActivityTaskManager.Stub接口
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };
    //找到了  就是它:
    public class ActivityTaskManagerService extends IActivityTaskManager.Stub 

ActivityTaskManagerService简称ATMS, 到这里明白:Launcher启动一个APP通过一系列的操作,使用AIDL的方式通过Binder将数据交给了ATMS,(这里是启动应用的一次IPC通信,即Launcher和ATMS的一次通信)ATMS开始处理消息,Instrumentation已经将Launcher所在的进程(whoThread参数,就是前面到mMainThread.getApplicationThread)传过来了,AMS将其保存为一个ActivityRecord对象,这个对象中有一个ApplicationThreadProxy即Binder的代理对象,AMS通ApplicationTreadProxy发送消息给Launcher。ATMS在接收到消息后,会通知前一个页面该休息了,也就是进入pause状态,这里通知Launcher你该休息了:

  //ActivityStack.java
  private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        //...
        ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
        //...
        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, next, false);
        }
        //...

        if (next.attachedToProcess()) {
            //应用已经启动
            try {
                //...
                transaction.setLifecycleStateRequest(
                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                getDisplay().mDisplayContent.isNextTransitionForward()));
                mService.getLifecycleManager().scheduleTransaction(transaction);
                //...
            } catch (Exception e) {
                //...
                mStackSupervisor.startSpecificActivityLocked(next, true, false);
                return true;
            }
            //...
            // From this point on, if something goes wrong there is no way
            // to recover the activity.
            try {
                next.completeResumeLocked();
            } catch (Exception e) {
                // If any exception gets thrown, toss away this
                // activity and try the next one.
                Slog.w(TAG, "Exception thrown during resume of " + next, e);
                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                        "resume-exception", true);
                return true;
            }
        } else {
            //冷启动流程
            mStackSupervisor.startSpecificActivity(next, true, true);
        }
    }

首先startPausingLocked()让上一个页面进入pause状态,然后开始判断要启动的APP的进程是否存在,如果存在就直接启动目标Activity,如果不存在就创建进程。如果当前是Launcher的话,存在进程,进入到 热启动流程,不存在,进入到 冷启动流程。也是Activity生命周期中为啥启动一个Activity时,先执行了onPause后,目标Activity才开始执行生命周期方法。接着看冷启动流程:

  //ActivityStackSupervisor.java  
  void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);

        boolean knownToBeDead = false;
        if (wpc != null && wpc.hasThread()) {
            try {
                //继续判断是否存在进程,如果存在就返回了 
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
             
            }
            knownToBeDead = true;
        }
        //....  mService是ATMS的实例对象,这里去创建了进程了,去看看怎么创建进程的
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
    }

ATMS如何创建新的进程?或者说怎么与Zygote进行通信,让Zygote进程fork()进程的,来看ATMS中:

  //ActivityTaskManagerService.java
  void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
            String hostingType) {
        try {
            //...
            // Post message to start process to avoid possible deadlock of calling into AMS with the
            //这里的意思:发布消息以启动进程,以避免调用AMS时可能出现的死锁
            // ATMS lock held.
            final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
                    mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
                    isTop, hostingType, activity.intent.getComponent());
            mH.sendMessage(m);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

ActivityManagerInternal::startProcess看这了,启动一个新的进程:

//是一个抽象方法,找到实现它的地方:
public abstract void startProcess(String processName, ApplicationInfo info,
            boolean knownToBeDead, boolean isTop, String hostingType, ComponentName hostingName);
//实现ActivityManagerInternal::startProcess方法的类在ActivityManagerService中:

//ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        // ... ...
    public final class LocalService extends ActivityManagerInternal {
        @Override
        public void startProcess(String processName, ApplicationInfo info,
                boolean knownToBeDead, String hostingType, ComponentName hostingName) {
            try {
                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
                            + processName);
                }
                //线程同步,防止多线程创建进程,进程创建只能支持单线程,所以后续AMS与Zygote的通信不能用Binder通信,选择使用Socket 
                synchronized (ActivityManagerService.this) {
                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                            new HostingRecord(hostingType, hostingName),
                            false /* allowWhileBooting */, false /* isolated */,
                            true /* keepIfLarge */);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
    }
    // ... ...
}

这里可以看到冷启动流程中,把创建新进程的任务交给了AMS去做,最后执行到ZygoteProcess中,Zygote进程负责创建新的进程。

//ZygoteProcess.java
    private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr)
            throws ZygoteStartFailedEx, IOException {
        try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
            final BufferedWriter usapWriter =
                    new BufferedWriter(
                            new OutputStreamWriter(usapSessionSocket.getOutputStream()),
                            Zygote.SOCKET_BUFFER_SIZE);
            final DataInputStream usapReader =
                    new DataInputStream(usapSessionSocket.getInputStream());

            usapWriter.write(msgStr);
            usapWriter.flush();

            Process.ProcessStartResult result = new Process.ProcessStartResult();
            result.pid = usapReader.readInt();
            // USAPs can't be used to spawn processes that need wrappers.
            result.usingWrapper = false;

            if (result.pid >= 0) {
                return result;
            } else {
                throw new ZygoteStartFailedEx("USAP specialization failed");
            }
        }
    }

可以看到,这里其实是通过socket和Zygote进行通信,BufferedWriter用于读取和接收消息。这里将要新建进程的消息传递给Zygote,由Zygote进行fork进程,并返回新进程的pid。这过程中也实例化ActivityThread对象,然后执行main方法:

//RuntimeInit.java
   protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;

        try {
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className,
                    ex);
        }

        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }
        //...
        return new MethodAndArgsCaller(m, argv);
    }

这里通过反射的方式调用了main方法,之后就进入到APP的主入口main方法了。

public static void main(String[] args) {
        //...
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        //这里将会告诉AMS,进程创建完毕,我启动好了...同时创建Application
        thread.attach(false, startSeq);

        //...

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        //...
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

到这里,AMS告诉Zygote创建新的进程已经就完事了,事后一根烟,赛过活神仙,我先休息一下....
现在继续:
创建进程完毕其实应用就已经启动了,因为一个应用其实就是一个进程,这个时候这个应用就开始自己去创建Application、启动Activity了,然后重新开始startActivity,进入到最开始的地方,这个时候判断的目标Activity所在的进程已经存在,则进入到Activity的启动流程,最终执行到ActivityThread的handleLaunchActivity()方法:

//ActivityThread.java
public Activity handleLaunchActivity(ActivityClientRecord r,
                                         PendingTransactionActions pendingActions, Intent customIntent) {
        //...
        WindowManagerGlobal.initialize();
        //... 接着执行performLaunchActivity
        final Activity a = performLaunchActivity(r, customIntent);
        //...
        return a;
    }

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //创建ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //创建Activity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        }

        try {
            if (activity != null) {
                //完成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, window, r.configCallback,
                        r.assistToken);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }

                //设置activity的主题
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                //调用activity的onCreate方法,到这里,就几乎结束了...
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
            }
        }
        return activity;
    }

然后到这里,第二场也结束了,总结一下这次的成果,消耗了不知道多少精力,精疲力尽的感觉,只为了最后那一刻,那就是:注意了,这里划重点,会考!
1、Launcher被调用点击事件,转到Instrumentation类的startActivity方法。
2、Instrumentation通过AIDL方式使用Binder机制告诉ATMS要启动应用的需求。
3、ATMS收到需求后,反馈Launcher,让Launcher进入Paused状态
4、Launcher进入Paused状态,ATMS将创建进程的任务交给AMS,AMS通过socket与Zygote通信,告知Zygote需要新建进程。
5、Zygote fork进程,并调用ActivityThread的main方法,也就是app的入口。
6、ActivityThread的main方法新建了ActivityThread实例,并新建了Looper实例,开始loop循环。
同时ActivityThread也告知AMS,进程创建完毕,开始创建Application,Provider,并调用Applicaiton的attach,onCreate方法。
7、最后就是创建上下文,通过类加载器加载Activity,调用Activity的onCreate方法。

最后:其实了解启动流程,最终也是为了启动优化做准备吧,这么看来,启动优化可以做的就是最后的:
1、Application的attach方法,MultiDexApplication会在方法里面会去执行MultiDex逻辑。所以这里可以进行MultiDex优化,比如今日头条方案就是单独启动一个进程的activity去加载MultiDex。
2、Application的onCreate方法,大量三方库的初始化都在这里进行,所以我们可以开启线程池,懒加载等等。把每个启动任务进行区分,哪些可以子线程运行,哪些有先后顺序。
3、Activity的onCreate方法,同样进行线程处理,懒加载。或者预创建Activity,提前类加载等等。

看到两张图,有时候都吃自己家的可能味道不大好,也可以偷吃点嘛,所以这两张图是我偷来的:


image.png
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容