Android N中带相机的FileUriExposedException

主要活动

import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Parcelable; import android.provider.MediaStore; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button mBtn; private Context context; private static final int SELECT_PICTURE_CAMARA = 101, SELECT_PICTURE = 201, CROP_IMAGE = 301; private Uri outputFileUri; String mCurrentPhotoPath; private Uri selectedImageUri; private File finalFile = null; private ImageView imageView; private PermissionUtil permissionUtil; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtn = (Button) findViewById(R.id.btn_img); imageView = (ImageView) findViewById(R.id.img_photo); permissionUtil = new PermissionUtil(); mBtn.setOnClickListener(this); context = this; } @Override public void onClick(View view) { selectImageOption(); } private void selectImageOption() { final CharSequence[] items = {"Capture Photo", "Choose from Gallery", "Cancel"}; AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Add Photo!"); builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { if (items[item].equals("Capture Photo")) { if (permissionUtil.checkMarshMellowPermission()) { if (permissionUtil.verifyPermissions(MainActivity.this, permissionUtil.getCameraPermissions())) onClickCamera(); else ActivityCompat.requestPermissions(MainActivity.this, permissionUtil.getCameraPermissions(), SELECT_PICTURE_CAMARA); } else onClickCamera(); } else if (items[item].equals("Choose from Gallery")) { if (permissionUtil.checkMarshMellowPermission()) { if (permissionUtil.verifyPermissions(MainActivity.this, permissionUtil.getGalleryPermissions())) onClickGallery(); else ActivityCompat.requestPermissions(MainActivity.this, permissionUtil.getGalleryPermissions(), SELECT_PICTURE); } else onClickGallery(); } else if (items[item].equals("Cancel")) { dialog.dismiss(); } } }); builder.show(); } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == SELECT_PICTURE) { selectedImageUri = data.getData(); cropImage(selectedImageUri); } else if (requestCode == CROP_IMAGE) { /*if (data != null) { // get the returned data Bundle extras = data.getExtras(); // get the cropped bitmap Bitmap selectedBitmap = extras.getParcelable("data"); imageView.setImageBitmap(selectedBitmap); }*/ Uri imageUri = Uri.parse(mCurrentPhotoPath); File file = new File(imageUri.getPath()); try { InputStream ims = new FileInputStream(file); imageView.setImageBitmap(BitmapFactory.decodeStream(ims)); } catch (FileNotFoundException e) { return; } } else if (requestCode == SELECT_PICTURE_CAMARA && resultCode == Activity.RESULT_OK) { cropImage(Uri.parse(mCurrentPhotoPath)); } } } private void onClickCamera() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) { File photoFile = null; try { photoFile = createImageFile(); } catch (IOException ex) { } if (photoFile != null) { Uri photoURI; if (Build.VERSION.SDK_INT >= 24) { photoURI = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", photoFile); } else { photoURI = Uri.fromFile(photoFile); } takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, SELECT_PICTURE_CAMARA); } } } private void onClickGallery() { List<Intent> targets = new ArrayList<>(); Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_PICK); intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); List<ResolveInfo> candidates = getApplicationContext().getPackageManager().queryIntentActivities(intent, 0); for (ResolveInfo candidate : candidates) { String packageName = candidate.activityInfo.packageName; if (!packageName.equals("com.google.android.apps.photos") && !packageName.equals("com.google.android.apps.plus") && !packageName.equals("com.android.documentsui")) { Intent iWantThis = new Intent(); iWantThis.setType("image/*"); iWantThis.setAction(Intent.ACTION_PICK); iWantThis.putExtra(Intent.EXTRA_LOCAL_ONLY, true); iWantThis.setPackage(packageName); targets.add(iWantThis); } } if (targets.size() > 0) { Intent chooser = Intent.createChooser(targets.remove(0), "Select Picture"); chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targets.toArray(new Parcelable[targets.size()])); startActivityForResult(chooser, SELECT_PICTURE); } else { Intent intent1 = new Intent(Intent.ACTION_PICK); intent1.setType("image/*"); startActivityForResult(Intent.createChooser(intent1, "Select Picture"), SELECT_PICTURE); } } private File createImageFile() throws IOException { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents if (Build.VERSION.SDK_INT >= 24) { mCurrentPhotoPath = String.valueOf(FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", image)); } else { mCurrentPhotoPath = String.valueOf(Uri.fromFile(image)); } return image; } private void cropImage(Uri selectedImageUri) { Intent cropIntent = new Intent("com.android.camera.action.CROP"); cropIntent.setDataAndType(selectedImageUri, "image/*"); cropIntent.putExtra("crop", "true"); cropIntent.putExtra("aspectX", 1); cropIntent.putExtra("aspectY", 1.5); cropIntent.putExtra("return-data", true); outputFileUri = Uri.fromFile(createCropFile()); cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri); startActivityForResult(cropIntent, CROP_IMAGE); } private File createCropFile() { File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); // path = path + (timeStamp + "1jpg"); File file = null; try { file = File.createTempFile(timeStamp, ".jpg", storageDir); } catch (IOException e) { e.printStackTrace(); } mCurrentPhotoPath = String.valueOf(Uri.fromFile(file)); return file; } } 

PermissionUtil.java

 package com.example.shwetachauhan.imagecropasoebi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.support.v4.app.ActivityCompat; public class PermissionUtil { private String[] galleryPermissions = { "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE" }; private String[] cameraPermissions = { "android.permission.CAMERA", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE" }; public String[] getGalleryPermissions(){ return galleryPermissions; } public String[] getCameraPermissions() { return cameraPermissions; } public boolean verifyPermissions(int[] grantResults) { if(grantResults.length < 1){ return false; } for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } public boolean verifyPermissions(Context context, String[] grantResults) { for (String result : grantResults) { if (ActivityCompat.checkSelfPermission(context, result) != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } public boolean checkMarshMellowPermission(){ return(Build.VERSION.SDK_INT> Build.VERSION_CODES.LOLLIPOP_MR1); } public boolean checkJellyBean(){ return(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN); } } 
  • 此代码用于从相机或图库中选取作物的图像
  • 此代码是适用于所有Android操作系统,但是当我尝试在Android N设备上运行时,它会崩溃,当我打开相机。 它在Android N与图库工作正常。

Solutions Collecting From Web of "Android N中带相机的FileUriExposedException"

我在Android N设备上也面临同样的问题 但我解决了它。

这是我的代码,可以解决这个问题:

  public void launchCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri()); } else { File file = new File(getPhotoFileUri().getPath()); Uri photoUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName() + ".provider", file); intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); } intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); if (intent.resolveActivity(getApplicationContext().getPackageManager()) != null) { startActivityForResult(intent, REQUEST_CAMERA); } } 

在此之后,您需要在res中创build一个XML文件夹 ,并在该文件夹中创build一个xml标签provider_paths.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> 

稍后在清单中,您需要在应用程序标记内添加以下内容,并确保compileSdkVersion> = 24

 <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> 

这里有两个参考链接可以更好地指导你更好地理解。

链接:

link1 – 从中等合作

link2 – 来自inthecheesefactory

如果你没有接触到系统中的其他应用程序

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

在你的Application.onCreate() 。 它会忽略URI的暴露,你很好去。

从Android N开始,android已经改变了你提供文件URI的方式。 使用file:// uri是被禁止的,会抛出这个。 使用FileProvider来克服这一点。

在包域之外传递file:// URI可能会使接收器无法访问path。 因此,尝试传递file:// URI会触发FileUriExposedException。 分享私人文件内容的推荐方法是使用FileProvider。

更多信息可以在这里find

改变你的buildToolsVersion

  buildToolsVersion "25.0.1" 

  buildToolsVersion "23.0.1"