来自文件提供程序Uri的Android 7.0通知声音无法播放

Jor*_*oza 5 java android android-studio android-fileprovider android-7.0-nougat

我正在更改我的应用程序代码以支持Android 7,但在我的NotificationCompat.Builder.setSound(Uri)中传递来自FileProvider的Uri,Notification不播放任何声音,在Android 6中使用Uri.fromFile()正常工作.

mp3文件位于:

/Animeflv/cache/.sounds/

这是我的通知代码:

knf.animeflv.RequestBackground

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_not_r)
.setContentTitle(NotTit)
.setContentText(mess);
...
mBuilder.setVibrate(new long[]{100, 200, 100, 500});
mBuilder.setSound(UtilSound.getSoundUri(not)); //int
Run Code Online (Sandbox Code Playgroud)

这是我的UtilSound.getSoundUri(int)

public static Uri getSoundUri(int not) {
        switch (not) {
            case 0:
                return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            default:
                try {
                    File file=new File(Environment.getExternalStorageDirectory()+"/Animeflv/cache/.sounds",getSoundsFileName(not));
                    if (file.exists()) {
                        file.setReadable(true,false);
                        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
                            return FileProvider.getUriForFile(context, "knf.animeflv.RequestsBackground",file);
                        }else {
                            return Uri.fromFile(file);
                        }
                    }else {
                        Log.d("Sound Uri","Not found");
                        return getSoundUri(0);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    return getSoundUri(0);
                }
        }
    }
Run Code Online (Sandbox Code Playgroud)

在AndroidManifest.xml中:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="knf.animeflv.RequestsBackground"
    android:exported="false"
    android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
</provider>
Run Code Online (Sandbox Code Playgroud)

provider_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="_.sounds" path="Animeflv/cache/.sounds/"/>
</paths>
Run Code Online (Sandbox Code Playgroud)

Com*_*are 18

以下是我刚刚发表的一篇博文,转载于此,因为,嘿,为什么不呢?


您可以Notification通过类似的方法 setSound()在a上添加自定义铃声NotificationCompat.Builder.这需要一个Uri,这会导致Android 7.0出现问题,正如Stack Overflow的一些人所报道的那样 .

如果您使用的是file: Uri值,则如果您的targetSdkVersion值为24或更高,它们将不再适用于Android 7.0 ,因为Uri会检查声音是否符合禁止file: Uri.

但是,如果您尝试使用content: Urifrom,比如说,FileProvider您的声音将无法播放...因为Android没有对该内容的读取权限.

以下是解决此问题的一些选项.

手术刀: grantUriPermissions()

您始终可以通过grantUriPermissions()可用方法向其他应用授予内容权限Context.挑战在于知道授予的权限.

适用于Nexus 6P(Android 6.0 ......仍然......)和Nexus 9(Android 7.0)的是:

grantUriPermission("com.android.systemui", sound,
    Intent.FLAG_GRANT_READ_URI_PERMISSION);
Run Code Online (Sandbox Code Playgroud)

(你正在使用的sound是哪里)UrisetSound()

这是否能够支持所有设备和所有Android OS版本,我不能说.

断头台:没有更多的用户文件

android.resource因为一个方案适用于Uri价值观setSound().您只允许用户从应用程序中选择作为原始资源提供的多个铃声之一,而不是允许用户从文件中选择自己的铃声.但是,如果这代表了应用功能的丢失,那么您的用户可能会感到不满意.

Ax:使用自定义 ContentProvider

FileProvider导出时无法使用 - 它在启动时崩溃.但是,对于这种情况,唯一content: Uri可以在没有其他问题的情况下工作的是提供者是exported并且没有读取访问权限(或者恰好需要某些权限 com.android.systemui或者等效的情况).

最后,我会为这个选项添加到 我的StreamProvider,因为一些"只读"提供功能的一部分.

但是,你可以为此推出自己的提供商.

电锯:禁止禁令

以下代码片段阻止StrictMode与VM行为相关的所有检查(即主应用程序线程行为以外的其他内容),包括禁止file: Uri值:

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build());
Run Code Online (Sandbox Code Playgroud)

或者,您可以VmPolicy使用您想要的任何规则配置自己的,而无需调用detectFileUriExposure().

这允许您在file: Uri任何地方使用值.谷歌禁止使用这个原因有很好的理由file: Uri,所以试图避免这种禁令可能会让你长期处于不幸的身体部位.

核弹:使用较低的 targetSdkVersion

这也消除了对file: Uri价值的禁令,以及targetSdkVersion24+选择加入的所有其他行为.值得注意的是,Toast 如果用户进入分屏多窗口模式,这将导致您的应用显示"可能无法使用分屏" .

真正的解决方案:Android中的修复

NotificationManager应呼吁grantUriPermissions() 对我们来说,还是应该有我们联想到一些其他的方式 FLAG_GRANT_READ_URI_PERMISSIONUri我们使用自定义 Notification声音. 请继续关注进一步的发展.

  • 非常感谢您的回答.为了验证grantUriPermission解决方案('The Scalpel')的稳健性,我在不同的物理设备(包括LG,摩托罗拉,HTC,三星和索尼),从API 15到API 24进行了测试.它在所有设备上都能正常工作其中,解决方案似乎相当稳固(虽然肯定是hacky). (2认同)