Activity 之 Window 创建过程

上一章 Android理解 Window 与 WindowManager,我们已经学习了 Window - ViewRootImpl - View 之间的关系,也知道 Window 实际是通过 WindowManagerService 去创建的。

那么 Activity 的 window 是怎么创建的呢?

一. Activity 的创建流程简述

首先,从 Activity启动流程 (基于8.0源码) 一文中,我们已经知道,Activity 的创建,最后会通过 ActivityThread 的 handleLaunchActivity(),这个时候 activity 才真正开始创建。如下:

    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        ... 
        WindowManagerGlobal.initialize();

        // Hint the GraphicsEnvironment that an activity is launching on the process.
        GraphicsEnvironment.hintActivityLaunch();

        final Activity a = performLaunchActivity(r, customIntent);

      ....

        return a;
    }

可以看到,它先对 WindowManagerGlobal 进行初始化,接着调用 performLaunchActivity() 方法:

    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
     ....

            if (activity != null) {
          	...
                appContext.setOuterContext(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);

   	... 
        return activity;
    }

#Activity#attach

    @UnsupportedAppUsage
    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,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        
        // 通过 PhoneWindow 创建 window
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        //设置回调,比如 onAttachToWindow()
        mWindow.setWindowControllerCallback(this);
     ...
        //设置 windowmanager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    	...
    }

在这个方法中,它通过 activity.attach() 实现了 window 的创建,而在 activity#attach() 中,创建了一个 Window;其实 window 已经被创建了,但是它只是一个空壳子,我们知道,window 只有与 view 绑定,才有意义。

二. Activity 的 DecorView 的创建

Activity view 的创建是通过 setContentView 来设置的,现在的Activity 都是继承AppcompatActivity,它会对使用代理类对 Activity 进行一些数据封装,这里为了更方便的了解 Activity 的创建,让 MainActivity 继承 Activity ,然后查看 setContentView :

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

从 Activity 创建流程知道,getWindow() 为 PhoneWindow,所以,可以直接去到 PhoneWindow 的 setContentView :

    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) {
            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 {
        	// 解析 layoutId 中的子 view
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
    
        mContentParentExplicitlySet = true;
    }

其中 installDecor() 为关键方法,它会完成以下功能:

  1. 通过 generateDecor(),创建一个 DecorView ,它继承自 FrameLayout
  2. 通过 generateLayout(),得到父布局的创建,设置 window 背景,
  3. 设置 title 等样式
    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //创建 DecorView
            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) {
            //创建父布局,设置 window 背景等
            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);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }
              ... 
            } else {
            // 是否隐藏 title
                mTitleView = findViewById(R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }

      
                ...
            }
        }
    }

其中 generateDecor() 比较简单,就是 创建 DectorView :

    protected DecorView 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;
		....
        return new DecorView(context, featureId, this, getAttributes());
    }

来重点看一下 generateLayout() 方法,它的功能如下:

  1. 解析 style 的属性,比如是否有 actionbar,是否背景透明,动画等
  2. 初始化 DecorView 布局,比如 R.layout.screen_simple,
  3. onResourcesLoaded() ,通过 LayoutInflater 去解析根布局,拿到啊 title 和 content
  4. 是否设置 DectorView 的背景
  5. 放回 android.R.id.content 所对应的 view
protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

        
        // 解析style 的属性,比如判断是否有 actionbar
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

   ...
        if (getContainer() == null) {
            if (mBackgroundDrawable == null) {

                if (mFrameResource == 0) {
                    mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                }
                //拿到背景
                if (a.hasValue(R.styleable.Window_windowBackground)) {
                    mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);
                }
            }
	...
        }

        // Inflate the window decor.
        // 初始化 DecorView 布局
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } 
        ...
else 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();
        //解析根布局的view
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

		//  拿到 R.id.content 的值
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

       ...
        if (getContainer() == null) {
            //先设置背景
            mDecor.setWindowBackground(mBackgroundDrawable);

      ...
        return contentParent;
    }

mDecor.onResourcesLoaded() 回去解析真正的根布局,在此之前,它会根据主题,去解析到底使用哪个 layoutId,比如使用 R.layout.screen_simple (在frameworks\base\core\res\res\layout下),它其实为一个 LinearLayout,分为 bar 和 content 两个部分:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

其实,我们用 tool - layout inspector 也可以看出来, DecorView 也是由上面两个部分组成的:
在这里插入图片描述
用一张简单的图,就是如下所示:
在这里插入图片描述
这样,根布局的 DecorView 就设置好了,接着会调用 mLayoutInflater.inflate(layoutResID, mContentParent) 方法,去解析 Activity 的layoutId,关于这部分的解析,可以参考:Android 换肤原理解析

三. Window 的显示

上面只是让 DecorView 填充了内容,但是还未显示出来,这时还需要 WindowManagerService 的 addView 方法,将其 Window 和 View 关联起来,而在 ActivityThread 的 handleResumeActivity 方法中,有如下代码:

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {

        // TODO Push resumeArgs into the activity for consideration
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
     ... 
        if (r.window == null && !a.mFinished && willBeVisible) {
        // 拿到 window
            r.window = r.activity.getWindow();
            // 拿到 decorview
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
               //拿到 ViewRootImpl
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 通过 Windowmanager 的 addView ,把 window 和view 关联起来
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            ....
    }

到这里,Window 中 终于被 WindowManager 接管了,接下来的 Window 和 VIew 的关联,可以参考上一章Android理解 Window 与 WindowManager

这里做一下总结:

  1. 通过 Activity 的 attach 方法创建 Window (实际调用时 PhoneWindow),我们在 Activity 中调用 getWindow()就是它
  2. 在 setContentView() 方法中,PhoneWindow 创建了顶层的 DecorView,它是一个 FrameLayout,通过不同主题选择根布局,并添加进去 (默认都是 LinearLayout),它包含 title 和 content。
  3. DecorView 最终通过 WindowManager 的 addView() ,把 Window 和 DecorView 关联起来

参考Android 艺术开发

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值