基于Android 10源码分析
前言
WMS是WindowManagerService的缩写。WMS即窗口管理服务,继承IWindowManager.Stub,属于一个Binder服务端,它运行在SystemServer进程,因此与WMS的交互也是一个IPC的过程。
WMS负责的功能:
- Z-ordered的维护函数:Window除了X、Y轴之外,还有Z轴的概念。所以他需要维护Window的层级关系;
- AddWindow/RemoveWindow:既然是Window的的管理,那么少不了Window的添加和删除;
- 输入法管理:输入法也是一个Window,键盘的输入输出,需要跟其他App进行交互,这里就需要InputManagerService来辅助管理
- Layerout
- Token(AppToken)管理
- 活动窗口管理(FocusWindow):当前正常活跃的窗口
- 活动应用管理(FocusAPP):当前正在活跃的应用
- 转场动画:以window 为单位的动画效果;我们应用Activity 启动另一个Activity的时候window 切换到另一个window的动效
- 系统消息收集和分发线程:事件来源与InputManagerService
WMS 启动
WMS 属于我们SystemServer的一个服务,他不属于我们的引导服务并且在SystemServer.startOtherServices
中创建。相较于AMS的启动WMS启动还是非常简单的。
SystemServer.startOtherServices
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
try {
//...
//WindowManagerService 初始化
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
//添加Binder服务 WMS
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
//AMS 设置 WMS
mActivityManagerService.setWindowManager(wm);
//初始化就绪
wm.onInitReady();
//...
} //...
//渲染就绪
wm.displayReady();
//...
//系统就绪
wm.systemReady();
//...
}
复制代码
WindowManagerService.main
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
//通过 DisplayThread 构建我们的 WindowManagerService
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
return sInstance;
}
复制代码
这里有一个重要的点是DisplayThread.getHandler().runWithScissors
他会阻塞当前线程,之后里面的Runnable 执行结束,也就是sInstance构建成功之后才会执行 return sInstance;
代码
WMS中数据结构和重要的成员变量
mTokenMap
保存所有显示令牌(类型为WindowToken),一个窗口必须隶属于某一个显示令牌。
mWindowMap
保存所有窗口的状态信息(类型为WindowState)
mSessions
保存了当前所有想向WMS寻求窗口管理服务的客户端。注意Session是具有进程唯一性
Window添加的流程
Window添加的这里涉及到Activity启动,所以需要对Activity启动流程有一定的理解。一个Acitivity对应一个Window,也就是Activity创建的过程 == Window创建的过程
窗口的创建
一个Acitivity对应一个ActivityRecord,那么ActivityRecord创建 ActivityStarter.executeRequest()方法里面创建了我们的ActivityRecord,注意这个时候还是在AMS所在的进程
ActivityRecord创建(WindowToken 的创建过程)
AcitivityRecord继承自WindowToken
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener{
ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
@Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord) {
//1.Token(_intent).asBinder() 赋值给 token
super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
null /* displayContent */, false /* ownerCanManageAppTokens */);
mAtmService = _service;
//2.将父类token 赋值给appToken
appToken = (Token) token;
info = aInfo;
mUserId = UserHandle.getUserId(info.applicationInfo.uid);
packageName = info.applicationInfo.packageName;
mInputApplicationHandle = new InputApplicationHandle(appToken);
intent = _intent;
//....
}
}
复制代码
这里的token是一个IApplicationToken.Stub的Binder对象
Token传递过程
Token传递过程其实存在于Activity启动的过程中
- 在ActivityStackSupervisor.realStartActivityLocked中我们可以看到Token被封装到ClientTransaction中去
- 接着我们直接看App应用进程收到Token的处理,token被取出来之后会执行LaunchActivityItem.execute()
- LaunchActivityItem.execute()将我们的token保存到ActivityClientRecord中
Token处理
在Token传递过程我们可以看到Token保存到ActivityClientRecord之后 ActivityThread.handleLaunchActivity
=> ActivityThread.performLaunchActivity
=> Activity.attach
//Activity.java
//这里的token就是传递过来的 IApplicationToken 的代理
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.创建我们的PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//...
//token赋值
mToken = token;
//....
//2.创建WindowManager 对象
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());
}
//3.赋值mWindowManager
mWindowManager = mWindow.getWindowManager();
//...
}
复制代码
这里需要注意的事第2步setWindowManager
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//这里mWindowManager是新实例化出来的本地WindowManagerImpl
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
复制代码
View的添加
Acitivty的View的添加是从handleResumeActivity开始的
Activity.handleResumeActivity
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
//...
//这里会优先执行onResume
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
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);
//获取WindowManagerImpl
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;
//这里开始将decorView 添加到Window
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} //...
//...
}
复制代码
WindowManagerImpl.addView
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
// 这里 mGlobal = WindowManagerGlobal.getInstance(); 是一个单例
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
复制代码
WindowManagerGlobal.addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
//...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//...
//1.初始化ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//2.缓存view
mViews.add(view);
//3.缓存root
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//4.将View设置到root
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
复制代码
初始化ViewRootImpl
public ViewRootImpl(Context context, Display display) {
//通过WindowManagerGlobal.getWindowSession()获取session
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
复制代码
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
mWidth = -1;
mHeight = -1;
mDirty = new Rect();
mTempRect = new Rect();
mVisRect = new Rect();
mWinFrame = new Rect();
//mWindow 是用来给WMS回调用的Binder
mWindow = new W(this);
mLeashToken = new Binder();
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
mPerformContentCapture = true; // also true for the first time the view is added
mAdded = false;
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
mCompatibleVisibilityInfo = new SystemUiVisibilityInfo();
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mHighContrastTextManager = new HighContrastTextManager();
mAccessibilityManager.addHighTextContrastStateChangeListener(
mHighContrastTextManager, mHandler);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
String processorOverrideName = context.getResources().getString(
R.string.config_inputEventCompatProcessorOverrideClassName);
if (processorOverrideName.isEmpty()) {
// No compatibility processor override, using default.
mInputCompatProcessor = new InputEventCompatProcessor(context);
} else {
InputEventCompatProcessor compatProcessor = null;
try {
final Class<? extends InputEventCompatProcessor> klass =
(Class<? extends InputEventCompatProcessor>) Class.forName(
processorOverrideName);
compatProcessor = klass.getConstructor(Context.class).newInstance(context);
} catch (Exception e) {
Log.e(TAG, "Unable to create the InputEventCompatProcessor. ", e);
} finally {
mInputCompatProcessor = compatProcessor;
}
}
if (!sCompatibilityDone) {
sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
sCompatibilityDone = true;
}
loadSystemProperties();
mImeFocusController = new ImeFocusController(this);
}
复制代码
获取Session
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient
Session 是一个Binder 对象
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// Emulate the legacy behavior. The global instance of InputMethodManager
// was instantiated here.
// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
//通过WMS 获取 session binder 代理
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
复制代码
ViewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
//...
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 添加刷新layout handler 任务,就会走View的绘制流程
requestLayout();
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
//通过session将mWindow(给WMS的 binder 回调),以及mTmpFrame(矩形区域)添加到WMS
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);
} //...
//...
}
}
}
复制代码
WMS.addWindow
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
WindowState parentWindow = null;
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
final int type = attrs.type;
synchronized (mGlobalLock) {
//...
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
//通过displayContent 获取WindowToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
if (token == null) {
//非Acitivty启动其他特殊情况不做深究
if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {//Acitivity启动
//1.启动acitivty的时候会创建ActivityRecord的WindowToken
activity = token.asActivityRecord();
//...
} //...一些其他window
//2.创建一一对应的WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
//...
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
//3.WindowState.attach
win.attach();
//添加到mWindowMap 这里的Client 就是 ViewRootImpl 传递过来的W binder 代理,用于回调,这里当作唯一Key
mWindowMap.put(client.asBinder(), win);
//...
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
//...
}
//...
return res;
}
复制代码
WindowState.attach
void attach() {
mSession.windowAddedLocked(mAttrs.packageName);
}
复制代码
Session.windowAddedLocked
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null) {
//创建SurfaceSession
mSurfaceSession = new SurfaceSession();
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
mNumWindow++;
}
复制代码
public final class SurfaceSession {
// Note: This field is accessed by native code.
@UnsupportedAppUsage
private long mNativeClient; // SurfaceComposerClient*
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
private static native void nativeKill(long ptr);
/** Create a new connection with the surface flinger. */
@UnsupportedAppUsage
public SurfaceSession() {
//创建一个jini 层 SurfaceComposerClient native类
mNativeClient = nativeCreate();
}
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
try {
if (mNativeClient != 0) {
nativeDestroy(mNativeClient);
}
} finally {
super.finalize();
}
}
/**
* Forcibly detach native resources associated with this object.
* Unlike destroy(), after this call any surfaces that were created
* from the session will no longer work.
*/
@UnsupportedAppUsage
public void kill() {
nativeKill(mNativeClient);
}
}
复制代码
SurfaceComposerClient通过IGraphicBufferProducer调用SurfaceFlinger进行页面的刷新,后面的细节本人也不是很清楚。
附录
WMS
WMS是WindowManagerService的缩写,负责管理Android系统中的所有的Window创建和删除等工作。WMS即窗口管理服务,继承IWindowManager.Stub,属于一个Binder服务端,它运行在SystemServer进程,因此WM与WMS的交互也是一个IPC的过程。
WM
WM 即WindowManager 的缩写,应用与窗口管理服务WindowManagerService交互的接口。WindowManagerImpl 是WM的实现类,但是真正与WMS交互的是通过WindowManagerGlobal中的Session进行Binder 通信。
Window
Window表示一个窗口的概念,是所有View的直接管理者,任何视图都通过 Window呈现(点击事件由Window->DecorView->View; Activity的 setContentView底层通过Window完成)。
但是Window是一个抽象类,具体实现是PhoneWindow
创建Window需要通过WindowManager创建
每个Window都需要指定一个Type(应用窗口、子窗口、系统窗 口)。Activity对应的窗口是应用窗口;PopupWindow, ContextMenu,OptionMenu是常用的子窗口;像Toast和系统警告提 示框(如ANR)就是系窗口,还有很多应用的悬浮框也属于系统窗口 类型。
手机上一块显示区域,添加一个Window的过程,也就是申请分配一块Surface的过程
WindowToken
是一种特殊的Binder令牌,WMS用它唯一标识系统中的一个窗口。
- 在进行窗口Zorder排序时,属于同一个WindowToken的窗口会被安排在一起,而且在其中定义的一些属性将会影响所有属于此WindowToken的窗口,这些都表明了属于同一个WindowToken的窗口之间的紧密联系.
- 应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken的类型一致
- 在创建系统类型的窗口时不需要提供一个有效的Token,WMS会隐式地为其声明一个WindowToken,看起来谁都可以添加个系统级的窗口。难道Android为了内部使用方便而置安全于不顾吗?非也,
addWindow()
函数一开始的mPolicy.checkAddPermission()
的目的就是如此。它要求客户端必须拥有SYSTEM_ALERT_WINDOW或INTERNAL_SYSTEM_WINDOW权限才能创建系统类型的窗口
Session
WMS提供给App进程,通过建立Session代理对象和Session对象通信,进而和WMS建立连接。每个App进程对应一个Session
Surface
每个显示界面的窗口都是一个Surface
Window、WMS、WM 三者之间的关系
WindowState
描述窗口的状态信息以及和WindowManagerService进行通信,一般一个窗口对应一个WindowState。它用来表示一个窗口的所有属性
WindowState 和 Window 是一一对应的关系,一个是WMS这边的,一个是App进程这边的。
DisplayContent
用于描述多屏输出相关信息。
根据窗口的显示位置将其分组。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每一个DisplayContent都对应这唯一ID,在添加窗口时可以通过指定这个ID决定其将被显示在那个屏幕中。
DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合
PhoneWindowManager
实现了窗口的各种策略,定义了窗口相关策略,比如:告诉WMS某一个类型Window的Z-Order的值是多少,帮助WMS矫正不合理的窗口属性,为WMS监听屏幕旋转的状态,预处理一些系统按键事件(例如HOME、BACK键等的默认行为就是在这里实现的)等
Animator
所有窗口动画的总管(WindowStateAnimator对象)。在Choreographer的驱动下,逐个渲染所有的动画
Choreographer
用于控制窗口动画、屏幕旋转等操作,它拥有从显示子系统获取VSYNC同步事件的能力,从而可以在合适的时机通知渲染动作,避免在渲染的过程中因为发生屏幕重绘而导致的画面撕裂。WMS使用Choreographer负责驱动所有的窗口动画、屏幕旋转动画、墙纸动画的渲染
SurfaceFlinger
SurfaceFlinger负责管理Android系统的帧缓冲区(Frame Buffer),Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户界面的
InputManager
IMS实例。管理每个窗口的输入事件通道(InputChannel)以及向通道上派发事件
Type
用来标识Window级别的
- 应用窗口:层级范围是1~99
- 子窗口:层级范围是1000~1999
- 系统窗口:层级范围是2000~2999
应用窗口(1~99)
//第一个应用窗口
public static final int FIRST_APPLICATION_WINDOW = 1;
//所有程序窗口的base窗口,其他应用程序窗口都显示在它上面
public static final int TYPE_BASE_APPLICATION = 1;
//所有Activity的窗口,只能配合Activity在当前APP使用
public static final int TYPE_APPLICATION = 2;
//目标应用窗口未启动之前的那个窗口
public static final int TYPE_APPLICATION_STARTING = 3;
//最后一个应用窗口
public static final int LAST_APPLICATION_WINDOW = 99;
复制代码
子窗口(1000~1999)
//第一个子窗口
public static final int FIRST_SUB_WINDOW = 1000;
// 面板窗口,显示于宿主窗口的上层,只能配合Activity在当前APP使用
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
// 媒体窗口(例如视频),显示于宿主窗口下层
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1;
// 应用程序窗口的子面板,只能配合Activity在当前APP使用(PopupWindow默认就是这个Type)
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
//对话框窗口,只能配合Activity在当前APP使用
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
//TYPE_APPLICATION_MEDIA的重影窗口,显示TYPE_APPLICATION_MEDIA和应用窗口之间
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4;
//最后一个子窗口
public static final int LAST_SUB_WINDOW = 1999;
复制代码
系统窗口(2000~2999)
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;//电话窗口,它用于电话交互(特别是呼入),置于所有应用程序之上,状态栏之下,属于悬浮窗(并且给一个Activity的话按下HOME键会出现看不到桌面上的图标异常情况)
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//系统警告提示窗口,出现在应用程序窗口之上,属于悬浮窗, 但是会被禁止
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//信息窗口,用于显示Toast, 不属于悬浮窗, 但有悬浮窗的功能
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;//系统顶层窗口,显示在其他一切内容之上,此窗口不能获得输入焦点,否则影响锁屏
//...省略了很多
public static final int LAST_SYSTEM_WINDOW=2999;//最后一个系统窗口
复制代码