45

我注意到我的新手机上的 Android 应用程序存在一个奇怪的问题。SDK 23 权限弹出窗口(如外部存储)被下面附加的警报阻止。我最初认为这与我的手机有关,但它似乎不会影响我安装的任何其他应用程序。

这个问题可能与安装调试版本有关,还是我的权限处理有问题?我认为它可能与我正在使用的广告平台之一有关,但我尝试禁用它们,但它仍然出现

在此处输入图像描述

我在下面粘贴了生成此权限请求的图像保存功能。我正在使用Dexter来节省编写一大堆可怕的样板文件

public static void saveToExternalStorageIfAllowed(final Context context, final Bitmap bitmapImage, final String title) {
    final Tracker t = ((LoLHistory) context.getApplicationContext()).getTracker(LoLHistory.TrackerName.APP_TRACKER);

    // saving to publicly visible/accessible folder. Requires write permission
    int permissionCheck = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        // do not have permissions to write, request
        t.send(new HitBuilders.EventBuilder()
                .setCategory("FILE")
                .setAction("PermissionMissing")
                .setLabel("WRITE_EXTERNAL")
                .build());
        Dexter.checkPermission(new PermissionListener() {
            @Override
            public void onPermissionGranted(PermissionGrantedResponse response) {
                t.send(new HitBuilders.EventBuilder()
                        .setCategory("FILE")
                        .setAction("PermissionGranted")
                        .setLabel("WRITE_EXTERNAL")
                        .build());

                saveToExternalStorage(context, bitmapImage, title);
            }

            @Override
            public void onPermissionDenied(PermissionDeniedResponse response) {
                t.send(new HitBuilders.EventBuilder()
                        .setCategory("FILE")
                        .setAction("PermissionDenied")
                        .setLabel("WRITE_EXTERNAL")
                        .build());
            }

            @Override
            public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {/* ... */}
        }, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    } else {
        saveToExternalStorage(context, bitmapImage, title);
    }
}

private static void saveToExternalStorage(Context context, Bitmap bitmapImage, String title) {
    Tracker t = ((LoLHistory) context.getApplicationContext()).getTracker(LoLHistory.TrackerName.APP_TRACKER);

    // create image folder if does not exist
    File imagesFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), context.getString(R.string.app_name));
    if (!imagesFolder.mkdirs() && !imagesFolder.isDirectory()) {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            // failed to create and is not a directory. Something went wrong...
            t.send(new HitBuilders.EventBuilder()
                    .setCategory("FILE")
                    .setAction("CreateDirFailed")
                    .setLabel(imagesFolder.getPath())
                    .build());
        } else {
            t.send(new HitBuilders.EventBuilder()
                    .setCategory("FILE")
                    .setAction("CreateDirFailedMediaNotMounted")
                    .setLabel(imagesFolder.getPath())
                    .build());
        }
    }

    // delete image if already exists so FOS can create a new one
    File image = new File(imagesFolder, title + ".jpg");
    if (image.exists()) {
        // image already exists, deleting to start from clean state
        if (!image.delete()) {
            // failed to delete
            t.send(new HitBuilders.EventBuilder()
                    .setCategory("FILE")
                    .setAction("DeleteFailed")
                    .setLabel(image.getPath())
                    .build());
        }
    }

    // compress bitmap and write to file stream. FOS creates file if does not exist
    FileOutputStream out = null;
    try {
        out = new FileOutputStream(image);
        bitmapImage.compress(Bitmap.CompressFormat.JPEG, 50, out);
        out.flush();
    } catch (Exception e) {
        e.printStackTrace();
        t.send(new HitBuilders.ExceptionBuilder()
                .setDescription(e.getLocalizedMessage())
                .setFatal(true)
                .build());
    } finally {
        try {
            if (out != null) {
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
            t.send(new HitBuilders.ExceptionBuilder()
                    .setDescription(e.getLocalizedMessage())
                    .setFatal(true)
                    .build());
        }
    }

    // get Uri from saved image
    Uri uriSavedImage = Uri.fromFile(image);

    // media scan the new file so it shows up in the gallery
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    mediaScanIntent.setData(uriSavedImage);
    context.sendBroadcast(mediaScanIntent);
}

更新:由于很多人都提到它,如前所述,这个问题不是由于安装了覆盖应用程序。在Draw over other apps菜单下,我有以下应用程序:Google Play 音乐、Google Play 服务、照片、TalkBack、Twitch、Twitter。所有这些都设置为 No

此外,我还测试了其他应用程序,例如 Google Hangouts 和 Twitter,它们也具有需要危险权限的操作,我能够提供这些权限而不会出现此问题。


解决方案:我已将R. Zagorski的答案标记为解决方案,因为它包含许多一般情况。对我来说,这实际上Toast是破坏了我的权限流程。这个弹出窗口让我走错了路,浪费了很多时间......

这是Toast我在权限弹出窗口出现后的最初几秒钟内可见的内容:

在此处输入图像描述

4

8 回答 8

59

此弹出窗口是由清单声明的manifest.PERMISSION.SYSTEM_ALERT_WINDOW权限引起的。开发人员必须注意 3 类权限:

  1. 正常权限 - 对它们不做任何事情,只需在清单中声明
  2. 易受攻击的权限- 在 Manifest 中声明并在第一时间请求权限。它们可以通过系统设置进行更改
  3. 以上危险权限:SYSTEM_ALERT_WINDOWWRITE_SETTINGS属于这一类。它们必须被授予,但在系统设置中不可见。要请求它,您不使用标准方式(int checkSelfPermission (String permission))但您必须检查Settings.canDrawOverlays()Settings.System.canWrite()适当地

如果您没有SYSTEM_ALERT_WINDOW权限:

  1. Toast检查与权限弹出窗口交互时是否可见。虽然 Overlay Detected 弹出窗口没有提及它,但 aToast也算作覆盖
  2. 检查您的任何依赖项是否需要它

如果您不确定是否使用此权限,可以执行以下几个测试用例:

  1. 自己请求此权限:

    public class MainActivity extends AppCompatActivity {
    
        public final static int REQUEST_CODE = 10101;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (checkDrawOverlayPermission()) {
                startService(new Intent(this, PowerButtonService.class));
            }
        }
    
        public boolean checkDrawOverlayPermission() {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                return true;
            }
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, REQUEST_CODE);
                return false;
            } else {
                return true;
            }
        }
    
        @Override
        @TargetApi(Build.VERSION_CODES.M)
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == REQUEST_CODE) {
                if (Settings.canDrawOverlays(this)) {
                    startService(new Intent(this, PowerButtonService.class));
                }
            }
        }
    }
    
  2. 检查是否不是您的情况

  3. 查看这篇文章以了解,通过 Play 商店安装应用程序时,此权限会自动授予(我没有检查,因此无法确认)

权限模型可以理解,只是有时候需要更多的挖掘。

于 2016-09-03T10:26:43.097 回答
12

这不是问题,您只需安装一个覆盖应用程序,最常见的是

  • 脸书信使
  • 启用弹出窗口的 Whatsapp
  • 再见聊天头
  • 维伯
  • 任何在屏幕上显示菜单或其他内容的应用程序都是覆盖应用程序。

关闭它,然后尝试。

于 2016-09-05T17:59:23.863 回答
2

当检测到屏幕覆盖时,系统将不允许您继续授予应用程序权限。因为屏幕覆盖可以接收用户的输入。因此,在授予应用程序权限之前,您必须禁用所有“屏幕覆盖”功能。这不是您的应用程序的问题。所有具有目标 SDK 23 的应用程序都会遇到此问题。

于 2016-08-31T10:25:05.953 回答
2

由于在 Android Marshmallow 中包含权限管理器,此弹出窗口成为一个问题。如果您的手机上安装了任何有权在屏幕上覆盖的应用程序,则有许多权限设置,包括文件访问权限,如果不先禁用这些屏幕覆盖权限,您将无法更改这些设置。对我来说,我的文本应用程序和 facebook messenger 应用程序是罪魁祸首。每次我想给任何其他应用程序请求的权限时,我都必须单击该弹出窗口上的“打开设置”选项并撤销上述两个应用程序的屏幕覆盖访问权限,然后重新打开有问题的应用程序以再次获得权限提示。然后,如果我想要我的消息弹出窗口或我的聊天头,我必须再次重新启用覆盖权限。这真的很烦人。我认为你的应用程序很好,android'

于 2016-08-30T16:48:01.933 回答
2

通常,这是由使用浮动应用程序引起的。

三星安卓手机用户:

Open the Settings
Then Applications > Application manager
Press on More > Apps that can appear on top

如果您发现有气泡的应用程序,请显示屏幕叠加层。如果某个应用程序调整了手机的亮度,请同时禁用这些应用程序上的屏幕覆盖选项。关键是,您必须找出罪魁祸首应用程序并禁用屏幕覆盖。

来源:修复 Android 上检测到的屏幕覆盖错误

于 2017-01-15T11:23:41.583 回答
0

我用谷歌搜索了很多解决方案,但没有任何效果。然后我尝试了设置中的所有选项:)

最后我可以给你明确的方法来禁用覆盖 -

转到设置>应用程序>默认应用程序>设备帮助>“打开屏幕上的阅读文本”选项

于 2017-07-13T09:58:29.573 回答
0

我已通过安装警报窗口检查器解决了此问题:https: //play.google.com/store/apps/details? id=jp.sfapps.alertwindowchecker 。它指示您当前哪些应用程序正在使用屏幕覆盖功能。

就我而言,我必须在应用程序 ES FileExplorer 中禁用滑动检测器才能解决此问题

于 2018-01-26T11:03:13.863 回答
0

重新启动手机对我有用。

于 2020-12-30T08:33:06.563 回答