Activity启动

Luncher Activity启动

App安装时,PMS会解析AndroidManifest.xml,拿到组件信息。Launcher从PMS中拿到action为android.intent.action.MAIN并且category为android.intent.category.LAUNCHER的Activity信息并为他们创建桌面图标。

<activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

点击桌面图标后,Launcher通过AMS启动MainActivity,Launcher、AMS、MainActivity三者在不同进程,通过Binder通信。

一切从Launcher开始

1、点击桌面图标时,Launcher调用startActivitySafely,在startActivitySafely方法里面,给intent添加Intent.FLAG_ACTIVITY_NEW_INTENT,来设置根Activity在新的任务栈中,然后调用Launcher的startActivity(intent);
startActivity最终会调用startActivityForResult(intent , -1);
其中-1表示Launcher不需要知道Activity的返回结果。

2、startActivityForResult里通过Instrumentation的execStartActivity来启动Activity。Instrumentation 用于监控应用和系统的交互。

3、跨进程通信,从Launcher到AMS
execStartActivity会调用ActivityManagerNative.getDefault().startActivity() 来拿到AMS的代理对象ActivityManagerProxy ,实际上就是Binder接口的客户端实现类,通过transact来调用服务端Binder,也就是ActivityManagerService (AMS)的startActivity。

AMS所在进程

4、获取目标Activity信息
AMS的startActivity调用ActivityStackstartActivityMayWait,在这个方法里,PMS解析Intent里面的 目标Activity信息 并保存在ActivityInfo里,然后通过同步方法执行startActivityLocked

5、一个Activity对应一个ActivityRecord,保存旧的,创建新的
startActivityLocked目的是拿到源Activity的进程信息,在AMS找到源Activity(也就是Launcher)的ActivityRecord,用sourceRecord保存,并且新建ActivityRecord保存目标Activity(MainActivity)的各种信息。然后调用startActivityUnCheckedLocked

6、需要新建任务栈 ?
startActivityUnCheckedLocked主要是判断是否需要新建任务栈,如果设置android:taskAffinity 属性,则AMS会新建TaskRecord,将目标Activity放到新的任务栈去执行,否则直接放入当前任务栈执行。

7、通知Resume状态的Launcher进入Pause状态,为MainActivity的启动做准备

新放入栈的MainActivity在栈顶,调用resumeTopActivityLocked来判断栈顶的这个Activity是否已经启动,若已启动,则直接返回;否则看当前是否有Resume状态的Activity。
显然Launcher就是Resumed状态,然后ActivityStack使用startPausingLocked通知Resume状态的Launcher进入Pause状态,以便把焦点让给即将启动的MainActivity

8、通知Launcher的进程中止Launcer,终止后进程向AMS再发送启动MainActivity组件的通知
startPausingLocked通知Launcher的进程中止Launcher,并更新ActivityRecord和ActivityStack里面保存的Resume状态和Pause状态的Activity信息(当前没有resume状态的,而Pause状态的就是Launcher了)

9、AMS -> Launcher,进程间再次通信
AMS进程中,ApplicationThreadProxy类的schedulePauseActivity,作为客户端Binder,向服务端Launcher进程发送通信请求。Launcher进程的ActivityThread收到客户端Binder传来的Message对象,给Launcher主线程发送PAUSE_ACTIVITY的信息,让Launcher在主线程执行中止操作。

如果直接在服务端收到msg的线程中止Launcher,可能会耗时。而且可能会有涉及UI的操作,所以特意到Launcher主线程

10、Launcher执行onPause,以及执行前后的数据和状态保存
就像AMS里面一个Activity对应一个ActivityRecord一样,Launcher进程中,每个Activity也对应一个ActivityClientRecord对象用于保存相关信息,然后调用onPause,然后做一些数据和状态的写入操作,做完后,再次拿到ActivityManagerProxy 的代理客户端,向服务端AMS发送通信请求,表示Launcher已经onPause完毕。

11、AMS收到Launcher的消息后,会再次调用resumeTopActivityLocked,就是第7步,此时没有Resumed状态的Activity了,于是开始调用startSpecificActivityLocked 启动MainActivity。这个方法会根据进程名和用户ID,判断对应的进程是否存在了,如果存在则交给进程去启动,没有则调用startProcessLocked 来创建新的进程。

12、startProcessLocked创建ProcessRecord ,看名字就知道是保存进程信息的,然后添加到AMS里保存进程信息的ArrayMap里。创建新进程时,指定了进程入口是ActivityThread里面的main方法:

 public static void main(String[] args) {
         //......
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop();
    }

在main方法里,通过prepareMainLooper创建并启动消息循环。创建ActivityThread并且通过attach向AMS发送启动完成通知。

13、ActivityThread也就是MainActivity所在进程,创建完成后,又得需要跨进程通信去通知AMS创建成功,并把ApplicationThread保存到AMS里,ApplicationThread是Binder客户端,携带新创建的进程信息。

14、ActivityThread的attach方法里,再次通过ActivityManagerNative.getDefault() 拿到AMS的代理ActivityManagerProxy,写入Parcel数据并调用服务端AMS的attachApplication来保存信息到AMS。

15、进程成功创建,然后AMS通过ActivityStack的realStartActivityLocked 来请求进程启动第一个Activity,realStartActivityLocked里面实际调用的是ApplicationThreadProxy的scheduleLaunchActivity,一看又是进程间通信了,ApplicationThreadProxy作为客户端Binder,而服务端肯定就是新建进程里的ActivityThread了。

16、ActivityThread调用queueOrSendMessage 来向当前进程的主线程的消息队列发送LAUNCH_ACTIVITY 消息,受到消息后调用handleLaunchActivity来启动MainActivity :

private class H extends Handler {
     public void handleMessage(Message msg) {
            
       switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
    
                r.packageInfo = getPackageInfoNoCheck(
                        r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            } 
            break;
            //......
     }

}

17、handleLaunchActivity首先通过performLaunchActivity启动MainActivity,然后调用handleResumeActivity把MainActivity的状态设置为Resumed。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        ComponentName component = r.intent.getComponent();
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
       
        } catch (Exception e) {//... }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, 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);

                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            mActivities.put(r.token, r);
        } catch { //...... }
        return activity;
    }

18、performLaunchActivity拿到ComponentName,比如"com.test.MainActivity",然后新建ClassLoader,在mInstrumentation的newActivity方法里,通过ClassLoader来实例化Activity类。接着创建了Application Context。然后再用mInstrumentation的callActivityOnCreate方法,来调用MainActivity的onCreate方法。这就是我们所熟知的Activity启动流程了。

最后整理了一张汇总的图:


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

推荐阅读更多精彩内容