Android Java层UI渲染实现 三 View的创建

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36391075/article/details/83513559

根据前面的分析,我们可以知道,每一个Activity都关联了一个Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含一个View对象,用于描述应用程序视图。应用程序视图是真正用来实现UI和布局的。也就是说,每一个Activity的UI内容和布局都是通过与其所关联的一个Window对象的内部一个View对象实现的。

还记得Activity的启动流程嘛?在ActivityThread.performLaunchActivity(ActivityClientRecord r, Intent customIntent)中,先创建了Context,然后是创建了一个Activity,然后在对Activity的初始化中又创建了一个Window,之后会去调用Activity.onCreate()方法,我们从这里开始分析:

一般我们会自定义Activity,调用的onCreate方法,也就是我们自己实现的onCreate方法

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


    }
}

在onCreate方法中,会调用父类的onCreate方法,最终到达Activity.onCreate(),然后会调用setContentView(R.layout.activity_main);进行视图的创建:

@Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

@NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

//AppCompatDelegate.java
 public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else {
            return new AppCompatDelegateImplV14(context, window, callback);
        }
    }

在这里,我们遇到一个新的类:AppCompatDelegate.java

public abstract class AppCompatDelegate

我们看看它的介绍:

This class represents a delegate which you can use to extend AppCompat’s support to any {@link android.app.Activity}.

上面大概是说,它是代表的是一个委托,你可以使用该委托将AppCompat的支持扩展到任何Activity。

继续看,当AppCompatDelegate对象建立完了后,会调用setContentView()方法,我这里使用的api是27,所以返回的AppCompatDelegate对象是AppCompatDelegateImplV14,而AppCompatDelegateImplV14继承自AppCompatDelegateImplV9:

class AppCompatDelegateImplV14 extends AppCompatDelegateImplV9

AppCompatDelegateImplV14(Context context, Window window, AppCompatCallback callback) {
        super(context, window, callback);
    }
AppCompatDelegateImplV9(Context context, Window window, AppCompatCallback callback) {
        super(context, window, callback);
    }

setContextView方法是在AppCompatDelegateImplV9这个类中实现的。

//AppCompatDelegateImplV9.java
@Override
    public void setContentView(View v, ViewGroup.LayoutParams lp) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v, lp);
        mOriginalWindowCallback.onContentChanged();
    }

我们先看ensureSubDecor()方法:

 private void ensureSubDecor() {
 //如果mSubDecor还没有初始化,就创建SubDecor
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();

            // If a title was set before we installed the decor, propagate it now
            //如果在decor安装前有设置title,现在就 把它传进去。
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                onTitleChanged(title);
            }
			//适配尺寸在屏幕上,一些参数的初始化
            applyFixedSizeWindow();
			//空实现
            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            // Invalidate if the panel menu hasn't been created before this.
            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
            // being called in the middle of onCreate or similar.
            // A pending invalidation will typically be resolved before the posted message
            // would run normally in order to satisfy instance state restoration.
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

在上面的方法中,如果SubDecor没有被创建过,就先创建:

//AppCompatDelegateImplV9.java
private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
		//下面省略的代码中,获取了系统的主题。
      ... ...

        // Now let's make sure that the Window has installed its decor by retrieving it
        //保证window是被加载了的
        //在这里也确保DecorView的初始化
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
		//根据属性,创建不同的subDecor
        if (!mWindowNoTitle) {
        //如果Window没有Titile
            if (mIsFloating) {
            //是否悬浮,也就是说该Activity可以使dialog的样子
                // If we're floating, inflate the dialog title decor
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);

                // Floating windows can never have an action bar, reset the flags
                mHasActionBar = mOverlayActionBar = false;
            } else if (mHasActionBar) {
            	//是否有ActionBar
                TypedValue outValue = new TypedValue();
                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

                Context themedContext;
                if (outValue.resourceId != 0) {
                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
                } else {
                    themedContext = mContext;
                }

                // Now inflate the view using the themed context and set it as the content view
                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);

                mDecorContentParent = (DecorContentParent) subDecor
                        .findViewById(R.id.decor_content_parent);
                mDecorContentParent.setWindowCallback(getWindowCallback());

                /**
                 * Propagate features to DecorContentParent
                 */
                if (mOverlayActionBar) {
                    mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
                }
                if (mFeatureProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
                }
                if (mFeatureIndeterminateProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                }
            }
        } else {
            if (mOverlayActionMode) {
            //是叠加模式,用于设置ActionBar的主题
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }
	
            if (Build.VERSION.SDK_INT >= 21) {
                // If we're running on L or above, we can rely on ViewCompat's
                // setOnApplyWindowInsetsListener
                ViewCompat.setOnApplyWindowInsetsListener(subDecor,
                        new OnApplyWindowInsetsListener() {
                            @Override
                            public WindowInsetsCompat onApplyWindowInsets(View v,
                                    WindowInsetsCompat insets) {
                                final int top = insets.getSystemWindowInsetTop();
                                final int newTop = updateStatusGuard(top);

                                if (top != newTop) {
                                    insets = insets.replaceSystemWindowInsets(
                                            insets.getSystemWindowInsetLeft(),
                                            newTop,
                                            insets.getSystemWindowInsetRight(),
                                            insets.getSystemWindowInsetBottom());
                                }

                                // Now apply the insets on our view
                                return ViewCompat.onApplyWindowInsets(v, insets);
                            }
                        });
            } else {
                // Else, we need to use our own FitWindowsViewGroup handling
                ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
                        new FitWindowsViewGroup.OnFitSystemWindowsListener() {
                            @Override
                            public void onFitSystemWindows(Rect insets) {
                                insets.top = updateStatusGuard(insets.top);
                            }
                        });
            }
        }

        if (subDecor == null) {
            throw new IllegalArgumentException(
                    "AppCompat does not support the current theme features: { "
                            + "windowActionBar: " + mHasActionBar
                            + ", windowActionBarOverlay: "+ mOverlayActionBar
                            + ", android:windowIsFloating: " + mIsFloating
                            + ", windowActionModeOverlay: " + mOverlayActionMode
                            + ", windowNoTitle: " + mWindowNoTitle
                            + " }");
        }

        if (mDecorContentParent == null) {
            mTitleView = (TextView) subDecor.findViewById(R.id.title);
        }

        // Make the decor optionally fit system windows, like the window's decor
        ViewUtils.makeOptionalFitsSystemWindows(subDecor);

        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
		//得到Window的ContentView
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);

        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
            @Override
            public void onAttachedFromWindow() {}

            @Override
            public void onDetachedFromWindow() {
                dismissPopups();
            }
        });

        return subDecor;
    }

 @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
        //获取DecorView,然后他是一个FrameLayout
            installDecor();
        }
        return mDecor;
    }

在上面代码中会根据不同的属性,加载不同的subDecor,然后看Window中的contentView中有没有子View,如果有就从contentView中移除并加入到subDecor的contentView中。
然后调用了mWindow.setContentView(subDecor);

//PhoneWindow.java
  @Override
    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // 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)) {
        //FEATURE_CONTENT_TRANSITIONS
        //用于请求窗口内容更改的标志应使用TransitionManager进行动画处理。
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //有动画转场添加
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
        //一般会到这里
        //添加subDecor
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

在上面中,如果mContentParent==null,那么就会调用installDecor()方法,但是在前面有调用PhoneWindow的getDecorView方法中,会调用installDecor方法。mContentParent用来描述一个类型为DecorView的视图对象,或者类型为DecorView的视图对象的一个子视图对象,用作UI容器。当它的值为null的时候,说明正在处理的应用程序创建的视图还没有创建.

我们看看installDecor方法:

//PhoneWindow.java
 // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
     private TextView mTitleView;
    DecorContentParent mDecorContentParent;

// This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    ViewGroup mContentParent;

private void 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);
            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }
                
                ... ...
            } else {
                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);
                    }
                }
            }
            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
            }

            ... ...

        }
    }

由于我们是在Activity启动中分析的,所以在这里我们可以假设mDecor是为null,因此会调用generateDecor()方法来创建DecorView:

 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.
        /*
         *系统是没有上下文的,在这种情况下,我们需要直接使用我们拥有的上下文,
         *否则,我们需要application 的coontext,
         *因此,我们不依赖于activity
        */
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

在上面代码中,我们可以看到,重点就是调用了DecorView的构造方法。
回到PhoneWindow类的installDecor方法,在创建成功DecorView之后,由于mContentParent也是null,所以会调用generateLayout(mDecor)来给mContentParent赋值:

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        ... ...
        // Inflate the window decor.
        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_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } 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();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);


        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks(contentParent);
        }
        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            mDecor.setWindowBackground(background);
            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);
            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);
            if (mTitle != null) {
                setTitle(mTitle);
            }
            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }
        mDecor.finishChanging();
        return contentParent;
    }
    

在这个方法中,会根据当前应用程序创建的Feature来加载对应的窗口布局文件,这些布局文件保存在frameworks/base/core/res/res/layout目录下,它们必须包含一个id值为“content”的布局控件。这个布局控件必须要从ViewGroup继承下来,作为窗口的UI容器。

回到PhoneWindow类中的installDecor方法
generateLayout方法执行完了之后,就将这个id值为“content”的ViewGroup返回给PhoneWinodw类的mContentParent。
之后会检查前面加载的窗口布局文件是否包含一个id值为“titile”的TextView控件,如果包含,就会给mTextView赋值,用来描述当前应用程序的标题栏。但是,如果当前应用程序没有标题栏,即它的Feature位FEATURE_NO_TITLE的值等于1,那么就需要将前面得到的标题栏隐藏起来。

注意:mTitleView所描述的标题栏有可能是包含在一个id值为“title_container”的容器里面的,在这种情况下,就需要隐藏该标题栏容器。另一方面,如果当前应用程序窗口是设置有标题栏的,那么PhoneWindow类中的installDecor方法中就会设置它的标题栏文字。应用程序窗口的标题栏文字保存在PhoneWindow类的成员变量mTitle中,我们可以调用PhoneWindow类的成员函数setTitle来设置。

setContentView()方法中,会将创建的subDecor添加到mContentParent中。

然后回到AppCompatDelegateImplV9的setContentView()方法:

//AppCompatDelegateImplV9.java
 final Window.Callback mOriginalWindowCallback;
@Override
    public void setContentView(View v, ViewGroup.LayoutParams lp) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v, lp);
        mOriginalWindowCallback.onContentChanged();
    }

ensureSubDecor()方法调用完毕后,这个时候,subDecor创建成果,并且创建了DecorVIew,得到了mContentParent,将subDecor添加进去了。之后会通过mSubDecor得到contentParent,将我们自己的布局添加进去。
在这个方法最后,调用了mOriginalWindowCallback.onContentChanged();,mOriginalWindowCallback是什么呢?在AppCompatDelegateImplV9对象创建的时候会调用父类的构造方法:

 AppCompatDelegateImplBase(Context context, Window window, AppCompatCallback callback) {
        mContext = context;
        mWindow = window;
        mAppCompatCallback = callback;
        mOriginalWindowCallback = mWindow.getCallback();
        if (mOriginalWindowCallback instanceof AppCompatWindowCallbackBase) {
            throw new IllegalStateException(
                    "AppCompat has already installed itself into the Window");
        }
        mAppCompatWindowCallback = wrapWindowCallback(mOriginalWindowCallback);
        // Now install the new callback
        mWindow.setCallback(mAppCompatWindowCallback);
        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
                context, null, sWindowBackgroundStyleable);
        final Drawable winBg = a.getDrawableIfKnown(0);
        if (winBg != null) {
            mWindow.setBackgroundDrawable(winBg);
        }
        a.recycle();
    }

可是看到,mOriginalWindowCallback = mWindow.getCallback();,所以对于现在分析的Activity启动流程来说,mOriginalWindowCallback 就是我们正在启动的Activity。
所以最后是调用它的方法来通知content改变了。

这一步执行完成之后,应用程序窗口视图就创建完成了。

然后我们回到ActivityThread中,在performLaunchActivity方法中,调用完Activity.onCreate方法后,我们知道,肯定开始Activity的生命周期,我们继续看看handleResumeActivity()方法

//ActivityThread.java
final void handleResumeActivity(IBinder token,
                                    boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
       ... ...
        // TODO Push resumeArgs into the activity for consideration
        //调用Activity的onResume方法
        //返回一个ActivityClientRecord对象,
        //它的一个成员变量activity描述的就是正在激活的Activity
        r = performResumeActivity(token, clearHide, reason);
        if (r != null) {
            final Activity a = r.activity;
            ... ...
            //是否可见
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManager.getService().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            //在Activity初始化后,并没有给r.window赋值
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                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 (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    } else {
                     ... ...
                        a.onWindowAttributesChanged(l);
                    }
                }
                // If the window has already been added, but during resume
                // we started another activity, then don't yet make the
                // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                        TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }
            
           ... ...
            
        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManager.getService()
                        .finishActivity(token, Activity.RESULT_CANCELED, null,
                                Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

Activity类中有一个成员变量mStartedActivity用来描述一个Activity是否正在启动另外一个Activity,并且等待这个新的Activity的执行结果。如果正在启动,那么mStartedActivity就等于ture,表示在新的activity组件的执行结果返回之前,当前Activity要保持不可见的状态。
如果Activity的成员变量mStartedActivity等于ture,它接下来的状态是保持不可见,但是有可能它所启动的Activity的UI不是全屏的。在这种情况下,Activity的UI仍是部分可见的,这时候也要将变量willBeVisible的值设置为true。因此,如果前面得到变量willBeVidible的值等于false,那么在handleResumeActivity方法中,就会通过Binder进程间通信机制来调用ActivityManagerService的willActivityBeVisible方法来检查在该Activity上面的其他Activity是否是全屏的。如果不是,那么willActivityBeVisible()方法就会返回ture,表明需要显示该Activity。

接下来,如果r.window == null,表示当前正在激活的Activity所关联的应用程序窗口对象还没有关联到一个ViewRoot对象,进一步,如果该Activity还活着,并且接下来是可见的:
(r.window == null && !a.mFinished && willBeVisible)
那么这时候就说明需要为与该Activity所关联的应用程序窗口对象关联一个ViewRootImpl对象。
将一个视图对象与一个ViewRootImpl对象关联是通过WindowManager来执行的。

 if (r.window == null && !a.mFinished && willBeVisible) {
				//获取应用程序窗口
                r.window = r.activity.getWindow();
                //获取应用程序视图
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                //得到WinodwManager
                ViewManager wm = a.getWindowManager();
                //获取WindowManager.LayoutParams的对象
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                ... ...
                //当前正在激活的Activity在本地进程中是否可见
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        //关联应用程序窗口视图对象和ViewRootImpl
                        wm.addView(decor, l);
                    } else {
                     ... ...
                        a.onWindowAttributesChanged(l);
                    }
                }
               
            }

看了上面的代码,我们知道wm是WindowManagerImpl类型的:


public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

继续看:


    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

/*
*参数view和参数params描述的就是要关联的view对象和LayoutParams对象
*/

 public void addView(View view, ViewGroup.LayoutParams params,
                        Display display, Window parentWindow) {
       ... ...
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ... ...
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
            //检查参数view所描述的一个view对象是否在mViews中,
            //如果存在,就说明该view已经关联过ViewRoot对象和LayoutParams对象了。
            //如果存在,则让它死亡
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }
            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            //检查view是否描述的是一个子应用程序的视图对象时,也就是满足下面这个条件
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                    //找到这个子视图对象的父视图对象 panelParentView
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                //wparams.token指向了一个类型为W的Binder本地对象的一个IBinder接口,
                //用来描述参数view所描述的子应用程序视图对象所属的父应用程序窗口视图对象 
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
            //创建ViewRootImpl对象
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            //并分别保存在list中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

通过上面的代码,我们就知道WindowManagerImpl是怎么关联一个应用程序窗口视图对象(DecorView)和ViewRootImpl对象。
一个View对象在于ViewRootImpl对象关联的同时,会关联一个WindowManager.LayoutParams对象,这个LayoutParams对象是用来描述应用程序窗口视图的布局属性的。
WindowMamagerImpl类中的三个成员变量:mViews,mRoots和mParams分别存储View,ViewRootImpl,LayoutParams对象,这三个list大小适中保持相等,也就是说相同位置上的View,ViewRootImpl,LayoutParams是有关联的。这样就关联好了。
代码的最后会调用:root.setView(view, wparams, panelParentView);

在看这个之前,先看一下ViewRootImpl的介绍:

The top of a view hierarchy, implementing the needed protocol between View and the WindowManager

视图层次结构的顶部,实现了View和WindowManager之间需要的协议。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks 

那么我们看它的setView方法把:

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
            //保存view
                mView = view;
                mAttachInfo.mDisplayState = mDisplay.getState();
                ... ...
                mWindowAttributes.copyFrom(attrs);
                if (mWindowAttributes.packageName == null) {
                    mWindowAttributes.packageName = mBasePackageName;
                }
                attrs = mWindowAttributes;
                setTag();
                ... ...
                // Keep track of the actual window flags supplied by the client.
                mClientWindowLayoutFlags = attrs.flags;
                setAccessibilityFocus(null, null);
                if (view instanceof RootViewSurfaceTaker) {
                    mSurfaceHolderCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                    if (mSurfaceHolderCallback != null) {
                        mSurfaceHolder = new TakenSurfaceHolder();
                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                        mSurfaceHolder.addCallback(mSurfaceHolderCallback);
                    }
                }
                // Compute surface insets required to draw at specified Z value.
                // TODO: Use real shadow insets for a constant max Z.
               ... ...

                boolean restore = false;

                mSoftInputMode = attrs.softInputMode;
                mWindowAttributesChanged = true;
                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
                //保存view
                mAttachInfo.mRootView = view;
                mAttachInfo.mScalingRequired = mTranslator != null;
                mAttachInfo.mApplicationScale =
                        mTranslator == null ? 1.0f : mTranslator.applicationScale;
                if (panelParentView != null) {
                //如果panelParentView!=null,就保存父应用程序窗口视图对象
                    mAttachInfo.mPanelParentWindowToken
                            = panelParentView.getApplicationWindowToken();
                }
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //开始第一次布局
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //增加一个WindowSate对象
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                ... ...
            }
        }
    }

参数view所描述的一个View对象会分别保存在ViewRootIml类中的mView和mAttachInfo中的mRootView中,而参数attrs所描述的一个WindowManager.LayoutParams对象的内容会被拷贝到ViewRootImpl类中的mWinodwAttributes中。
当参数panelParentView的值不等于null的时候,就表示参数view描述的是一个子应用程序窗口视图对象,在这种情况下,panelParentView描述的就是一个父应用程序窗口视图对象,这个时候,我们就需要获取用来描述父应用程序窗口视图对象的一个类型为W的Binder本地对象的IBinder接口,保存在mAttachInfo中的mPanelParentWindowToken中,这样以后就可以知道ViewRootImpl中的mView所描述的子应用程序窗口视图的父应用程序窗口视图。
在处理完上面的步骤后,mAdd的值就为true了, 表示当前正在处理的ViewRooImpl对象已经关联好了一个View对象了。
接下来,ViewRootImpl中的setView方法还需要执行两个操作:
第一个是: requestLayout();,请求对应用程序窗口的UI做第一次布局。
第二个是:

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);

mWindowSession是在ViewRootImpl构造方法中被初始化的:

mWindowSession = WindowManagerGlobal.getWindowSession();
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
            context);
//WindowManagerGlobal.java
 public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

调用mWindowSession.addToDisplay方法,来请求WindowManagerService增加一个WindowState对象,返回的res 可以用来描述当前正在处理的一个ViewRootImpl所关联的一个应用程序窗口。

到这里,就分析完了:

总结:
在这里插入图片描述

参考:https://www.kancloud.cn/alex_wsc/androids/473772

猜你喜欢

转载自blog.csdn.net/qq_36391075/article/details/83513559