很明显这个问题不是肯定的,是可以为其他的Context的。不然传参就定为了activity了。
先看下不传如activity基本的使用。一般用作悬浮窗。
在AndroidManifest.xml 里面添加权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
检测在应用上层展示的权限是否开启
fun checkOverlayPermission(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
Settings.canDrawOverlays(context)
else
true
}
如果没有开启,则打开设置开启权限。不同的机型打开的方式可能不同。
startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName")))
创建Dialog设置TYPE_APPLICATION_OVERLAY (8.0之前使用TYPE_ SYSTEM_ ALERT )
val dialog = AlertDialog.Builder(this.applicationContext)
.setTitle("title")
.setMessage("message")
.create()
dialog.window?.setType(TYPE_APPLICATION_OVERLAY)
dialog.show()
这样就完成了一个悬浮在应用上层的Dialog。
如果我把TYPE_APPLICATION_OVERLAY 去掉,则会报异常BadTokenException
Token 的定义和作用
因为在WMS那边需要根据这个Token来确定Window的位置(不是说坐标),如果没有Token的话,就不知道这个窗口应该放到哪个容器上了;
大致就是添加window时的验证,就是IBinder中的token和添加window时的token对应才能添加成功。
Token的赋值
Window.class - adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp)
走到这一步的方式为:
Dialog.class -> show() ->
WindowManagerImpl.class -> addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
WindowManagerGlobal.class -> addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId)
// 这一步是关键
...
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
...
所以可以看出parentWindow 是否为空起了关键作用。
Activity 重写了getSystemService方法,自定义了WINDOW_SERVICE 类型的返回。
非Activity的Context得到的WindowManager没有parentWindow 的,所以token无法对应就会异常。
添加TYPE_APPLICATION_OVERLAY 的原因就是: WMS在看到Window.TYPE 为 SYSTEM_WINDOW 时会为SYSTEM_WINDOW 类型的窗口专门创建一个WindowToken ,并放置在DisplayArea.Tokens 里面
|