惊呆了!女儿拿着小天才电话手表,问我Android启动流程!

2023-11-18

首先,new一个女儿,

var mDdaughter = new 女儿(“6岁”,“漂亮可爱”,“健康乖巧”,“最喜欢玩小天才电话手表和她的爸爸”)

好了,女儿有了,有一天,女儿问我:

“爸爸爸爸,你说我玩的这个小天才电话手表怎么这么厉害,随便点一下这个小图片,这个应用就冒出来了,就可以听儿歌了。好神奇啊。”

我心里一惊:

小天才电话手表的系统就是Android,所以这不就是。。面试官常考的应用启动流程嘛!女儿也要来面试我了吗!好了,既然女儿问了,那就答吧。但是,对付这个小小的0经验面试官,我该咋说呢?

解答小小面试官

女儿,你可以把手表里面想象成一个幼儿园,里面有一个老师,一个班长,一个班干部,以及一大堆小朋友。

一个老师:Z老师(Zygote进程)

一个班长:小A(ActivityManagerService)

一个班干部:小L(Launcher桌面应用)

一大堆小朋友:所有应用,包括音乐小朋友,聊天小朋友,日历小朋友等等。

应用启动过程就像一个小朋友被叫醒一样,开机之后呢,Z老师会依次叫醒班长和班干部(SystemServer#ActivityManagerService,Launcher),小L醒了之后就会去了解手表里有哪些小朋友,长什么样(icon,name),家庭信息(包名,androidmanifest)等等,然后一个个把小朋友的照片(icon)贴到自己的身上。比如有音乐小朋友,聊天小朋友,日历小朋友,其实也就是你手表上这个桌面啦。

在这里插入图片描述

这时候你要点开一个音乐小朋友呢(startActivity),小L就会通知班长小A(Binder),小A知道了之后,让小L自己休息下(Paused),然后就去找Z老师了。Z老师就负责叫音乐小朋友起床了(fork进程,启动ActivityThread),音乐小朋友起来后就又找小A带她去洗脸刷牙(启动ApplicationThread,Activity),都弄完了就可以进行各种表演了,唱歌啊,跳舞啊。

女儿似懂非懂的给我点了一个赞,爸爸你真棒。

十五年后

mDdaughter.grow(15)
mDdaughter.study(“Android”)
复制代码

过了十五年,女儿已经21岁了,正在学习Android,考虑要不要女从父业。

这天,她一脸疑惑的来找我: “爸,这个app启动到底是怎么个流程啊,我看了好久还是不大明白,要不你再跟我详细讲一遍吧?” “好嘞,别担心,我这次详细跟你说说”

解答Android程序媛

还记得我小时候跟你说过的故事吗,Android系统就像一个幼儿园,有一个大朋友叫Launcher,身上会贴很多其他小朋友的名片。这个Launcher就是我们的桌面了,它通过PackageManagerService获知了系统里所有应用的信息,并展示了出来,当然它本身也是一个应用。

通过点击一个应用图标,也就是触发了点击事件,最后会执行到startActivity方法。这里也就和启动Activity步骤重合上了。

那么这个startActivity干了啥?是怎么通过重重关卡唤醒这个应用的?

首先,介绍下系统中那些重要的成员,他们在app启动流程中都担任了重要的角色.

系统成员介绍

init进程,Android系统启动后,Zygote并不是第一个进程,而是linux的根进程init进程,然后init进程才会启动Zygote进程。

Zygote进程,所有android进程的父进程,当然也包括SystemServer进程

SystemServer进程,正如名字一样,系统服务进程,负责系统中大大小小的事物,为此也是启动了三员大将(ActivityManagerService,PackageManagerService,WindowManagerService)以及binder线程池。

ActivityManagerService,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,对于一些进程的启动,都会通过Binder通信机制传递给AMS,再处理给Zygote。

PackageManagerService,主要负责应用包的一些操作,比如安装,卸载,解析AndroidManifest.xml,扫描文件信息等等。

WindowManagerService,主要负责窗口相关的一些服务,比如窗口的启动,添加,删除等。

Launcher,桌面应用,也是属于应用,也有自己的Activity,一开机就会默认启动,通过设置Intent.CATEGORY_HOME的Category隐式启动。

搞清楚这些成员,就跟随我一起看看怎么过五关斩六将,最终启动了一个App。

第一关:跨进程通信,告诉系统我的需求

首先,要告诉系统,我Launcher要启动一个应用了,调用Activity.startActivityForResult方法,最终会转到mInstrumentation.execStartActivity方法。 由于Launcher自己处在一个单独的进程,所以它需要跨进程告诉系统服务我要启动App的需求。 找到要通知的Service,名叫ActivityTaskManagerService,然后使用AIDL,通过Binder与他进行通信。

这里的简单说下ActivityTaskManagerService(简称ATMS)。原来这些通信工作都是属于ActivityManagerService,现在分了一部分工作给到ATMS,主要包括四大组件的调度工作。也是由SystemServer进程直接启动的,相关源码可见ActivityManagerService.Lifecycle.startService方法,感兴趣朋友可以自己看看。

接着说跨进程通信,相关代码如下:

//Instrumentation.java
int result = ActivityTaskManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);


//ActivityTaskManager.java            
public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
        new Singleton<IActivityTaskManager>() {
            @Override
            protected IActivityTaskManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                return IActivityTaskManager.Stub.asInterface(b);
            }
        };



//ActivityTaskManagerService.java
public class ActivityTaskManagerService extends IActivityTaskManager.Stub

public static final class Lifecycle extends SystemService {
    private final ActivityTaskManagerService mService;

    public Lifecycle(Context context) {
        super(context);
        mService = new ActivityTaskManagerService(context);
    }

    @Override
    public void onStart() {
        publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
        mService.start();
    }
}        

复制代码

startActivity我们都很熟悉,平时启动Activity都会使用,启动应用也是从这个方法开始的,也会同样带上intent信息,表示要启动的是哪个Activity。

另外要注意的一点是,startActivity之后有个checkStartActivityResult方法,这个方法是用作检查启动Activity的结果。当启动Activity失败的时候,就会通过这个方法抛出异常,比如有我们常见的问题:未在AndroidManifest.xml注册。

public static void checkStartActivityResult(int res, Object intent) {
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ “; have you declared this activity in your AndroidManifest.xml?”);
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
case ActivityManager.START_PERMISSION_DENIED:
throw new SecurityException("Not allowed to start activity "
+ intent);
case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
“FORWARD_RESULT_FLAG used while also requesting a result”);
case ActivityManager.START_NOT_ACTIVITY:
throw new IllegalArgumentException(
“PendingIntent is not an activity”);
//…
}
}

第二关:通知Launcher可以休息了

ATMS收到要启动的消息后,就会通知上一个应用,也就是Launcher可以休息会了,进入Paused状态。

//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.startSpecificActivityLocked(next, true, true);
    }        
}

这里有两个类没有见过:

ActivityStack,是Activity的栈管理,相当于我们平时项目里面自己写的Activity管理类,用于管理Activity的状态啊,如栈出栈顺序等等。

ActivityRecord,代表具体的某一个Activity,存放了该Activity的各种信息。

startPausingLocked方法就是让上一个应用,这里也就是Launcher进入Paused状态。 然后就会判断应用是否启动,如果已经启动了,就会走ResumeActivityItem的方法,看这个名字,结合应用已经启动的前提,是不是已经猜到了它是干嘛的?没错,这个就是用来控制Activity的onResume生命周期方法的,不仅是onResume还有onStart方法,具体可见ActivityThread的handleResumeActivity方法源码。

如果应用没启动就会接着走到startSpecificActivityLocked方法,接着看。

第三关:是否已启动进程,否则创建进程

Launcher进入Paused之后,ActivityTaskManagerService就会判断要打开的这个应用进程是否已经启动,如果已经启动,则直接启动Activity即可,这也就是应用内的启动Activity流程。如果进程没有启动,则需要创建进程。

这里有两个问题:

怎么判断应用进程是否存在呢?如果一个应用已经启动了,会在ATMS里面保存一个WindowProcessController信息,这个信息包括processName和uid,uid则是应用程序的id,可以通过applicationInfo.uid获取。processName则是进程名,一般为程序包名。所以判断是否存在应用进程,则是根据processName和uid去判断是否有对应的WindowProcessController,并且WindowProcessController里面的线程不为空。代码如下:

//ActivityStackSupervisor.java
void startSpecificActivityLocked(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;
        } 
    }
}

//WindowProcessController.java
IApplicationThread getThread() {
    return mThread;
}

boolean hasThread() {
    return mThread != null;
}

还有个问题就是怎么创建进程?还记得Z老师吗?对,就是Zygote进程。之前说了他是所有进程的父进程,所以就要通知Zygote去fork一个新的进程,服务于这个应用。

//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。

可能又会有人问了?fork是啥?为啥这里又变成socket进行IPC通信,而不是Bindler了?

首先,fork()是一个方法,是类Unix操作系统上创建进程的主要方法。用于创建子进程(等同于当前进程的副本)。

那为什么fork的时候不用Binder而用socket了呢?主要是因为fork不允许存在多线程,Binder通讯偏偏就是多线程。

问题总是在不断产生,总有好奇的朋友会接着问,为什么fork不允许存在多线程?

防止死锁。其实你想想,多线程+多进程,听起就不咋靠谱是不。假设多线程里面线程A对某个锁lock,另外一个线程B调用fork创建了子进程,但是子进程却没有了线程A,但是锁本身却被fork了出来,那么这个锁没人可以打开了。一旦子进程中另外的线程又对这个锁进行lock,就死锁了。

第四关:ActivityThread闪亮登场

刚才说到由Zygote进行fork进程,并返回新进程的pid。其实这过程中也实例化ActivityThread对象。一起看看是怎么实现的:

//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);
}

复制代码

原来是反射!通过反射调用了ActivityThread 的 main 方法。ActivityThread大家应该都很熟悉了,代表了Android的主线程,而main方法也是app的主入口。这不对上了!新建进程的时候就调用了,可不是主入口嘛。来看看这个主入口。

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

    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    //...

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

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

main方法主要创建了ActivityThread,创建了主线程的Looper对象,并开始loop循环。除了这些,还要告诉AMS,我醒啦,进程创建好了!也就是上述代码中的attach方法,最后会转到AMSattachApplicationLocked方法,一起看看这个方法干了啥:

//ActivitymanagerService.java
private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    //...
    ProcessRecord app;
    //...
    thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                    null, null, null, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.isPersistent(),
                    new Configuration(app.getWindowProcessController().getConfiguration()),
                    app.compat, getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked(),
                    buildSerial, autofillOptions, contentCaptureOptions);
    //...
    app.makeActive(thread, mProcessStats);

    //...
    // See if the top visible activity is waiting to run in this process...
    if (normalMode) {
        try {
            didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
            badApp = true;
        }
    }
    //...        
}

//ProcessRecord.java
public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
    //...
    thread = _thread;
    mWindowProcessController.setThread(thread);
}

这里主要做了三件事:

bindApplication方法,主要用来启动Application。

makeActive方法,设定WindowProcessController里面的线程,也就是上文中说过判断进程是否存在所用到的。

attachApplication方法,启动根Activity。

第五关:创建Application

接着上面看,按照我们所熟知的,应用启动后,应该就是启动Applicaiton,启动Activity。看看是不是怎么回事:

//ActivityThread#ApplicationThread
public final void bindApplication(String processName, ApplicationInfo appInfo,
            List<ProviderInfo> providers, ComponentName instrumentationName,
            ProfilerInfo profilerInfo, Bundle instrumentationArgs,
            IInstrumentationWatcher instrumentationWatcher,
            IUiAutomationConnection instrumentationUiConnection, int debugMode,
            boolean enableBinderTracking, boolean trackAllocation,
            boolean isRestrictedBackupMode, boolean persistent, Configuration config,
            CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
            String buildSerial, AutofillOptions autofillOptions,
            ContentCaptureOptions contentCaptureOptions) {
        AppBindData data = new AppBindData();
        data.processName = processName;
        data.appInfo = appInfo;
        data.providers = providers;
        data.instrumentationName = instrumentationName;
        data.instrumentationArgs = instrumentationArgs;
        data.instrumentationWatcher = instrumentationWatcher;
        data.instrumentationUiAutomationConnection = instrumentationUiConnection;
        data.debugMode = debugMode;
        data.enableBinderTracking = enableBinderTracking;
        data.trackAllocation = trackAllocation;
        data.restrictedBackupMode = isRestrictedBackupMode;
        data.persistent = persistent;
        data.config = config;
        data.compatInfo = compatInfo;
        data.initProfilerInfo = profilerInfo;
        data.buildSerial = buildSerial;
        data.autofillOptions = autofillOptions;
        data.contentCaptureOptions = contentCaptureOptions;
        sendMessage(H.BIND_APPLICATION, data);
    }

    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case BIND_APPLICATION:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            }
        }

可以看到这里有个H,H是主线程的一个Handler类,用于处理需要主线程处理的各类消息,包括BIND_SERVICE,LOW_MEMORY,DUMP_HEAP等等。接着看handleBindApplication:

private void handleBindApplication(AppBindData data) {
    //...
    try {
        final ClassLoader cl = instrContext.getClassLoader();
        mInstrumentation = (Instrumentation)
                cl.loadClass(data.instrumentationName.getClassName()).newInstance();
    }
    //...
    Application app;
    final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
    final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
    try {
        // If the app is being launched for full backup or restore, bring it up in
        // a restricted environment with the base application class.
        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) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.providers);
            }
        }

        // 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);
        }
        //...
        try {
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                                + ": " + e.toString(), e);
            }
        }
    }
        //...
}

这里信息量就多了,一点点的看:

首先,创建了Instrumentation,也就是上文一开始startActivity的第一步。每个应用程序都有一个Instrumentation,用于管理这个进程,比如要创建Activity的时候,首先就会执行到这个类里面。

makeApplication方法,创建了Application,终于到这一步了。最终会走到newApplication方法,执行Application的attach方法。

public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}

attach方法有了,onCreate方法又是何时调用的呢?马上来了:

instrumentation.callApplicationOnCreate(app);

public void callApplicationOnCreate(Application app) {
    app.onCreate();
}

也就是创建Application->attach->onCreate调用顺序。

等等,在onCreate之前还有一句重要的代码:

installContentProviders

这里就是启动Provider的相关代码了,具体逻辑就不分析了。

第六关:启动Activity

说完bindApplication,该说说后续了,上文第五关说到,bindApplication方法之后执行的是attachApplication方法,最终会执行到ActivityThread的handleLaunchActivity方法:

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

首先,初始化了WindowManagerGlobal,这是个啥呢? 没错,就是WindowManagerService了,也为后续窗口显示等作了准备。

继续看performLaunchActivity:

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;
}

哇,终于看到onCreate方法了。稳住,还是一步步看看这段代码。

首先,创建了ContextImpl对象,ContextImpl可能有的朋友不知道是啥,ContextImpl继承自Context,其实就是我们平时用的上下文。有的同学可能表示,这不对啊,获取上下文明明获取的是Context对象。来一起跟随源码看看。

//Activity.java
Context mBase;

@Override
public Executor getMainExecutor() {
    return mBase.getMainExecutor();
}

@Override
public Context getApplicationContext() {
    return mBase.getApplicationContext();
}

这里可以看到,我们平时用的上下文就是这个mBase,那么找到这个mBase是啥就行了:

protected void attachBaseContext(Context base) {
    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

//一层层往上找

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) {
    
    attachBaseContext(context);

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }

   
}

这不就是刚才一开始performLaunchActivity方法里面的attach吗?太巧了,所以这个ContextImpl就是我们平时所用的上下文。

顺便看看attach还干了啥?新建了PhoneWindow,建立自己和Window的关联,并设置了setSoftInputMode等等。

ContextImpl创建完之后,会通过类加载器创建Activity的对象,然后设置好activity的主题,最后调用了activity的onCreate方法。

最后由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,如有需要获取资料文档的朋友,可以点击我GitHub免费领取!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

惊呆了!女儿拿着小天才电话手表,问我Android启动流程! 的相关文章

随机推荐

  • 【STM32 HAL库+STM32CUBEMX】使用usart1打印串口数据

    1 设置RRC外部时钟 设置高速外部时钟 2 设置DEBUG调试 debug设置成SW 3 设置usart 设置模式为异步通信 比特率115200 起始位8位 停止位1位 没有奇偶效验 4 设置时钟 选择外部时钟HSE 选择PLLCLK 设
  • H5右侧悬浮

    前言 实现H5右侧悬浮 点击展开收回移除 示例如下 默认 点击动画移入 html代码片段 div class right item menu i class right menu icon i div class right item st
  • Redis缓存穿透、击穿和雪崩

    Redis是一种开源的高性能缓存数据库 常用于加速数据访问 提高系统性能 然而 在使用Redis时 我们需要注意一些常见的问题 例如缓存穿透 击穿和雪崩 这些问题可能会导致系统性能下降甚至崩溃 因此了解和解决这些问题是非常重要的 1 缓存穿
  • 关于版本回退后怎么还原原来的分支

    如果在reset当前分支到某个节点时发现操作失误 想还原当前操作 回到reset之前的分支 如下图所示 1 输入git reflog 查看分支所有的操作历史记录 2 查看HEAD num 其中num的数值越大 则代表当前的历史分支的时间点越
  • 医院管理信息系统 HIS EMR PACS LIS

    主要功能模块 系统设置 数据字典 业务数据 权限管理医疗卡 门诊挂号 大屏叫号 分诊 急诊管理 门诊医院站 门诊护士站 门诊收费 药库 药房 住院管理 住院结算 住院医生 住院护士 医技执行 手术室管理 会诊 临床路径 电子病历 院长查询
  • 自协商功能原理及工作过程

    自协商原理 自协商是通过一种叫做快速连接脉冲 Fast Link Pulse 的信号实现的 简称FLP 自协商的双方通过FLP来交换数据 在具备自协商能力的端口没有Link的情况下 端口一直发送FLP 在FLP中包含着自己的连接能力信息 包
  • 已有一个Excel表格,用VBA批量新建文件夹

    已有一组Excel表格数据 使用VBA快速批量建文件夹 不知道标题怎么写 已经有了这样一组数据 然后需要根据这两组数据新建文件夹 代码如下 Sub 建文件夹 Dim i As Integer 用于下方数组 Dim Arr1 Arr2 定义一
  • 浅析集合不安全例子

    package com bochy list import java util ArrayList import java util List public class ArrayListConcurrentProblem public s
  • 弹性盒子(Flex)相关属性全解析

    目录 基本介绍 添加在父元素上的属性 添加在flex容器上的 一 设置为弹性布局 display flex 二 修改主轴的属性 flex direction 三 设置子元素是否折换 flex wrap 四 设置主轴的对齐方式 justify
  • C语言中函数的调用和函数返回值(17)

    C语言中函数的调用和函数返回值 一 函数的调用形式 按照函数调用出现的形式 可以分为三种调用方式 1 函数调用语句 例如 printf star 2 函数表达式 c 2 max a b 3 函数参数 printf d max a b m m
  • 这可能是全网最详细的python安装教程(windows)

    python安装是学习pyhon第一步 很多刚入门小白不清楚如何安装python 今天我来带大家完成python安装与配置 跟着我一步步来 很简单 你肯定能完成 第一部分 python安装 一 准备工作 1 下载和安装python 认准官方
  • TiggerRamDisk的超详细使用教程(亲测成功)

    目录 一 软件介绍 一 型号对应 二 可以支持的功能 二 操作步骤
  • SAP PO上传异步接口(PO发布Webservice)

    导语 接口是SAP与其他系统进行数据交互的一种方式 在这篇文章中主要讲述通过PO实现的上传接口 即SAP被动接收其他系统发送的数据 注意 对于配置中涉及的基本组件的介绍和创建在前面的博客有说明和介绍 再次不做过多赘述了 一 ESB Ente
  • 去除a标签默认样式_超链接样式的使用CSS入门基础(014)

    今天我们分享关于超链接样式的内容 在浏览器中 超链接的样式有默认样式 点击后样式 鼠标悬停样式和激活样式四种状态 超链接默认情况下是带有下划线的 看起来很丑 用户体验不好 前面我们说到了text decoration none可以去除下划线
  • Unity-后期处理效果之Bloom

    Unity 后期处理效果之Bloom 什么是Bloom Bloom属性 什么是Bloom Bloom是一种游戏常见的一种屏幕效果叫做高光溢出 是一种光学效果 其中来自明亮来源 如闪光 的光表现为泄露到周围对象中 通俗来说就是这种特效可以模拟
  • Visual Studio Code 插件

    Visual Studio Code 插件安装 插件安装 Script插件 Vue插件 插件安装 首页点击 工具和语言 如下图 接下来 在输入框中输入想要安装的插件的名字 点击 install 即可进行安装 Script插件 1 Eslin
  • C++中cout和cerr的区别?

    之前一直在用 但就是没在意两者到底有啥却别 今天又想到这个问题 总结下吧 以下的内容均是本人从网上查阅资料看来整理的 暂时还没有查阅官方资料 不保证准确 欢迎讨论 其实大家平常常会用的主要有三个 cout cerr clog 首先简单介绍下
  • http协议的状态码:404等常见网页错误代码

    http协议的状态码 一 1xx 临时响应 表示临时响应并需要请求者继续执行操作的状态码 100 继续 请求者应当继续提出请求 服务器返回此代码表示已收到请求的第一部分 正在等待其余部分 101 切换协议 请求者已要求服务器切换协议 服务器
  • 滴滴夜莺:从监控告警系统向运维平台演化

    简述 滴滴夜莺 Nightingale 是一款经过大规模生产环境验证的 分布式高性能的运维监控系统 基于Open Falcon 结合滴滴内部的最佳实践 在性能 可维护性 易用性方面做了大量的改进 支撑了滴滴内部数十亿监控指标 覆盖了从系统
  • 惊呆了!女儿拿着小天才电话手表,问我Android启动流程!

    首先 new一个女儿 var mDdaughter new 女儿 6岁 漂亮可爱 健康乖巧 最喜欢玩小天才电话手表和她的爸爸 好了 女儿有了 有一天 女儿问我 爸爸爸爸 你说我玩的这个小天才电话手表怎么这么厉害 随便点一下这个小图片 这个应