1. 概述
WMS架构.png-
窗口管理:
窗口的启动、添加和删除,窗口的大小和层级 -
窗口动画
WindowAnimator负责窗口间切换动画等 -
输入系统中转站
InputManagerService会对触摸事件进行处理,查找最合适的窗口来处理触摸反馈信息 -
Surface
为每个窗口分配Surface实现绘制动作
2. 源码架构
image.png3. 源码分析
以上会议当前最新的Android Q ,1.0 源码进行分析
3.1 Window 与 Activity 之间的关系
一个Activity 拥有至少一个Window 对象,创建Activity时,附带会创建
Window对象,且Activity 默认实现了Window的一些回调接口。
具体的绑定过程,在Activity的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,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// 1. 创建Window 对象,实际为 PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
// 2. 设置Callback
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
//省略无关代码
...
// 3. 设置WindowManager,真实实现类为WindowManagerImpl
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;
mWindow.setColorMode(info.colorMode);
setAutofillOptions(application.getAutofillOptions());
setContentCaptureOptions(application.getContentCaptureOptions());
}
Window 的callback 回调是指 dispatchTouchEvent,onCreatePanelMenu之类的触摸 & 键盘操作事件回调。
WindowManagerImpl 是WindowManager的具体实现类。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerImpl通过全局的mGlobal
3.2 Activity 添加Window 过程
Activity 的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.
// 代码 1
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 {
// 代码 2
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
// 代码 3
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
代码1 : 判断 mContentParent 是否为null,是的话,调用installDecor()
mContentParent 为 ViewGroup ,典型的组合模式。
// 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.
代码2:通过LayoutInflater 解析layoutResID并放入mContentParent
代码3:设置Callback监听事件, Activity会在#attach 方法中设置 Callback 回调对象。
installDecor()方法比较长,重点是generateLayout() 生成mContentParent对象。
private void installDecor() {
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
generateLayout()方法:
protected ViewGroup generateLayout(DecorView decor) {
.....
.....
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
// 代码1
layoutResource = R.layout.screen_title_icons;
}
.....
.....
// 代码2
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) {
mDecor.setWindowBackground(mBackgroundDrawable);
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;
}
代码1: screen_title_icons 为常见的默认布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:fitsSystemWindows="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Popout bar for action modes -->
<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"/>
<RelativeLayout android:id="@android:id/title_container"
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize">
<!-- The title background has 9px left padding. -->
<ImageView android:id="@android:id/left_icon"
android:visibility="gone"
android:layout_marginEnd="9dip"
android:layout_width="16dip"
android:layout_height="16dip"
android:scaleType="fitCenter"
android:layout_alignParentStart="true"
android:layout_centerVertical="true" />
<ProgressBar android:id="@+id/progress_circular"
style="?android:attr/progressBarStyleSmallTitle"
android:visibility="gone"
android:max="10000"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="6dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- There are 6dip between this and the circular progress on the right, we
also make 6dip (with the -3dip margin_left) to the icon on the left or
the screen left edge if no icon. This also places our left edge 3dip to
the left of the title text left edge. -->
<ProgressBar android:id="@+id/progress_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-3dip"
android:layout_toStartOf="@android:id/progress_circular"
android:layout_toEndOf="@android:id/left_icon"
android:layout_centerVertical="true"
android:visibility="gone"
android:max="10000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_toStartOf="@id/progress_circular"
android:layout_toEndOf="@android:id/left_icon"
>
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:fadingEdge="horizontal"
android:scrollHorizontally="true"
android:gravity="center_vertical"
android:layout_marginEnd="2dip"
/>
<!-- 2dip between the icon and the title text, if icon is present. -->
<ImageView android:id="@android:id/right_icon"
android:visibility="gone"
android:layout_width="16dip"
android:layout_height="16dip"
android:layout_weight="0"
android:layout_gravity="center_vertical"
android:scaleType="fitCenter"
/>
</LinearLayout>
</RelativeLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
代码2 中的 ID_ANDROID_CONTENT,即
/**
* The ID that the main layout in the XML layout file should have.
*/
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
就是上面布局中的
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
3.3 将DecorView添加到PhoneView
ActivityThread 经过handleLaunchActivity 执行了oncreate之后,调用handleResumeActivity
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
....
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 (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 代码1
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);
}
}
代码1:WindowManager 添加addView 到Activity 中。
WindowManagerImpl 中
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal 中
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
....
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 代码1
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
....
代码1 :将 DecorView、ViewRootImple、WindowManager.LayoutParams 添加到对应的 List 中
网友评论