安卓App启动流程源码分析

源码在26之后貌似变了一些东西,自己学习,自己记录一下,加深一下理解

首先当你在laucher点击一个应用图标的就会去启动一个app
一个app启动的入口就是这个ActivityThread,实际就是点击图标之后启动一个线程来搞事情,这个线程就是主线程
ActivityThread方法有一个main方法,就是程序的入口了,看一下这个方法都搞了一些什么事情

 public static void main(String[] args) {
        ....
       //这一堆是初始化了系统目录等一些东西,从Environment类就可以看出来主要干什么
        Environment.initForCurrentUser();
        EventLogger.setReporter(new EventLoggingReporter());
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
         ...
        //初始化化主线程的Looper
        Looper.prepareMainLooper();
   
        //绑定线程
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        //主线程处理消息的Handler
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ...
        //开启Looper进行无线循环处理
        Looper.loop();
        ...
    }

其实启动很简单,就是在主线程初始化一个Looper,然后创建一系列的消息指令放到消息队列,开启looper循环进行处理,复杂的地方全在 thread.attach这个步骤里.
先不去看其他的,先看一下主线程的这个Looper是怎们创建的,我们知道在平时使用Handler的时候不需要创建Looper(UI线程使用),其实就是用为在这个地方进行了初始化,点击方法prepareMainLooper我们查看代码发现这里首先调用了prepare方法,然后直接调用myLooper方法为主线程Looper进行了赋值

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

这俩个方法做了什么呢
prepare方法:
发现一个东西ThreadLocal,ThreadLocal是一个线程安全的包装机制

  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper中保存着一个ThreadLocal,首先通过ThreadLocal.get去获取looper,如果获取到了,那么久说明不对头,抛出异常,一个线程只能有一个Looper的,如果没有,那么就为ThreadLocal设置一个Looper进去

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

看下Looper的构造函数,挣了一个消息队列,然后保存当前这个调用他的线程,很简单,

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

然后就是看下mylooper这个方法了

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

再看get方法,这个threadLocal存放着一个map来保存looper,调用get的时候回根据线程来获取相应的looper,所以现在可以知道了,主线程的Looper以及平时可能会在子线程中调用的looper都被存放到这个map中了,现在可以看明白myLooper方法其实就是去获取一下当前线程的looper

  public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

到这里Looper的相关创建就完成了.

然后就是attach方法,这里面做了大部分主要内容

 private void attach(boolean system) {
            ...
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ...
         }

上面的代码大体可以看出来是做了一个绑定applaction的操作,在api25之前这里获取到的是一个ActivityManager的代理来处理这个问题的,25之后变成了使用AIDL来做处理这个问题,看代码

public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

ServiceManager.getService方法会经过ServiceManagerNative类的一个内部类代理去获取AMS,最终获取到这个类ActivityManagerService
下一步会调用mgr.attachApplication(mAppThread)这个东西,去AMS中找一下这个方法看看有没有

    @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
--------------------------------------------------------------------------------
            attachApplicationLocked(thread, callingPid);
------------------------------------------------------------------------------
            Binder.restoreCallingIdentity(origId);
        }
    }

找到如上图一个方法,获取了一下进程,清楚了一下进程的调用标记?好像是这样子额,
然后最主要的是中间代码,看看代码
代码太长了这个方法,几百行代码,掐头去尾,留下中间比较重要的.

 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
...
if (app.instr != null) {
                thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            } else {
                thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            }
...
}

中间着部分代码的前半段貌似做了一些进程的相关操作,后半段做了一些安全的验证,检查下这个进程中是否还遗留一些activity啊,广播啊这类的,这些没有深看,主要看中间代码
IApplicationThread 是个什么,发现这个是个ActivityThread的内部类,并且作为一个final常量被使用,在这个方法中调用了IApplicationThread的bindApplication方法,看方法名应该是个重要操作,绑定Applaction,我们看一下这个方法做了什么
咋一看,哎呀呀好多参数,心理慌得一笔,细看其实就是一个赋值,直到最后一行代码,发现了么,这个里很明显发送了一个消息,去绑定applaction,并把相关的包信息啊什么的传过去,反正经过饶了AMS一圈还是回到了activityThread来处理...


        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) {

            if (services != null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            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;
            sendMessage(H.BIND_APPLICATION, data);
        }

然后我们找一下handler是怎们处理的,这个方法也比较长,大部分代码为进行一些启动app前的配置操作,比如配置时区啊等等一些根据data的内容进行的,所以我们删除一些不重要的,慢慢看,留下几个看起来有用的代码

private void handleBindApplication(AppBindData data) {
  // 将UI线程注册为敏感线程
        VMRuntime.registerSensitiveThread();
        if (data.trackAllocation) {
            DdmVmInternal.enableRecentAllocations(true);
        }
    ...
    mInstrumentation = (Instrumentation)
        cl.loadClass(data.instrumentationName.getClassName())
        .newInstance();
    //通过反射初始化一个Instrumentation仪表。
    ...
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    //通过LoadedApp命令创建Application实例
    mInitialApplication = app;
    ...
    mInstrumentation.callApplicationOnCreate(app);
    //让仪器调用Application的onCreate()方法
    ...
}

我们发现在一顿猛如虎的操作者之后有个名字叫mInstrumentation的少年调用了applaction的onCreat方法,只是简单的调用了一下 app.onCreate(),为什么不直接调用呢?

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

我们先看mInstrumentation这个是什么?
Android instrumentation是Android系统里面的一套控制方法或者”钩子“。这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行,其实指的就是Instrumentation类提供的各种流程控制方法.
然后我们继续发现callApplicationOnCreate要一个app参数,这个app是从哪里来的?继续像上看代码发现app产自这么一个方法makeApplication()
看一下这个方法干了什么事情

 public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
if (mApplication != null) {
            return mApplication;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
...........
}

发现是由一个叫LoadedApk的类去执行的,这个类是维护这本地已经加载的apk,不管为啥要用它去创建applaction,看这个代码的意思就是如果有这个app就直接返回,没有就用反射创建一个,创建过程又交给了仪表盘mInstrumentation

  static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

简单的反射创建了applaction,整个过程操作犹如猛虎下山,势不可挡啊.

然后就是Application初始化完毕之后会给主线程的handler发送一个启动activity的消息,最终会走这么一个方法

  private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
      创建一个activty
       Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}

首先调用performLaunchActivity方法创建一个activity,然后就可以handleResumeActivity,就是可以走onResume方法了

具体看下performLaunchActivity方法

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
.....
 activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

.....
 Application app = r.packageInfo.makeApplication(false, mInstrumentation);
.....
  activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
....
  if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
....
}

发现任然是由仪表盘去创建的这个activity,然后做一个attach操作绑定界面,然后就是一些流程控制的操作,callActivityOnCreate,就可以执行activity的oncreat方法了...最后返回一个activity.

mInstrumentation.newActivity来创建activity其实就是一个反射区创建,很简单
基本走到handleResumeActivity方法的时候就没什么了,一个app的启动并展示第一个界面的过程就完成了,总体就是这么一个流程,但是其中还有无数细节待我们慢慢研究啊

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

推荐阅读更多精彩内容