其他
通俗易懂,Android视图系统的设计与实现
https://juejin.cn/user/2629687546479742
ActivityManagerService(AMS):用来创建应用进程(通过socket ipc通知zygote进程)、管理四大组件 WindowManagerService(WMS):用来开辟和管理屏幕上的窗口,让视图有条不紊的显示 InputManagerService(IMS):用来处理和分发各种事件 等等...
当点击一个App图标时,如果对应的应用进程还没有创建则会通过Binder IPC通知到AMS创建应用进程 应用进程启动后会执行我们所熟悉的main方法,而这个main方法则位于ActivityThread这个类中,main方法对应的就是Android主线程 ActivityThread的main方法首先会调用Looper.loop(),用来循环处理主线程Hanlder分发的消息。 接下来的main方法会发送一个BIND_APPLICATION的消息,Looper收到后会通过Binder IPC通知AMS创建App进程对应的Application Application创建后会再次通过Binder IPC通知AMS要创建Activity,AMS验证后会回到App进程, 回到App进程后会间接调用ActivityThread#performLaunchActivity()来真正启动创建Activity,并且执行attach()和onCreate()。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
//注释1
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
if (activity != null) {
...
//注释2.
activity.attach(...);
...
//注释3.
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
...
return activity;
}
final void attach(...){
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
mWindow.setWindowManager(...);
mWindowManager = mWindow.getWindowManager();
...
}
public void setWindowManager(...) {
...
if (wm == null) {
//注释1
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//注释2
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
...
}
public Window getWindow() {
return mWindow;
}
ViewGroup mContentParent;
public void setContentView(int layoutResID) {
//注释1
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
//注释2
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
public void handleResumeActivity(...) {
//注释1
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
final Activity a = r.activity;
...
//注释2
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
...
//注释3
wm.addView(decor, l);
...
}
注释1处 会间接调用Activity的onResume方法 注释2处 通过Activity获取PhoneWindow、DecorView、WindowManager,它们的创建时机前面小结有写,忘记的可以回翻阅读。 注释3处 调用了WindowManager的addView方法,顾名思义就是将DecorView添加至Window当中,这一步非常关键
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
public void addView(...) {
...
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId());
...
}
WindowManager提供的功能全局通用不会与某个View/Window单独绑定,为了节省内存理应设计出一个单例。 WindowManagerImp具备多个职责如Token管理、WindowManager功能等,所以通过单一设计原则将WindowManager功能拆分到另一个类中即WindowManagerGlobal,并将其定义为单例。 为了不违背迪米特法则又通过组合模式将WindowManagerGlobal屏蔽在内部。
/**
* 用来存储所有的DecorView
*/
private final ArrayList<View> mViews = new ArrayList<View>();
/**
* 用来存储所有的ViewRootImpl
*/
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
/**
* 用来存储所有的LayoutParams
*/
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
public void addView(...) {
...
ViewRootImpl root;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
...
root.setView(view, wparams, panelParentView, userId);
...
}
}
public void setView(...) {
synchronized (this) {
if (mView == null) {
...
mView = view;
...
//注释1
requestLayout();
//注释2
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
...
//注释3
view.assignParent(this);
}
}
}
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
}
...
}
注释1,requestLayout()通过一系列调用链最终会开启mView(DecorView)的绘制(measure、layout、draw)。这一流程很复杂,由于篇幅原因本文就不提了,感兴趣的可查阅Choreographer相关知识 注释2,mWindowSession是一个IWindowSession类型的AIDL文件,它会通过Binder IPC通知WMS在屏幕上开辟一个窗口,关于WMS的实现流程也非常庞大,我们点到为止。这一步执行完我们的View就可以显示到屏幕上了 注释3,最后一步执行了View#assignParent,内部将mParent设置为ViewRootImpl。所以,虽然ViewRootImpl不是一个View,但它是所有View的顶层Parent
Window是一个抽象类,通过控制DecorView提供了一些标准的UI方案,比如背景、标题、虚拟按键等 PhoneWindow是Window的唯一实现类,完善了Window的功能,并提供了事件的中转 WindowManager是一个接口,继承自ViewManager接口,提供了View的基本操作方法 WindowManagerImp实现了WindowManager接口,内部通过组合方式持有WindowManagerGlobal,用来操作View WindowManagerGlobal是一个全局单例,内部可以通过ViewRootImpl将View添加至窗口中 ViewRootImpl是所有View的Parent,用来管理View的绘制以及窗口的开辟 IWindowSession是IWindowSession类型的AIDL接口,可以通过Binder IPC通知WMS开辟窗口
一切视图均由Canvas而来 View的出现是为了提供视图模板,用来提升开发效率 窗口可以让View有条不紊的显示 Activity给每个窗口增加生命周期,让窗口切换更加优雅 PhoneWindow只是提供些标准的UI方案,与窗口不等价 可通过WindowManager将View添加到窗口 ViewRootImpl才是开辟窗口的那个角色,并管理View的绘制,是视图系统最关键的一环 错综复杂的视图系统基本都隐藏Activity内部,开发者只需基于模板方法即可开发