本文正在参与「金石方案 . 分割6万现金大奖」
🔥 Hi,我是小余。本文已收录到 GitHub · Androider-Planet 中。这儿有 Android 进阶生长常识体系,重视大众号 [小余的自习室] ,在成功的路上不走失!
前语
咱们好,由于作业和面试需求,笔者结合大佬们的经验以及自身对源码理解,写了一篇关于Android framework层:WindowManager体系的解说。
本篇是Android framework的第一讲《WindowManager体系-上》,重在解说Window在被增加到WindowManagerService前的流程。
1.Android WindowManager体系
Android中的窗口体系能够分为下面三部分:
-
1.Window:能够简单理解为一个窗口,这个窗口承载了需求制作的View,他是一个抽象类,详细完成类是
PhoneWindow
。 -
2.WindowManager:也是一个接口类,承继
ViewManager
,用来对Window进行办理,
完成类:WindowManagerImpl
,其对Window的操作详细是经过WMS来完成的。理解为一个app层面的WMS和Window的中间人。 -
3.WindowManagerService(AMS):是体系服务中的一重要成员,WindowManager和WMS经过binder进行通讯,真正对Window增加,更新,删除等操作的执行者。
三者之间联络:
前面剖析了窗口体系中的类联络,下面咱们从源码视点和剖析下:
为了咱们不会走失在源码中,笔者会根据面试中或许被问到的几个问题出发,有目的性的介绍
。
- 问题1:Window和Activity以及WindowManager什么时候树立的联络?
- 问题2:Window什么时候和View进行相关?
- 问题3:Window有哪些特点?类型?层级联络?z-order?Window标志?软键盘方式都了解么?
- 问题4:View是怎么一步一步增加到屏幕上的?更新?删除呢?
那么就开始咱们的源码(mo)遨(yu)游吧。
1.Window和Activity以及WindowManager什么时候树立的联络?
前面几篇文章咱们剖析过:Activity在发动的进程中,会调用它的attach办法进行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) {
attachBaseContext(context);
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);//1
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//2
}
先看attach注释1处:创立了一个PhoneWindow,并传入当时Activity的this目标,
PhoneWindow类,承继了Window
public class PhoneWindow extends Window {
public PhoneWindow(Context context, Window preservedWindow,
ActivityConfigCallback activityConfigCallback) {
//这儿终究会调父类Window的结构办法,并传入Activity的实例context。
this(context);
//只有Activity的Window才会运用Decor context,其他依靠运用的Context
mUseDecorContext = true;
...
}
}
Window结构办法:
public Window(Context context) {
mContext = context;
mFeatures = mLocalFeatures = getDefaultFeatures(context);
}
PhoneWindow的结构办法中传入当时Activity目标,
并终究在父类Window结构办法中给mContext赋值,Window得到Activity的引证,由此就树立了Window和Activity的联络。
下面持续看attach注释2:mWindow.setWindowManager
setWindowManager在PhoneWindow的父类Window中完成:
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
//假如wm是空,则创立一个WindowManager服务:注释1
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//注释2
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
进入setWindowManager的注释1:
wm =(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
这儿的mContext是ContextImpl类的目标:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
进入SystemServiceRegistry的getSystemService办法,name = Context.WINDOW_SERVICE
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);//1
return fetcher != null ? fetcher.getService(ctx) : null;//2
}
先看getSystemService办法的注释1:
SYSTEM_SERVICE_FETCHERS是一个HashMap的调集,在哪里初始化的?
咱们注意到SystemServiceRegistry的static 办法,这个办法内部注册了许多服务,咱们能够在办法里边找到name = Context.WINDOW_SERVICE的服务
PS:static办法是在类加载的时候进行初始化的,所以在整个类运用进程中只会执行一次。
static {
...
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
}
来看下registerService办法:
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher)
第三个参数是ServiceFetcher类型目标,完成的createService办法回来了一个WindowManagerImpl目标。
所以getSystemService办法的注释1:fetcher = CachedServiceFetcher目标,且其createService办法回来了一个WindowManagerImpl目标
回到getSystemService注释2处:fetcher.getService(ctx)终究会回来调用到createService方,回来WindowManagerImpl目标。
持续回到Window的setWindowManager办法的注释2处:
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this),
进入WindowManagerImpl的createLocalWindowManager办法:
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
这儿也是创立了一个WindowManagerImpl,和前面getSystemService创立的WindowManagerImpl区别:
这儿传入了parentWindow,让WindowManagerImpl持有了Window的引证,能够对此Window进行操作了。
总结下Activity的attach办法
:
- 1.new一个PhoneWindow目标并传入当时Activity引证,树立Window和Activity的一一对应联络。此Window是Window类的子类PhoneWindow的实例。Activity在Window中是以mContext特点存在。
- 2.调用PhoneWindow的setWindowManager办法,在这个办法中让Window和WindowManager树立了一一联络。此WindowManager是WindowManagerImpl的实例、
笔者画了一张Window联络类图:
2.Window什么时候和View进行相关?
咱们来看Activity的setContentView办法
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);//1
initWindowDecorActionBar();//2
}
注释1中的getWindow前面剖析了是:PhoneWindow目标
进入PhoneWindow的setContentView办法:
public void setContentView(int layoutResID) {
...
if (mContentParent == null) {
installDecor();//注释1
}
mLayoutInflater.inflate(layoutResID, mContentParent);//注释2
...
}
private void installDecor() {
...
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
}
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
PhoneWindow:setContentView注释1处:创立一个DecorView给当时Window的mDecor。
PhoneWindow:setContentView注释2处:解析当时layoutResID为id的xml文件并传递给mContentParent。
这儿的mContentParent其实便是当时Window对应的ContentView。
持续回到Activity的setContentView办法注释2处:initWindowDecorActionBar这儿是初始化Window的ActionBar
private void initWindowDecorActionBar() {
Window window = getWindow();
// Initializing the window decor can change window feature flags.
// Make sure that we have the correct set before performing the test below.
window.getDecorView();
if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
mActionBar = new WindowDecorActionBar(this);
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
...
}
经过上面两步骤:咱们就给当时Activity的Window创立了一个DecorView,这个DecorView便是当时Window的rootView、并对rootView的ContentView设置了mContentParent
完成了将Window和DecorView绑定。
假如你够细心
,你会发现Activity一切的对View进行的操作几乎都是经过Window进行的。而咱们的Activity又是经过AMS来操控生命周期,所以AMS和View的通讯其实便是经过WindowManager这个介子进行的、
比方:
1.设置主题
public void setTheme(int resid) {
super.setTheme(resid);
mWindow.setTheme(resid);
}
2.设置Window特点等
public final boolean requestWindowFeature(int featureId) {
return getWindow().requestFeature(featureId);
}
3.获取Layout解析器
public LayoutInflater getLayoutInflater() {
return getWindow().getLayoutInflater();
}
3.Window有哪些特点?类型?层级联络?z-order?Window标志?软键盘方式都了解么
上面咱们说过了Window、WindowManager和WMS之间的体系联络,WMS就比方老板,Window就比方职工,老板为了便利办理职工,就给职工定了许多规矩.
这些规矩便是Window的特点:这些特点都界说在WindowManager.LayoutParams类中。
运用开发中比较常见的,主要有以下几类:
- 1.Window窗口类型(Type)
- 2.Window窗口层级联络(Z-Order)
- 3.Window窗口标志(Flag)
- 4.Window 软键盘方式(SoftInputModel)
下面咱们就别离来解说下这几种特点
1.Window窗口类型(Type)
Window窗口类型有许多种:比方:运用程序窗口,PopupWindow,Toast,Dialog,输入法窗口,体系过错窗口等。全体来说主要分为以下三大类:
- 1.运用程序窗口(Application Window)
- 2.子窗口(Sub Window)
- 3.体系窗口(System Window)
窗口类型在WindowManager的静态内部类LayoutParams中界说
运用程序窗口
运用程序窗口指咱们运用程序运用的(非Dialog)界面Window,如Activity便是一个典型的运用窗口。
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//运用程序窗口的起始值
public static final int FIRST_APPLICATION_WINDOW = 1;
//运用程序窗口的基准值
public static final int TYPE_BASE_APPLICATION = 1;
//规范的运用程序窗口:依附于Activity存在
public static final int TYPE_APPLICATION = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
//运用程序窗口完毕值
public static final int LAST_APPLICATION_WINDOW = 99;
...
}
运用程序窗口起始值为1,完毕值为99,阐明咱们运用程序窗口的类型为:1~99
.
这个值直接影响了窗口在屏幕中的层级联络,后边再讲。
子窗口
子窗口看姓名就知道其是一个窗口的子窗口,所以不能独立存在,需求依附于父窗口存在,比方PopupWindow,Dialog等就归于子窗口,子窗口的类型界说如下所示:
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
public static final int FIRST_SUB_WINDOW = 1000;//子窗口类型初始值
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;//子窗口类型完毕值
...
}
子窗口的Type值规模为1000到1999
.
体系窗口
输入法窗口,体系过错提示窗口,Toast等都归于体系窗口,体系窗口的类型界说如下所示:
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
public static final int FIRST_SYSTEM_WINDOW = 2000;//体系窗口类型初始值
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//体系状况栏窗口
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//搜索条窗口
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//通话窗口
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//体系ALERT窗口
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//锁屏窗口
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//TOAST窗口
...
public static final int LAST_SYSTEM_WINDOW = 2999;//体系窗口类型完毕值
}
体系窗口的Type值规模为2000到2999
。
关于不同类型的窗口增加进程会有所不同,可是关于WMS处理部分,增加的进程基本上是一样的, WMS关于这三大类的窗口基本是“一视同仁”的。
2.Window窗口层级联络(Z-Order)
首要咱们得有个概念便是窗口是叠加的,能够简单理解为:几页纸按前后次序叠加在一块。
咱们能够将手机屏幕运用X,Y,Z轴来表明,X,Y表明屏幕的长和宽,而Z轴笔直于屏幕,从屏幕内指向外。
确认窗口的层级联络其实便是确认窗口在Z轴上的方位,这个层级联络便是Z-Order,而Z-Order的排序根据便是前面剖析的Window类型值。
类型值越大,越靠近用户。经过上面剖析可知:体系窗口>子窗口>运用程序窗口。
这也便是体系窗口会掩盖运用窗口最直接的原因。
那么假如多个窗口都是运用程序窗口怎么显现呢?
WMS会结合实际情况,去显现窗口合适的层级。
3.Window窗口标志(Flag)
Window Flag用于操控窗口的一些事情:如是否接纳触屏事情,窗口显现时是否答应锁屏,窗口可见时屏幕常亮,躲藏窗口等。
这儿咱们列出几个常用的标志:
- 1.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
窗口可见时,答应在此窗口锁屏,一般需求结合FLAG_KEEP_SCREEN_ON和FLAG_SHOW_WHEN_LOCKED运用
- 2.FLAG_DIM_BEHIND
该窗口后边的画面会变含糊,一般会有一个含糊值dimAmount。
- 3.FLAG_NOT_FOCUSABLE
窗口不能获取焦点,设置这个Flag的同时FLAG_NOT_TOUCH_MODAL也会被设置
- 4.FLAG_NOT_TOUCHABLE
该窗口获取不到输入事情,不可点击状况。
- 5.FLAG_NOT_TOUCH_MODAL
在该窗口区域外的触摸事情传递给其他的Window,而自己只会处理窗口区域内的触摸事情
- 6.FLAG_KEEP_SCREEN_ON
只需窗口可见,屏幕就会一向亮着
- 7.FLAG_LAYOUT_NO_LIMITS
答应窗口超过屏幕之外
- 8.FLAG_FULLSCREEN
躲藏一切的屏幕装饰窗口,比方在游戏、播放器中的全屏显现
- 9.FLAG_SHOW_WHEN_LOCKED
窗口能够在锁屏的窗口之上显现
- 10.FLAG_IGNORE_CHEEK_PRESSES
当用户的脸靠近屏幕时(比方打电话),不会去响应此事情
- 11.FLAG_TURN_SCREEN_ON
窗口显现时将屏幕点亮
- 12.FLAG_DISMISS_KEYGUARD
窗口显现时,键盘会封闭
窗口标志Flag的2种设置办法:
- 1.经过Window的addFlags办法
Window mWindow =getWindow();
mWindow.addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- 2.经过Window的setFlags办法
mWindow.setFlags(LayoutParams.FLAG_KEEP_SCREEN_ON,LayoutParams.FLAG_KEEP_SCREEN_ON);
addFlags办法内部也是调用setFlags
- 3.给LayoutParams设置Flag,并经过WindowManager的addView办法进行增加
WindowManager.LayoutParams wl = new WindowManager.LayoutParams();
wl.flags = LayoutParams.FLAG_KEEP_SCREEN_ON;
Log.d("TAG","window:"+window.getWindowManager());
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
TextView mTextView=new TextView(this);
mTextView.setText("ttttttttdddddd");
Log.d("TAG","mWindow:"+mWindowManager);
mWindowManager.addView(mTextView,wl);
4.Window 软键盘方式(SoftInputModel)
咱们的软键盘也是窗口的一种,归于体系窗口,层级较高,所以在一些场景下会掩盖层级较低的运用窗口。
比方咱们的登录场景,假如键盘弹出窗口处理欠好,会掩盖输入登录按钮等,这样用户体会会十分糟糕。
所以WindowManager给咱们供给了关于软键盘方式的Window窗口处理办法:
- 1.SOFT_INPUT_STATE_UNSPECIFIED
没有指定状况,体系会选择一个合适的状况或依靠于主题的设置
- 2.SOFT_INPUT_STATE_UNCHANGED
不会改变软键盘状况
- 3.SOFT_INPUT_STATE_HIDDEN
当用户进入该窗口时,软键盘默许躲藏
- 4.SOFT_INPUT_STATE_ALWAYS_HIDDEN
当窗口获取焦点时,软键盘总是被躲藏
- 5.SOFT_INPUT_ADJUST_RESIZE
当软键盘弹出时,窗口会调整大小
- 6.SOFT_INPUT_ADJUST_PAN
当软键盘弹出时,窗口不需求调整大小,要保证输入焦点是可见的
软键盘方式设置办法:getWindow().setSoftInputMode
到这儿,咱们现已将Window的特点:窗口类型(Type)
,窗口层级(Z-Order)
,窗口标志(Flag)
,软键盘方式(softInputModel)
做了一个体系的解说
接下来咱们来剖析咱们的View是怎么显现到屏幕上:
4.View是怎么一步一步增加到屏幕上的?更新?删除呢?
经过前面咱们对WindowManager体系剖析知道,咱们屏幕中一切的View首要需求经过WindowManager的处理,终究提交给WMS来处理。
咱们先来看WindowManager的父类ViewManager:
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
ViewManager只供给了三个接口办法:别离对应增加,更新,删除
- 1.addView:增加一个View到WMS中,第二个参数为当时View的参数。
- 2.updateViewLayout:更新当时View,第二个参数为需求更新的view参数
- 3.removeView:删除当时View
而这几个办法详细完成是在WindowManagerImpl类中(前面剖析过)
直接来看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);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
...
}
能够看到这三个办法都是委托给了WindowManagerGlobal进行处理,这是设计方式中的桥接方式。
进入WindowManagerGlobal中:
先剖析addView:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
//将params强转为WindowManager.LayoutParams类型
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
synchronized (mLock) {
//创立一个ViewRootImpl目标
root = new ViewRootImpl(view.getContext(), display);
//给View设置LayoutParams参数
view.setLayoutParams(wparams);
//存储view到mViews列表
mViews.add(view);
//存储root到mRoots列表
mRoots.add(root);
//存储wparams到mParams列表
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;
}
}
}
- 1.首要创立一个ViewRootImpl目标root。
- 2.然后将当时view,当时params以及1中创立的ViewRootImpl目标root别离存储到对应的List列表中,注意这三者的index是一一对应的。
- 3.调用root的setView办法
ViewRootImpl身负了许多职责:
- 1.办理View树,且其是View的根
- 2.触发三大制作流程:丈量,布局,制作
- 3.输入事情中转站
- 4.办理Surface
- 5.担任与WMS通讯
咱们来看ViewRootImpl的setView办法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();//注释1
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);//注释2
...
}
来看setView的注释1:requestLayout,屏幕制作部分
咱们在前面一篇文章《”一文读懂”系列:Android屏幕改写机制》现已对原理进行解析过了,这儿咱们再大概说下:
requestLayout内部主要运用笔直同步信号VSync的办法,在收到GPU供给的VSync信号后,会触发View的三大制作layout,mesure,draw流程…
需求知道完好机制的能够调到原文查看
这儿咱们要点来看setView的注释2:mWindowSession.addToDisplay
望文生义:addToDisplay便是将Window增加到屏幕上,怎么增加的呢?
mWindowSession是IWindowSession类型的,它是一个binder目标,用于进程间通讯,IWindowSession是C端署理,在S端运用的是Session类完成。addToDisplay运行在WMS进程中。
mWindowSession = WindowManagerGlobal.getWindowSession();
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();//1
sWindowSession = windowManager.openSession(//2
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
注释1处:回来一个WMS目标的署理类。在注释2处调用WMS的署理类的openSession办法
定位到WMS的openSession办法:
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
能够看到回来的便是一个Session目标。
所以之前的mWindowSession.addToDisplay办法调用的便是Session类的addToDisplay办法
此时现已进入了WMS进程:
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
这儿的mService = WMS服务,终究又回调到WMS中去了:
调用WMS的addWindow办法增加Window,在WMS眼里,一切View都是以Window方式存在的,
剩下的作业就交由WMS进行处理了:在WMS中会为这个Window分配Surface,并确认显现层级,
可见担任显现界面的是画布Surface,而不是窗口本身,WMS将他办理的Surface交由SurfaceFlinger处理,SurfaceFlinger将这些Surface合并后放入到buffer中,屏幕会定时从buffer中获取显现数据,显现到屏幕上。
同理咱们再来剖析下View删除流程:
定位到WindowManagerGlobal的removeView
public void removeView(View view, boolean immediate) {
...
synchronized (mLock) {
int index = findViewLocked(view, true);//1
View curView = mRoots.get(index).getView();//2
removeViewLocked(index, immediate);//3
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private int findViewLocked(View view, boolean required) {
final int index = mViews.indexOf(view);
if (required && index < 0) {
throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
return index;
}
在注释1处获取view在mViews中的索引,在注释2处经过索引获取当时mRoots列表中view。在注释3处调用removeViewLocked,这个是要点移出办法。
private void removeViewLocked(int index, boolean immediate) {
...
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
看root.die办法:
boolean die(boolean immediate) {
...
//假如是同步移除,则立即调用doDie办法
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
...
//异步移除,宣布一个msg进行移除
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
不管是同步还是异步,终究都会调用doDie办法进行移除。
进行doDie办法:
void doDie() {
...
synchronized (this) {
if (mAdded) {
dispatchDetachedFromWindow();//1
}
..
}
WindowManagerGlobal.getInstance().doRemoveView(this);//4
}
doDie注释1:
这个办法里边做一些对资源毁掉的操作:
void dispatchDetachedFromWindow() {
if (mView != null && mView.mAttachInfo != null) {
//这儿能够看出咱们能够在View毁掉前,在View的dispatchDetachedFromWindow做一些资源释放操作
mView.dispatchDetachedFromWindow();
}
//毁掉硬件烘托线程
destroyHardwareRenderer();
//将mView的root置为mull
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
//释放当时Surface
mSurface.release();
//中心移除View的办法。
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
//移除同步屏障以及View的制作task
unscheduleTraversals();
}
mWindowSession.remove(mWindow)
在addView流程剖析过:mWindowSession是WMS进程中的Session类
Session.java
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
在 Session 中直接调用了 WindowManagerService 的 removeWindow(Session session, IWindow client) 办法。
WindowManagerService
public void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
// 得到 windowstate 目标
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return;
}
// 进行移除 window 操作
removeWindowLocked(win);
}
}
先得到 WindowState 目标,再调用 removeWindowLocked 去移除该 WindowState 。而详细的 removeWindowLocked 代码咱们在这就不深入了,能够自行研究。
至此,整个 Window 移除机制也剖析完毕了。
总结
本篇文章基于Window模型以及源码对Window在整个Android FrameWork体系中所涉及的常识点以及效果做了一个全体的概括,篇幅问题,关于WMS中怎么对Window的操作放在下篇进行解说。敬请期待。。
假如觉得我的文章对你有协助,请帮忙点个赞,重视下,谢谢。
下面是博主的大众号:小余的自习室