天天看點

Activity 調用setContentView,加載視圖過程分析直接撸代碼整體流程是:

直接撸代碼

扛不住直接翻到後面,但是不看代碼,隻看結論,對于學習是沒有什麼幫助的

先初始化PhoneWindow:

Activity 在建立出來之後,會調用attach方法,在這裡面會建立PhoneWindow,并初始化WindowManager,WindowManagerService就是Binder的本地代理對象,用于向系統Window中添加我們視圖;

final void Activity.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,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
        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());
        }
        ...
}
           

調用PhoneWindow的setContentView,初始化後,再進行設定ActionBar;

public void Activity.setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
}

private void initWindowDecorActionBar() {
        Window window = getWindow();
        // Initializing the window decor can change window feature flags.
        // Make sure that we have the correct set before performing the test below.
        window.getDecorView();
        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
            return;
        }
        mActionBar = new WindowDecorActionBar(this);
        mActionBar.set ...
}
           

接下來就進入到PhoneWindow中;

這裡面就能看到整體的建立流程;可以得出幾個結論:

  1. Activity的根View就是DecorView;

    在mContentParent沒有初始化過,那就要先進行初始化DecorView;進入到installDecor();

  2. 當View設定了變換動畫,需要先清空View,然後讓這些View去執行變換動畫;比如Android5.0的轉場動畫;

    也就是在場景Scene變化時,是通過Transitions變換來實作轉場動畫的;

    更多場景和變換動畫可參考http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0113/2310.html

    Activity 調用setContentView,加載視圖過程分析直接撸代碼整體流程是:
  3. mContentParent就是com.android.internal.R.id.content,我們稱為RootView
  4. 最後如果是ResLayoutId就通過LayoutInflater來解析填充,并挂載到mContentParent上;
public void PhoneWindow.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) {
        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 {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}
           

再看下DecorView和mContentParent的生成:

//初始化DecorView和mContentParent
 private void PhoneWindow.installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();
            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( R.id.decor_content_parent);
        }
}
//建立DecorView
protected DecorView PhoneWindow.generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
}
//建立mContentParent,裡面代碼省略了一大堆
protected ViewGroup PhoneWindowgenerateLayout(DecorView decor){
        // Apply data from current theme.

        TypedArray a=getWindowStyle();
        ...
        if(a.getBoolean(R.styleable.Window_windowFullscreen,false)){
        setFlags(FLAG_FULLSCREEN,FLAG_FULLSCREEN&(~getForcedWindowFlags()));
        }
        ...設定各種style

        if(a.hasValue(R.styleable.Window_windowFixedWidthMajor)){
        if(mFixedWidthMajor==null)mFixedWidthMajor=new TypedValue();
        a.getValue(R.styleable.Window_windowFixedWidthMajor,
        mFixedWidthMajor);
        }
        ...設定Window屬性
        WindowManager.LayoutParams params=getAttributes();

        if(params.windowAnimations==0){
        params.windowAnimations=a.getResourceId(
        R.styleable.Window_windowAnimationStyle,0);
        }
        ...建立DecorView的屬性
        // Inflate the window decor.
        int layoutResource;
        int features=getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if((features&(1<<FEATURE_ACTION_MODE_OVERLAY))!=0){
        layoutResource=R.layout.screen_simple_overlay_action_mode;
        }else{
        // Embedded, so no decoration is needed.
        layoutResource=R.layout.screen_simple;
        // System.out.println("Simple!");
        }

        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater,layoutResource);
        // 根據不同的屬性,選擇不同的layout,比Theme帶ActionBar的選用ActionBar的layout;
        //但是裡面都個id是ID_ANDROID_CONTENT,也就是com.android.internal.R.id.content
        ViewGroup contentParent=(ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if(contentParent==null){
        throw new RuntimeException("Window couldn't find content container view");
        }
        ...
        mDecor.finishChanging();
}
           

到此全部建立完成。

整體流程是:

  1. attach時建立PhoneWindow,用于挂載到系統上;
  2. 在PhoneWindow中調用generateDecor(-1)建立DecorView,設定為根View,在DecorView實際是個FrameLayout;并在這時候設定各種系統屬性;
  3. 在DecorView中建立mContentParent,實際是通過不同的主題,直接選擇不同Layout,然後填充成mRootView;并将mRootView添加到DecorView中;最後通過findViewById(ID_ANDROID_CONTENT),取出mContentParent;
  4. 設定場景和變換動畫;
  5. 如果我們傳入的是ResLayoutId,就将mContentParent做為parent使用LayoutInflater填充;否則直接addView挂載到mContentParent上;