一、7.0系统,不再将应用程序的私有程序向使用者放宽,随之带来的就是你的App对外无法暴露file://类型的URI了;如果继续使用intent携带file://类型的uri去访问其他应用,比如说相机、安装apk等则会抛出FileUriExposedException异常;
二、7.0之前,有种统一的调用相机的方式,细分下也可以说是两种,因为笔者就是用的第二种非主流的方式,当然,第二种仅限拓展,其实跟第一种也没什么区别,只是依然可以在7.0系统上运行,代码如下:
String cachePath = getApplicationContext().getExternalCacheDir().getPath();
File picFile = new File(cachePath, "test.jpg");
Uri picUri = Uri.fromFile(picFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,picUri);
startActivityForResult(intent, PHOTO_ONE_REQUEST_CODE);
这种方式在7.0之前可以正常无误的使用,但是在7.0以及以上版本则会抛出FileUriExposedException异常;
第二种笔者拓展的方法
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, PHOTO_TWO_REQUEST_CODE);
有同学看了可能会扔板砖了,这他喵的不一样么!!!
确实是一样的,不一样的是第一种在intent之中传了个路径,有了这个路径,相机拍完照之后是不会将图片保存到相册的,也就是说相册里面没有刚才拍的照;同时,重点来了 ,在onActivityResult的回调中intent类型的参数是没有值的 也就是null
同样的第二种方法,onActivityResult回调中intent类型的参数是有值的,我们可以根据这个值来获取到bitmap对象,然后这样这样。。hh
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data != null) {
String sdStatus = Environment.getExternalStorageState();
// 检测sd是否可用
if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) {
Log.i("小川",
"SD card is not avaiable/writeable right now.");
return;
}
Bundle bundle = data.getExtras();
// 获取相机返回的数据,并转换为Bitmap图片格式
Bitmap bitmap = (Bitmap) bundle.get("data");
mImageView.setImageBitmap(bitmap);
} else {
Log.i("小川", " 打开相机 空集");
}
}
ps:当然这种方式不太推荐,大家还是按照正常的、谷歌推荐的方式来写
三、使用FileProvider
查看FileProvider的父类可知,FileProvider其实是个ContentProvider,不过不在本文的范围之内,有关的ContentProvider的知识,请自行度娘谷歌;
使用FileProvider首先androidMainfest文件中注册:
<provider
android:name="com.chuan.jun.UpdateFileProvider"
android:authorities="com.feihong.txgw.updatefileprovider"
android:exported="false"
android:grantUriPermissions="true"
android:initOrder="101" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/update_cache_path"/>
</provider>
android:name 【固定值】 FileProvider的包名+类名
这里可以直接引用v4包下面的provider 也可以去自定义实现provider类
笔者这里是实现的provider子类,都是一样的
android:name="android.support.v4.content.FileProvider"
import android.support.v4.content.FileProvider;
public class UpdateFileProvider extends FileProvider {
}
android:authorities 【自定义】 推荐以包名+”.fileprovider”方式命名,系统唯一
android:exproted 要求必须为false,为true则会报安全异常
android:grantUriPermissions 是否允许为文件设置临时权限
android:initOrer: 优先级
android:resource="@xml/update_cache_path"就是我们的共享路径配置的xml文件(此文件需要在res文件夹下面创建一个名字为xml的目录,然后在此目录下创建一个资源文件,这里的资源文件名称为update_cache_path)
关于这个资源文件的内容
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="update_cache" path=""/>
<!--<external-path name="external_path" path=""/>-->
<!--<root-path path="txgw" name="cache" />-->
<!--/ path可以为空 表示指定目录下的所有文件、文件夹 都可以被共享-->
<file-path name="my_image" path=""/>
<!--物理路径相当于Context.getFilesDir() + /path/-->
<!--<files-path name="name" path="path" />-->
<!--物理路径相当于Context.getCacheDir() + /path/-->
<!--<cache-path name="name" path="path" />-->
<!--物理路径相当于Environment.getExternalStorageDirectory() + /path/。-->
<!--<external-path name="name" path="path" />-->
<!--物理路径相当于Context.getExternalFilesDir(String) + /path/。-->
<!--注意:external-cache-path在support-v4:24.0.0这个版本并未支持,直到support-v4:25.0.0才支持-->
<!--<external-files-path name="name" path="path" />-->
<!--物理路径相当于Context.getExternalCacheDir() + /path/。-->
<!--<external-cache-path name="name" path="path" />-->
<!--如果你想使用外置SD卡,可以用这个:物理路径相当于/path/-->
<!--<root-path name="name" path="path" />-->
</paths>
ps:具体用那种类型,取决于你文件存放的位置
准备工作到这已经差不多了
四、调用相机
public class AndroidNActivity extends BaseActivity {
private Toolbar mToolBar;
private AppCompatImageView mImageView;
private final static int PHOTO_ONE_REQUEST_CODE = 1;
private final static int PHOTO_TWO_REQUEST_CODE = 2;
private final static int PHOTO_THREE_REQUEST_CODE = 3;
private final static int IMG_REQUEST_CODE = 4;
private final static int CROP_REQUEST_CODE = 5;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_n_layout);
mToolBar = (Toolbar) findViewById(R.id.android_N_ToolBar);
setSupportActionBar(mToolBar);
mToolBar.setTitleTextColor(getResources().getColor(R.color.colorAccent));
mImageView = (AppCompatImageView) findViewById(R.id.local_img_show);
}
public void openImgsOrPhtoto(View view) {
switch (view.getId()) {
case R.id.openImg:
openImgs();
break;
case R.id.openPhoto:
openPhoto();
break;
default:
break;
}
}
/**
* 打开相机
*/
private void openPhoto() {
// 相机
/**
* 7.0以前有两种写法 也可以理解为一种
* 1)打开的相机的时候 传一个保存路径进去 然后拍下来的照片会保存到这个路径里面 相应的intent返回的是null
* 2)直接打开相机 不传路径进去 这时候 需要在拿到这个图片的时候 再去手动的将图片保存到本地 intent返回不为null
* 网上大多都是第一种方案 也是系统推荐的一种方案
* 但是在7.0以后 就会抛出FileUriExposedException异常
* 第二种方案 可以正常使用 但是仅供参考 因为笔者以前用的就是这种非主流方案
*
* 在7.0以后的正常方案则是使用FileProvider
*/
/**
* 7.0以前第一种方案 (会报异常)
*/
// String cachePath = getApplicationContext().getExternalCacheDir().getPath();
// File picFile = new File(cachePath, "test.jpg");
// Uri picUri = Uri.fromFile(picFile);
// Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// intent.putExtra(MediaStore.EXTRA_OUTPUT,picUri);
// startActivityForResult(intent, PHOTO_ONE_REQUEST_CODE);
/**
* 7.0以前第二种非主流方案
*/
// Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// startActivityForResult(intent, PHOTO_TWO_REQUEST_CODE);
/**
* 7.0以上的正确姿势
*/
String cachePath = getApplicationContext().getExternalCacheDir().getPath();
File picFile = new File(cachePath, "imgName.jpg");
Uri contentUri = getUriForFile(this,
"com.feihong.txgw.updatefileprovider", picFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
intent.putExtra(MediaStore.EXTRA_OUTPUT,contentUri);
startActivityForResult(intent, PHOTO_THREE_REQUEST_CODE);
}
/**
* 打开相册
*/
private void openImgs() {
Intent intent = new Intent(Intent.ACTION_PICK);
//相片类型 相册
intent.setType("image/*");
startActivityForResult(intent, IMG_REQUEST_CODE);
}
private File localCropPath;// 裁剪之后的图片保存路径
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case PHOTO_ONE_REQUEST_CODE:
if (data != null) {
// 这里data必为null
} else {
Log.i("小川", " 打开相机 空集");
}
break;
case PHOTO_TWO_REQUEST_CODE:
String sdStatus = Environment.getExternalStorageState();
// 检测sd是否可用
if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) {
Log.i("小川",
"SD card is not avaiable/writeable right now.");
return;
}
Bundle bundle = data.getExtras();
// 获取相机返回的数据,并转换为Bitmap图片格式
Bitmap bitmap = (Bitmap) bundle.get("data");
mImageView.setImageBitmap(bitmap);
break;
case PHOTO_THREE_REQUEST_CODE:
/**
* 7.0正确姿势 以及裁剪(注意uri格式)
*/
// 拍照之后的照片路径的 uri
Uri contentUri = getUriForFile(this,
"com.feihong.txgw.updatefileprovider", new File(getApplicationContext().getExternalCacheDir().getPath() + File.separator + "imgName.jpg"));
Uri desUri = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String storage = getApplicationContext().getExternalCacheDir().getPath();
File dirFile = new File(storage + File.separator+"orgImg");
if(dirFile.exists()){
dirFile.delete();
}
dirFile.mkdirs();
localCropPath = new File(dirFile, "crop.jpg");
desUri = Uri.fromFile(localCropPath);
Intent intent = new Intent("com.android.camera.action.CROP");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
intent.setDataAndType(contentUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1); // 裁剪框比例
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 320); // 输出图片大小
intent.putExtra("outputY", 320);
intent.putExtra("scale", true); // 是否可移动
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); // 返回一个文件
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
startActivityForResult(intent, CROP_REQUEST_CODE);
}
break;
case IMG_REQUEST_CODE:
/**
* 从相册选择
*/
if (data != null) {
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
mImageView.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}
} else {
Log.i("小川", "打开相册: 空集");
}
break;
case CROP_REQUEST_CODE:
/**
* 裁剪
*/
if(data != null){
// 读取uri所在的图片
FileOutputStream outputStream = null;
try {
Bitmap photoBitmap = MediaStore.Images.Media.getBitmap(getApplication().getContentResolver(), Uri.fromFile(localCropPath));
mImageView.setImageBitmap(photoBitmap);
} catch (IOException e) {
e.printStackTrace();
}
}else {
Log.i("小川", " 图片裁剪 空集");
}
break;
default:
break;
}
}
}
五、关于安装apk
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
} else {
Uri uri = FileProvider.getUriForFile(context, context.getPackageName()+"updatefileprovider", file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
ok 7.0的共享文件权限到这就没啦 希望能帮助大家!!!
网友评论