Android相机意图在SDK版本大于等于24时出现FileUriExposedException异常

13
我使用这段代码从相机获取图片并将其放在ImageView上:

我使用这段代码从相机获取图片并将其放在ImageView上:

  private void openCamera()
{
    mMediaUri =getOutputMediaFileUri(MEDIA_TYPE_IMAGE);

    Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    photoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
    startActivityForResult(photoIntent, REQUEST_TAKE_PHOTO);
   // dialog.dismiss();


}


private Uri getOutputMediaFileUri(int mediaTypeImage)
{
    //check for external storage
    if(isExternalStorageAvaiable())
    {
        File mediaStorageDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);

        String fileName = "";
        String fileType = "";
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new java.util.Date());

        fileName = "IMG_"+timeStamp;
        fileType = ".jpg";

        File mediaFile;
        try
        {
            mediaFile = File.createTempFile(fileName,fileType,mediaStorageDir);
            Log.i("st","File: "+Uri.fromFile(mediaFile));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            Log.i("St","Error creating file: " + mediaStorageDir.getAbsolutePath() +fileName +fileType);
            return null;
        }
        return Uri.fromFile(mediaFile);
    }
    //something went wrong
    return null;

}

private boolean isExternalStorageAvaiable()
{
    String state = Environment.getExternalStorageState();

    if(Environment.MEDIA_MOUNTED.equals(state))
    {
        return true;
    }
    else
    {
        return false;
    }
}

 public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    if(resultCode == DIALOG_CODE)
    {
        String s = data.getStringExtra("choose");

        if(s.equals(getString(R.string.takephoto)))
        {
          openCamera();
        }
        else if(s.equals(getString(R.string.library)))
        {
         // openGallery();
        }
    }
    else if(resultCode == RESULT_OK)
    {
        if (requestCode == REQUEST_TAKE_PHOTO || requestCode == REQUEST_PICK_PHOTO) //dalla fotocamera
        {
            if (data != null)  //caso galleria
            {
                mMediaUri = data.getData();
            }


            Picasso.with(getActivity()).load(mMediaUri).resize(1280, 1280).centerCrop().into(photo, new Callback()
            {
                public void onSuccess()
                {


                }

                @Override
                public void onError() {

                }
            });
        }
    }
}

在Marshmallow中它可以正常工作,但在Nougat中我遇到了这个错误:

E/AndroidRuntime: FATAL EXCEPTION: main
              PID: 15471
              android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.plen.myapp/files/Pictures/IMG_20161214_1516561294620205.jpg exposed beyond app through ClipData.Item.getUri()
                  at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
                  at android.net.Uri.checkFileUriExposed(Uri.java:2346)
                  at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832)
                  at android.content.Intent.prepareToLeaveProcess(Intent.java:8925)
                  at android.content.Intent.prepareToLeaveProcess(Intent.java:8910)
                  at android.app.Instrumentation.execStartActivity(Instrumentation.java:1519)
                  at android.app.Activity.startActivityForResult(Activity.java:4224)
                  at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:48)
                  at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:77)
                  at android.support.v4.app.ActivityCompatJB.startActivityForResult(ActivityCompatJB.java:26)
                  at android.support.v4.app.ActivityCompat.startActivityForResult(ActivityCompat.java:146)
                  at android.support.v4.app.FragmentActivity.startActivityFromFragment(FragmentActivity.java:937)
                  at android.support.v4.app.FragmentActivity$HostCallbacks.onStartActivityFromFragment(FragmentActivity.java:1046)
                  at android.support.v4.app.Fragment.startActivityForResult(Fragment.java:956)
                  at android.support.v4.app.Fragment.startActivityForResult(Fragment.java:945)
                  at com.plen.myapp.MyAircrafts.AddAircraftFirstPartFragment.openCamera(AddAircraftFirstPartFragment.java:263)
                  at com.plen.myapp.MyAircrafts.AddAircraftFirstPartFragment.askReadStorage(AddAircraftFirstPartFragment.java:338)
                  at com.plen.myapp.MyAircrafts.AddAircraftFirstPartFragment.askWriteStorage(AddAircraftFirstPartFragment.java:355)
                  at com.plen.myapp.MyAircrafts.AddAircraftFirstPartFragment.loadNewPhoto(AddAircraftFirstPartFragment.java:159)
                  at com.plen.myapp.MyAircrafts.AddAircraftFirstPartFragment.access$000(AddAircraftFirstPartFragment.java:48)
                  at com.plen.myapp.MyAircrafts.AddAircraftFirstPartFragment$1.onClick(AddAircraftFirstPartFragment.java:75)
                  at android.view.View.performClick(View.java:5609)
                  at android.view.View$PerformClick.run(View.java:22266)
                  at android.os.Handler.handleCallback(Handler.java:751)
                  at android.os.Handler.dispatchMessage(Handler.java:95)
                  at android.os.Looper.loop(Looper.java:154)
                  at android.app.ActivityThread.main(ActivityThread.java:6077)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

我该如何解决这个错误?我已经搜索了很多解决方案,但没有一个有效。

谢谢


1
请参考以下链接,了解如何解决Android OS FileUriExposedException问题:https://dev59.com/zVoT5IYBdhLWcg3wvRpk (同时,您也可以查看这个示例项目:https://github.com/commonsguy/cw-omnibus/tree/master/Camera/FileProvider) - CommonsWare
@CommonsWare 如果我使用这个,它也可以在Jelly Bean上工作吗?还是我需要为之前的Android版本使用其他代码? - ste9206
但是如果我使用这个,它也可以在果冻豆上工作吗?-- 如果“这个”是FileProvider,问题不在于Android操作系统版本,而在于用户的相机应用程序是否支持content作为EXTRA_OUTPUTUri的方案。有些会,有些不会。如果“这个”是StrictMode方法,据我所知,在所有Android版本中都可以正常使用,因为它还原了FileUriExposedException更改,并允许您再次使用file方案。 - CommonsWare
2个回答

37

不要使用return Uri.fromFile(mediaFile);,而是使用

return FileProvider.getUriForFile(MainActivity.this,
    BuildConfig.APPLICATION_ID + ".provider",
    mediaFile);

这将需要您向AndroidManifest中添加一个提供程序:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<application
    ...
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
</application>

对于AndroidX,请使用 androidx.core.content.FileProvider

然后在res文件夹下的xml文件夹中创建一个名为provider_paths.xml的文件。

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

阅读更多:完整文章


1
官方文档 - https://developer.android.com/training/secure-file-sharing/setup-sharing - Nishanth

19

以下是简单的操作步骤。

将这两行代码放在您活动的onCreate方法中即可。

StrictMode.VmPolicy.Builder newbuilder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(newbuilder.build());

虚拟机将忽略URI暴露问题,我已经解决了。


1
这似乎关闭了很多东西。 - TheRealChx101
1
它将无缝运行。我在这段代码中没有发现任何新的错误或故障。 - deva11
1
是的,你说得对。它可以工作。它只是看起来像你正在安装一个未配置的策略对象。但它可以工作。 - TheRealChx101

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