无法添加窗口 android.view.ViewRoot$W@44da9bc0 -- 对于此窗口类型被拒绝了权限

82

我更倾向于使用这个帖子作为例子,但是当我将ViewGroup添加到WindowManager对象中时出现了错误。我已经使用与问题中发布的Service相同的类,没有进行更改,我不知道我的错误在哪里。

WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mView, params); // here
当我向WindowManager添加视图时,这是我的清单文件。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.searce.testoverlay"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name="TestOverlayActivity"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        <service android:enabled="true" android:name=".HUD"></service>
    </application>
</manifest>

错误

09-27 18:49:23.561: ERROR/AndroidRuntime(653): Uncaught handler: thread main exiting due to uncaught exception
09-27 18:49:23.571: ERROR/AndroidRuntime(653): java.lang.RuntimeException: Unable to create service com.searce.testoverlay.HUD: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRoot$W@44da9bc0 -- permission denied for this window type
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.handleCreateService(ActivityThread.java:2790)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.access$3200(ActivityThread.java:119)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1917)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.os.Handler.dispatchMessage(Handler.java:99)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.os.Looper.loop(Looper.java:123)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.main(ActivityThread.java:4363)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at java.lang.reflect.Method.invokeNative(Native Method)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at java.lang.reflect.Method.invoke(Method.java:521)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at dalvik.system.NativeStart.main(Native Method)
09-27 18:49:23.571: ERROR/AndroidRuntime(653): Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRoot$W@44da9bc0 -- permission denied for this window type
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.view.ViewRoot.setView(ViewRoot.java:492)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at com.searce.testoverlay.HUD.onCreate(HUD.java:41)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.handleCreateService(ActivityThread.java:2780)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     ... 10 more
4个回答

154

@ceph3us 你知道如何在>=M上实现吗? ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW}, Perm.PERMISSIONS_REQUEST_SYSTEM_ALERT_WINDOW); 不会触发任何东西。同样适用于: ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW}, Perm.PERMISSIONS_REQUEST_SYSTEM_ALERT_WINDOW); - Martin Pfeffer
此外,您可能需要明确地授予权限,无论是通过编程还是手动从手机设置中进行。我正在使用华为Nexus 6p,在应用程序的设置中,我在屏幕部分的其他应用程序上绘制上单击了“Yes”。 - emir
1
请查看此链接以获取一个好的解决方案:https://github.com/facebook/react-native/issues/5886#issuecomment-224397883 - WiRa

147

"@ceph3us 你知道如何在API >=M上实现 SYSTEM_ALERT_WINDOW 权限(绘制在其他应用程序上等):

  1. API >= 23 上的 SYSTEM_ALERT_WINDOW 权限(绘制在其他应用程序上等):

    • 不再显示在应用程序的权限屏幕中。
    • 甚至不会出现在奇怪的新“所有权限”屏幕中
  2. 使用此权限调用 Activity.requestPermissions(),

    • 将不会显示任何对话框供用户允许/拒绝。
    • 相反,Activity.onRequestPermissionsResult() 回调将立即被调用并返回一个被拒绝的标志。

解决方案:

如果应用程序的目标API级别为23或更高,则必须通过权限管理屏幕显式地向应用程序用户授予此权限。 应用程序通过发送具有动作ACTION_MANAGE_OVERLAY_PERMISSION的意图来请求用户的批准。 应用程序可以通过调用Settings.canDrawOverlays()检查是否具有此授权。

示例代码:

/** code to post/handler request for permission */
public final static int REQUEST_CODE = -1010101; *(see edit II)*

public void checkDrawOverlayPermission() {
    /** check if we already  have permission to draw over other apps */
    if (!Settings.canDrawOverlays(Context)) {
        /** if not construct intent to request permission */
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        /** request permission via start activity for result */
        startActivityForResult(intent, REQUEST_CODE);
    }
}

@Override 
protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
    /** check if received result code 
        is equal our requested code for draw permission  */
    if (requestCode == REQUEST_CODE) {
       / ** if so check once again if we have permission */
       if (Settings.canDrawOverlays(this)) {
           // continue here - permission was granted 
       }
    }
}

"用户如何禁用此权限?在设置->应用程序->“MyApp”->权限中不显示。另外,为什么我们请求这个权限的方式与其他权限不同呢? - 匿名者 2月12日21:01"

有一些权限不像普通和危险的权限那样行为。SYSTEM_ALERT_WINDOW和WRITE_SETTINGS特别敏感,因此大多数应用程序不应使用它们。如果应用程序需要其中一个权限,则必须在清单中声明该权限,并发送意图请求用户的授权。系统通过显示详细的管理屏幕来响应意图。

特殊权限

编辑 II:

我在扩展FragmentActivity的活动中使用了这段代码,结果出现了异常java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode because request code used is not in the range of 0.. 65535. You might consider changing your request code to an appropriate value. – mtsahakis

请求代码必须在0..65535范围内。

这是因为:

  • 在Java中,整数由32位表示
  • 您只能使用低16位作为requestCode
  • 其他位用于请求处理

因此,例如:

integer value:  5463             ///hi 16 bits //   |    // lo 16 bits //
as binary string will look like: 0000 0000 0000 0000 0001 0101 0101 0111 

给定范围内简单使用代码

编辑 III:

适用于目标 AOSP API 26(Android Oreo / 8+)的应用程序

使用 SYSTEM_ALERT_WINDOW 权限的应用程序不能再使用以下窗口类型显示在其他应用程序和系统窗口上方的警报窗口:

TYPE_PHONE TYPE_PRIORITY_PHONE TYPE_SYSTEM_ALERT TYPE_SYSTEM_OVERLAY TYPE_SYSTEM_ERROR

相反,应用程序必须使用称为 TYPE_APPLICATION_OVERLAY 的新窗口类型。

TYPE_APPLICATION_OVERLAY

窗口类型:应用程序叠加窗口位于所有活动窗口(FIRST_APPLICATION_WINDOW 和 LAST_APPLICATION_WINDOW 之间的类型)之上,但位于状态栏或 IME 等关键系统窗口下方。

系统可能会随时更改这些窗口的位置、大小或可见性,以降低对用户的视觉混乱并管理资源。

需要 SYSTEM_ALERT_WINDOW 权限。

系统将调整具有此窗口类型的进程的重要性,以降低低内存杀手杀死它们的机会。在多用户系统中,仅显示在所属用户的屏幕上。

WindowManager.LayoutParams wLp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
      ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
      : WindowManager.LayoutParams.TYPE_PHONE;

Window.setAttributes(WindowManager.LayoutParams)

2
用户如何禁用此权限?它不会显示在设置->应用程序->“MyApp”->权限中。另外...为什么我们请求此权限的方式与其他权限不同,有什么解释吗? - Anonymous
1
我在一个扩展FragmentActivity的Activity中使用了这段代码,但是出现了异常java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode,因为使用的请求码不在0..65535的范围内。您可能需要将请求码更改为适当的值。 - mtsahakis
1
使用此请求代码......public final static int REQUEST_CODE = 5463&0xffffff00; - Muhammad Adil
@MuhammadAdil 尝试使用类似 "public final static int REQUEST_CODE = 9999;" 的内容。 - Fayçal
这应该是这里唯一的答案。但是这里是适用于API > 25的更新`@TargetApi(Build.VERSION_CODES.M) public void checkDrawOverlayPermission() { if (!Settings.canDrawOverlays(this.getApplicationContext())) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE); } }` - prog_24
显示剩余6条评论

3

跟随ceph3us的回答添加一个警告对话框,在这里运作良好。

final AlertDialog dialog = dialogBuilder.create();
                final Window dialogWindow = dialog.getWindow();
                final WindowManager.LayoutParams dialogWindowAttributes = dialogWindow.getAttributes();

                // Set fixed width (280dp) and WRAP_CONTENT height
                final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
                lp.copyFrom(dialogWindowAttributes);
                lp.width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 280, getResources().getDisplayMetrics());
                lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
                dialogWindow.setAttributes(lp);

                // Set to TYPE_SYSTEM_ALERT so that the Service can display it
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    dialogWindow.setType(WindowManager.LayoutParams.TYPE_TOAST);
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    dialogWindow.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
                }
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
                {
                    dialogWindow.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                }
                dialog.show();

但使用TYPE_SYSTEM_ALERT可能触发Google下架政策,因为使用危险权限的应用程序。请确保您有充分的理由以防万一谷歌要求。


不使用TYPE_APPLICATION_OVERLAY,而是使用其他类型(在我的情况下为TYPE_PHONE)适用于Android 7.1.1或更低版本的手机,这有助于我摆脱这个讨厌的BadTokenException。请参见https://dev59.com/ClwY5IYBdhLWcg3wyaTV#47103959。 - Martin Vysny

-8
你可以将目标SDK更改为22或更低版本,那么它也可以在API 23上运行。
在Gradle.Build中进行更改。

3
@Hulk 这也许不是最佳解决方案,但指出来是很好的。 - Quark
这不会解决问题,这只是一个快速的解决方法,你可以使用它,但你永远无法针对API>23进行目标定位。 - Aaron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接