Activity的启动机制

概述

最近去面试,被问到Activity的启动机制,顿时一脸懵逼,什么鬼!回来后就决定了要看下源码,学习下Activity启动相关的知识。

启动过程介绍

Activity的启动是从ActivityThread开始的,我们直接来看下ActivityThread的代码。代码过长只挑重点的看

    public static void main(String[] args) {
        ....(此处省略一万行代码)....

        // 原来Looper的初始化和线程绑定在这里做了,所以我们在UI线程里可以直接用handler,如果是在线程中就要自己调用prepare()和loop()。
        Looper.prepareMainLooper();

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

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

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

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // 触发事件循环
        Looper.loop();

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

这里只是ActivityThread的开始而已,Activity呢?ActivityThread里有个Handler的实现,里面收到LAUNCH_ACTIVITY消息后会调用handleLaunchActivity(r, null)进入处理启动相关事项,我们看这个函数

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ....(此处省略一万行代码)....

        // Initialize before creating the activity
        // 初始化WindowManagerGlobal,用于后面addView用的,只需要知道window是一个窗户,透明的窗户,后面还是要填充玻璃的
        WindowManagerGlobal.initialize();
        
        // 这里能过performLaunchActivity创建了一个Activity实例
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            // 这里执行了Activity的resume,create呢?在performActivity里面已经做好了
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);

            ....(此处省略一万行代码)....
            }
        } else {
            // If there was an error, for any reason, tell the activity
            // manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
                // Ignore
            }
        }
    }

通过performLaunchActivity创建Activity成功后就执行了handleResumeActivity,就是进入Activity的onResume状态了,那创建初始化的过程我们再看看performLaunchActivity里的具体实现

  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       
       ....(此处省略一万行代码)....

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            // 这里通过mInstrumentation生成了一个Activity实例,后面Activity状态也是由mInstrumentation来管理的
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        ....(此处省略一万行代码)....
        
        if (activity != null) {
            ....(此处省略一万行代码)....
            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 (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }    
            }
        }
        ....(此处省略一万行代码)....

        return activity;
    }

attach后面就调用了callActivityOnCreate,就进入了Activity的onCreate状态,那attach里面又做了什么,我们再跟进去看下

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

       mFragments.attachHost(null /*parent*/);

       // 实例化window对象,有了window就有了activity内容的载体了
       mWindow = new PhoneWindow(this);
       mWindow.setCallback(this);
       mWindow.setOnWindowDismissedCallback(this);
       mWindow.getLayoutInflater().setPrivateFactory(this);
       if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
           mWindow.setSoftInputMode(info.softInputMode);
       }
       if (info.uiOptions != 0) {
           mWindow.setUiOptions(info.uiOptions);
       }
       mUiThread = Thread.currentThread();

       mMainThread = aThread;
       mInstrumentation = instr;
       mToken = token;
       mIdent = ident;
       mApplication = application;
       mIntent = intent;
       mReferrer = referrer;
       mComponent = intent.getComponent();
       mActivityInfo = info;
       mTitle = title;
       mParent = parent;
       mEmbeddedID = id;
       mLastNonConfigurationInstances = lastNonConfigurationInstances;
       if (voiceInteractor != null) {
           if (lastNonConfigurationInstances != null) {
               mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
           } else {
               mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                       Looper.myLooper());
           }
       }

       mWindow.setWindowManager(
               (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
               mToken, mComponent.flattenToString(),
               (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
       if (mParent != null) {
           mWindow.setContainer(mParent.getWindow());
       }
       mWindowManager = mWindow.getWindowManager();
       mCurrentConfig = config;
   }

其实这段代码就是初始化window,为后面加载activity内容做准备,刚才也说了,attach后就进入了Activity的onCreate状态,在onCreate里就调用了我们常见的setContentView了。

public void setContentView(@LayoutRes int layoutResID) {
       getWindow().setContentView(layoutResID);
       initWindowDecorActionBar();
   }

setContentView其实就是调用刚才attach里初始化的window里的setContentView方法,我们再看PhoneWindow里setContentView的实现

@Override
public void setContentView(int layoutResID) {
       // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
       // decor, when theme attributes and the like are crystalized. Do not check the feature
       // before this happens.
       if (mContentParent == null) {
           // 初始化DecorView
           installDecor();
       } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
           mContentParent.removeAllViews();
       }

       if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
           final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                   getContext());
           transitionTo(newScene);
       } else {
           // 加载我们的layout到mContentParent中
           mLayoutInflater.inflate(layoutResID, mContentParent);
       }
       mContentParent.requestApplyInsets();
       final Callback cb = getCallback();
       if (cb != null && !isDestroyed()) {
           cb.onContentChanged();
       }
   }  
 private void installDecor() {
       if (mDecor == null) {
           mDecor = generateDecor();
           mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
           mDecor.setIsRootNamespace(true);
           if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
               mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
           }
       }
       if (mContentParent == null) {
           mContentParent = generateLayout(mDecor);
           ....(此处省略一万行代码)....
       }
 }
protected ViewGroup generateLayout(DecorView decor) {
       ....(此处省略一万行代码)....
       View in = mLayoutInflater.inflate(layoutResource, null);
       decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
       mContentRoot = (ViewGroup) in;

       ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       if (contentParent == null) {
           throw new RuntimeException("Window couldn't find content container view");
       }
       ....(此处省略一万行代码)....
}

setContentView的时候,调用了installDecor方法,以及mLayoutInflater.inflate(layoutResID, mContentParent);把我们的布局加载到mContentParent中。但mContentParent从哪里来?在installDecor里,通过generateLayout把我们的内容区域添加进去了,我们看到了熟悉的ID_ANDROID_CONTENT,就是我们的内容区域。

总结

看完这些代码后,对Activity的启动流程有了一个大概的了解。总的来说就是通过IPC调用AMS里startActivity,然后在ActivityThread的handleLanchActiivyt里先初始化window,实际是PhoneWindow;然后就调用了Activity的onCreate,setContentView就是调用phoneWindow的setContentView,里面初始化了decorView,decorView是phoneWindow的变量,继承自FrameLayout,是我们整个view的根布局,里面有一个ContentViewGroup的区域是用来加载我们的layout的。加载我们的Layout后,通过WindowManager把window添加进去,显示到界面上。可以说window就是一个透明的窗户区域,decorView就是窗户的边框,有固定的一些东西(ActionBar、Title),layout就是我自定义的窗户,可以雕花可以贴纸。自己的理解,不喜勿喷。

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

推荐阅读更多精彩内容