之前在做新项目是把以前的上传头像功能复制过来,运行发现闪退了,当时就是各种排除,然后用模拟器运行是没问题的,但是用真机运行就报错,然后各种筛查,还是无果,然后我就想是不是手机问题手机不兼容,于是我用相同型号的手机测试发现同样可以使用就我的手机不能使用,当时就陷入沉思,唉是不是系统问题,我查看了手机系统版本,我的是最新的系统android7.0 另外的是android5.0 .于是我上网查了下发现果然是,当时教程很少
问题原因:主要是由于在Android 7.0以后,用了Content Uri 替换了原本的File Uri,故在targetSdkVersion=24的时候,部分 “`Uri.fromFile()“` 方法就不适用了。 **File Uri 与 Content Uri 的区别** - File Uri 对应的是文件本身的存储路径 - Content Uri 对应的是文件在Content Provider的路径 所以在android 7.0 以上,我们就需要将File Uri转换为 Content Uri
这个是图片路径改变问题,同样的我们拍照后存储的图片地址也要经过转换 好下面我们一步步实现
首先新建一个项目在res 文件夹下新建文件夹xml 新建文件 file_path 名字随便写 如下
然后在AndroidManifest 里面配置 如
<provider android:name="android.support.v4.content.FileProvider" android:authorities="自己包名.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
关于FileProvider
FileProvider 是 ContentProvider 的一个特殊的子类,它有利于安全地分享应用相关的文件,通过对一个文件创建content:// Uri而不是file:/// Uri。
由于FileProvider的默认功能包括文件的content URI的生成,你并不需要在代码中定义一个子类。相反,你可以在你的应用中包含一个FileProvider通过在XML文件中指定它。对于指定FileProvider,添加一个元素在你应用的清单文件中。设置android:name属性为android.support.v4.content.FileProvider。根据你控制的域名设置android:authorities属性为一个URI authority(authorities可以随意填写,但是要保证使用时与authority保持一致,推荐applicationId.fileprovider,以免定义重复)。设置android:exported属性为false;FileProvider不需要公开。设置android:grantUriPermissions属性为true,为了允许你进行临时访问文件的授权。
现在开始查看逻辑代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener { public static final int TAKE_PHOTO = 1; public static final int CHOOSE_PHOTO = 2; public static final int CROP_PHOTO = 3; private String cachPath; private File cacheFile; private File cameraFile; private Uri imageUri; //相机 private Button camera; //相册 private Button album; //裁剪后显示照片 private ImageView imageView; //动态获取权限监听 private static PermissionListener mListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cachPath=getDiskCacheDir(this)+ "/handimg.jpg";//图片路径 cacheFile =getCacheFile(new File(getDiskCacheDir(this)),"handimg.jpg"); //输出地址 camera= (Button) findViewById(R.id.button0); album= (Button) findViewById(R.id.button1); imageView= (ImageView) findViewById(R.id.imageView); camera.setOnClickListener(this); album.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.button0: takePhotoForCamera(); break; case R.id.button1: takePhotoForAlbum(); break; } } private void takePhotoForAlbum() { String[] permissions={Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE}; requestRuntimePermission(permissions, new PermissionListener() { @Override public void onGranted() { openAlbum(); } @Override public void onDenied(List<String> deniedPermission) { //没有获取到权限,什么也不执行,看你心情 } }); } private void openAlbum() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, CHOOSE_PHOTO); // 打开相册 } private void takePhotoForCamera() { String[] permissions={Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}; requestRuntimePermission(permissions, new PermissionListener() { @Override public void onGranted() { openCamera(); } @Override public void onDenied(List<String> deniedPermission) { //有权限被拒绝,什么也不做好了,看你心情 } }); } private void openCamera() { cameraFile = getCacheFile(new File(getDiskCacheDir(this)),"output_image.jpg"); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { imageUri = Uri.fromFile(cameraFile); } else { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); imageUri = FileProvider.getUriForFile(MainActivity.this, "com.wing.phototest.fileprovider", cameraFile); } // 启动相机程序 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, TAKE_PHOTO); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_CANCELED) { return; } switch (requestCode) { case TAKE_PHOTO: if (resultCode == RESULT_OK) { try { // 将拍摄的照片显示出来 startPhotoZoom(cameraFile,350); } catch (Exception e) { e.printStackTrace(); } } break; case CHOOSE_PHOTO: if (resultCode == RESULT_OK) { // 判断手机系统版本号 if (Build.VERSION.SDK_INT >= 19) { // 4.4及以上系统使用这个方法处理图片 handleImageOnKitKat(data); } else { // 4.4以下系统使用这个方法处理图片 handleImageBeforeKitKat(data); } } break; case CROP_PHOTO: try { if (resultCode==RESULT_OK){ Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(Uri.fromFile(new File(cachPath)))); imageView.setImageBitmap(bitmap); } }catch (Exception e){ e.printStackTrace(); } break; } } private File getCacheFile(File parent, String child) { // 创建File对象,用于存储拍照后的图片 File file = new File(parent, child); if (file.exists()) { file.delete(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } return file; } @TargetApi(19) private void handleImageOnKitKat(Intent data) { Uri uri = data.getData(); Log.d("TAG", "handleImageOnKitKat: uri is " + uri); String imagePath= uriToPath(uri); // displayImage(imagePath); // 根据图片路径显示图片 Log.i("TAG","file://"+imagePath+"选择图片的URI"+uri); startPhotoZoom(new File(imagePath),350); } private String uriToPath(Uri uri) { String path=null; if (DocumentsContract.isDocumentUri(this, uri)) { // 如果是document类型的Uri,则通过document id处理 String docId = DocumentsContract.getDocumentId(uri); if("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; // 解析出数字格式的id String selection = MediaStore.Images.Media._ID + "=" + id; path = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); path = getImagePath(contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是content类型的Uri,则使用普通方式处理 path = getImagePath(uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 如果是file类型的Uri,直接获取图片路径即可 path = uri.getPath(); } return path; } private void handleImageBeforeKitKat(Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null); // displayImage(imagePath); Log.i("TAG","file://"+imagePath+"选择图片的URI"+uri); startPhotoZoom(new File(imagePath),350); } private String getImagePath(Uri uri, String selection) { String path = null; // 通过Uri和selection来获取真实的图片路径 Cursor cursor = getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; } /** * 剪裁图片 */ private void startPhotoZoom(File file, int size) { Log.i("TAG",getImageContentUri(this,file)+"裁剪照片的真实地址"); try { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(getImageContentUri(this,file), "image/*");//自己使用Content Uri替换File Uri intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 180); intent.putExtra("outputY", 180); intent.putExtra("scale", true); intent.putExtra("return-data", false); intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));//定义输出的File Uri intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); startActivityForResult(intent, CROP_PHOTO); } catch (ActivityNotFoundException e) { String errorMessage = "Your device doesn't support the crop action!"; Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); } } /** * 将图片存到本地 * 获得本地图片路径 * @param context * @return */ public String getDiskCacheDir(Context context) { String cachePath = null; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) { cachePath = context.getExternalCacheDir().getPath(); } else { cachePath = context.getCacheDir().getPath(); } return cachePath; } /** * 转化地址为content开头 * @param context * @param imageFile * @return */ public static Uri getImageContentUri(Context context, File imageFile) { String filePath = imageFile.getAbsolutePath(); Cursor cursor = context.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ", new String[] { filePath }, null); if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor .getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { if (imageFile.exists()) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DATA, filePath); return context.getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { return null; } } } //andrpoid 6.0 需要写运行时权限 public void requestRuntimePermission(String[] permissions, PermissionListener listener) { mListener = listener; List<String> permissionList = new ArrayList<>(); for (String permission : permissions) { if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) { permissionList.add(permission); } } if (!permissionList.isEmpty()) { ActivityCompat.requestPermissions(MainActivity.this, permissionList.toArray(new String[permissionList.size()]), 1); } else { mListener.onGranted(); } } @TargetApi(23) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case 1: if (grantResults.length > 0) { List<String> deniedPermissions = new ArrayList<>(); for (int i = 0; i < grantResults.length; i++) { int grantResult = grantResults[i]; String permission = permissions[i]; if (grantResult != PackageManager.PERMISSION_GRANTED) { deniedPermissions.add(permission); } } if (deniedPermissions.isEmpty()) { mListener.onGranted(); } else { mListener.onDenied(deniedPermissions); } } break; default: break; } } public interface PermissionListener { /** * 成功获取权限 */ void onGranted(); /** * 为获取权限 * @param deniedPermission */ void onDenied(List<String> deniedPermission); } }
效果图
项目地址: http://download.youkuaiyun.com/detail/liufatao/9844983