Android View 系统 2 - View树与Window关联

View树与Window关联

前面介绍的是View树的创建,但是创建好的View树只有添加到一个Window里才会显示到屏幕上,因为WindowManagerService服务会为每一个Window创建一块Surface作为画布,View树里所有的View都会绘画到这块Surface上,最后SurfaceFlinger服务会将所有Window创建的的Surface传递给GPU,最终显示到屏幕上。

创建Window

通过WindowManager.addView接口就可以申请创建一个新的Window并添加一个View树,弹出菜单、浮动窗口等自定义的窗口都是通过这个接口显示出来的:

//获得WindowManager服务
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

//设置Window参数
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.setTitle("FloatWindow");

//创建View树
View rootView = getLayoutInflater().inflate(R.layout.float_layout, null);

//创建Window并关联View树
windowManager.addView(rootView, params);

应用中使用的WindowManager其实是一个WindowManagerImpl类的实例:

frameworks/base/core/java/android/view/WindowManagerImpl.java

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    ...
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

如上面的代码,WindowManagerImpl.addView()实现会调用WindowManagerGlobal.addView(),实际的请求创建Window的逻辑也在这个类里实现:

frameworks/base/core/java/android/view/WindowManagerGlobal.java

public final class WindowManagerGlobal {
    private final ArrayList mViews = new ArrayList();
    private final ArrayList mRoots = new ArrayList();
    private final ArrayList mParams =
            new ArrayList();
    ...
    public void addView(View view, ViewGroup.LayoutParams params,
                        Display display, Window parentWindow) {
        ...
        ViewRootImpl root;

        synchronized (mLock) {
            ...
            root = new ViewRootImpl(view.getContext(), display);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(params);
            try {
                root.setView(view, params, panelParentView);
            } catch (RuntimeException e) {
                throw e;
            }
        }
    }
}

WindowManagerGlobal 类里有这三个ArrayList列表:

  • mViews 保存着每个Window的根View
  • mRoots 保存每个根View对应的ViewRootImpl
  • mParams 保存的是每个Window的窗口配置参数WindowManager.LayoutParams

WindowManagerGlobal.addView()的实现里会为每一个Window创建一个ViewRootImpl实例,以及窗口配置参数WindowManager.LayoutParams实例,并将传递过来的根View实例保存在上面提到的三个列表里。addView()实现里最后调用的root.setView()就是通过ViewRootImpl将Window与View树关联起来,同时ViewRootImpl还有控制View树的刷新、显示以及输入事件分发的作用。

创建Activity主界面

Android 系统中的Activity组件也是通过View树与Window显示出来的,只是将创建View树与Window的逻辑封装到了生命周期的处理流程里了。
所有Activity的窗口都是用一个PhoneWindow类的实例表示的,每个PhoneWindow都会创建一个内部类DecorView作为这个窗口中View树的根View。下面的结构图表示的就是ActivityPhoneWindow以及DecorView之间的关系,下面我们通过这个结构图结合相关代码来研究一下Activity主界面的View树的创建过程。

  • 应用里Activity一般会在onCreate里通过调用 setContentView()设置自己主界面的View树布局,如下面的代码:
public class MainActivity extends Activity {

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

setContentView()有多个重载实现,既可以传递布局资源ID也可以直接传递创建好的View。
Activity里会将setContentView()设置过来的View传递给PhoneWindow

frameworks/base/core/java/android/app/Activity.java

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

getWindow()返回的就是PhoneWindow的实例。

  • PhoneWindow里的setContentView实现如下面的代码:
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java

private DecorView mDecor;
private ViewGroup mContentParent;
public void setContentView(int layoutResID) {
    ...
    if (mContentParent == null) {
        installDecor();
    } else {
        mContentParent.removeAllViews();
    }
    mLayoutInflater.inflate(layoutResID, mContentParent);
    ...
}

mContentParent是用作为ContentView的父View的,setContentView()设置过来的Layout 资源ID通过LayoutInflater解析后会被添加到mContentParent的子View树里

  • 如果这个Activity是第一次启动,那么mContentParent应该是null的,需要在installDecor()里初始化所有的装饰View:
private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();
        ...
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
    }
}
  • generateDecor()的作用是创建出DecorView的实例,如下面:
protected DecorView generateDecor() {
    ...
    return new DecorView(getContext(), -1);
}
  • generateLayout()的作用是根据Activity设置的各种feature、Theme创建ContentParentActionBar等Activity的基础视图框架,如下面:
protected ViewGroup generateLayout(DecorView decor) {
    ...
    // Inflate the window decor.
    int layoutResource;
    ...
    mDecor.startChanging();

    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);
    ...
    mDecor.finishChanging();

    return contentParent;
}

如果Activity设置了需要ActionBar,那么就会加载有ActionBar的布局资源:

if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
    layoutResource = R.layout.screen_action_bar;
} else {
    layoutResource = R.layout.screen_title;
}

ActionBar的界面会使用R.layout.screen_action_bar布局资源,没有ActionBar会使用R.layout.screen_title(5.0引入Material主题后有新引入了一个R.layout.screen_toolbar):

frameworks/base/core/res/res/layout/screen_action_bar.xml

<com.android.internal.widget.ActionBarOverlayLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/decor_content_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:splitMotionEvents="false"
    android:theme="?attr/actionBarTheme">
    <FrameLayout android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
    <com.android.internal.widget.ActionBarContainer
        android:id="@+id/action_bar_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        style="?attr/actionBarStyle"
        android:transitionName="android:action_bar"
        android:touchscreenBlocksFocus="true"
        android:gravity="top">
        ......
com.android.internal.widget.ActionBarContainer>
......
com.android.internal.widget.ActionBarOverlayLayout>

contentParent会取其中ID为R.id.content的View,应用的Activity里设置的ContentView会被添加到contentParent的子View里

上面是Activity的View树的创建过程,而Activity的Window需要等到Activity resume的时候才会创建,同样也是调用WindowManager.addView接口,而根View就是前面创建的DecorView

frameworks/base/core/java/android/app/ActivityThread.java

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume) {
    ...
    ActivityClientRecord r = performResumeActivity(token, clearHide);

    if (r != null) {
        final Activity a = r.activity;
        ...
        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;
            ...
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }
        }
    } 
}

你可能感兴趣的:(Android)