我刚接触安卓系统,需要帮助解决这个问题。
我的应用程序支持果冻豆(16)至奥利奥(26)。
我有一个uploadservice,它需要openInputStream()来上传数据,因为nougat中的新行为。
这段代码在棉花糖和下面的代码中工作得很好,但是总是在nougat上给我SecurityException崩溃。它在调用openInputStream()时出错的行上崩溃:
java.lang.SecurityException:权限拒绝:从PID=30846、uid=10195中读取com.miui.gallery.provider.galleryOpenProvider uri content://com.miui.gallery.open/raw/%2fstorage%2fsemulated%2f0%2fdcim%2fcamera%2fimg_.jpg需要导出提供程序,或granturipermission()
文件uri可以来自不同的应用程序(库、相机等)。我已经将问题缩小到来自ACTION_GET_CONTENTintent的uri(任何来自camera intent或mediarecorder的都可以工作)。
我认为这是因为uri在传入服务时失去了它的权限,但是添加Intent.FLAG_GRANT_WRITE_URI_PERMISSIONIntent.FLAG_GRANT_READ_URI_PERMISSION没有帮助。
也尝试添加FLAG_GRANT_PERSISTABLE_URI_PERMISSION标志,但它仍然崩溃,并导致另一个getContentResolver().takePersistableUriPermission()崩溃,说所说的uri没有被授予持久uri…
上传服务.java

  //.......... code to prepare for upload

  if ( contentResolver != null && schemeContentFile ) {
            mMime = UtilMedia.getMime(this, uri);

            try {
                InputStream is     = contentResolver.openInputStream(uri);
                byte[]      mBytes = getBytes(is);

                Bundle fileDetail = UtilMedia.getFileDetailFromUri(this, uri);

                Log.d("AndroidRuntime", TAG + " " + mMime + " " + UtilToString.bundleToString(fileDetail) + " imageFile " + mFile);

                currTitle = fileDetail.getString(OpenableColumns.DISPLAY_NAME, "");
                MediaType type = MediaType.parse(mMime);
                requestFile = RequestBody.create(type, mBytes);

            } catch ( IOException e ) {
                e.printStackTrace();
            } catch ( SecurityException e ) {
                e.printStackTrace();
            }
        }

  //............continue to upload

提前谢谢你。
编辑(附加信息)
万一这很重要。调用服务的活动在将所有必需的数据发送到服务后调用SecurityException,允许用户使用应用程序,而在后台继续上载(通知用户)。而且,上传是基于队列的,用户可以选择在活动中上传多个文件。第一个文件总是被上传,但是之后的文件总是随着崩溃而返回。

最佳答案

我终于解决了这个问题。显然,这是因为给定uri的权限只有在接收活动处于活动状态时才有效。因此,将uri发送到后台服务(上传服务)将导致SecurityException如预期的那样,除非uri是一个可持久的uri(即fromACTION_OPEN_DOCUMENT)。
因此,我的解决方案是将文件复制到我的应用程序创建的文件中,并使用FileProvider.getUriForFile()函数获取uri并将其发送到后台服务,并在我的服务完成上载时删除副本。即使在调用活动完成后,此操作也可以正常工作。

10-06 06:15