最近对以前的Android项目进行开发时,因为新的平板Android中系统版本为10,将旧项目的Android API升级为26。然后旧代码中的拍照权限失效,经过查询资料,发现权限并未打开,因此需要提醒用户,手动打开权限。
// 拍照的requestCode
private static final int CAMERA_REQUEST_CODE = 0x00000010;
// 申请相机权限的requestCode
private static final int PERMISSION_CAMERA_REQUEST_CODE = 0x00000012;
/**
* 检查权限并拍照。
* 调用相机前先检查权限。
*/
private void checkPermissionAndCamera() {
int hasCameraPermission = ContextCompat.checkSelfPermission(getApplication(),
Manifest.permission.CAMERA);
if (hasCameraPermission == PackageManager.PERMISSION_GRANTED) {
//有权限,调起相机拍照。
openCamera();
} else {
//没有权限,申请权限。
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},
PERMISSION_CAMERA_REQUEST_CODE);
}
}
/**
* 处理权限申请的回调。
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSION_CAMERA_REQUEST_CODE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//允许权限,有调起相机拍照。
openCamera();
} else {
//拒绝权限,弹出提示框。
Toast.makeText(this,"拍照权限被拒绝",Toast.LENGTH_LONG).show();
}
}
}
/**
* 调起相机拍照
*/
private void openCamera() {
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 判断是否有相机
if (captureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
Uri photoUri = null;
if (isAndroidQ) {
// 适配android 10
photoUri = createImageUri();
} else {
try {
photoFile = createImageFile();
} catch (IOException e) {
e.printStackTrace();
}
if (photoFile != null) {
mCameraImagePath = photoFile.getAbsolutePath();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//适配Android 7.0文件权限,通过FileProvider创建一个content类型的Uri
photoUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile);
} else {
photoUri = Uri.fromFile(photoFile);
}
}
}
mCameraUri = photoUri;
if(mCameraUri == null)
Log.e("Test", "mCameraUri 获取为空");
else
Log.e("Test", "mCameraUri 获取不为空");
if (photoUri != null) {
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
captureIntent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
try {
startActivityForResult(captureIntent, CAMERA_REQUEST_CODE);
}catch (Exception e){
System.out.println(e.getMessage());
sendMsg(handler,error,e.getMessage());
}
}
}
}
/**
* 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
*
* @return 图片的uri
*/
private Uri createImageUri() {
String status = Environment.getExternalStorageState();
// 判断是否有SD卡,优先使用SD卡存储,当没有SD卡时使用手机存储
if (status.equals(Environment.MEDIA_MOUNTED)) {
return getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
} else {
return getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());
}
}
/**
* 创建保存图片的文件
* @return
* @throws IOException
*/
private File createImageFile() throws IOException {
String imageName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (!storageDir.exists()) {
storageDir.mkdir();
}
File tempFile = new File(storageDir, imageName);
if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
return null;
}
return tempFile;
}
相机拍照成功后的处理
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CAMERA_REQUEST_CODE:
if (resultCode == RESULT_OK) {
if (isAndroidQ) {
imgPhoto.setImageURI(mCameraUri);//imgPhoto 为界面中ImageView控件
} else {
// 使用图片路径加载
imgPhoto.setImageBitmap(BitmapFactory.decodeFile(mCameraImagePath));
}
} else {
Toast.makeText(this,"取消",Toast.LENGTH_LONG).show();
}
break;
}
}
至此,已经可以成功调用相机进行拍照。但是在程序的测试过程中,发现并非每次都能够将照片展示出来,且伴随着程序的崩溃,但本地却以将照片存储。且在Android Studio中并未爆出错误,同过不断的进行Log日志的输出,发现mCameraUri有时会null。在Logcat中也无法捕获程序异常。但在每次调用相机时,可看到Logcat中线程标注为DEAD。通过网络查询资料(Android onSaveInstanceState()和onRestoreInstanceState()调用时机_此去正年少的博客onsaveinstancestate),发现是Activity被回收导致mCameraUri置为空。
根据资料可得:
总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
本项目将布局一直固定为横屏,在调用相机时,相机方向可横屏或竖屏,会触发上述第五种情况。
因此在当前Activity中重写 onSaveInstanceState()与 onRestoreInstanceState()两个方法即可。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String imgUri= mCameraUri.toString();
outState.putString("imgUri",imgUri);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState!=null){
String imgUri=savedInstanceState.getString("imgUri");
mCameraUri = Uri.parse(imgUri);
}
}
同样,网上还有一种解决方案,是针对Activity没有被回收,但是成员变量被回收的情况,解决方案为重写onRestoreInstanceState()方法,暂时未遇到,先留存记录。
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mCameraUri == null) {
String imgUri=savedInstanceState.getString("imgUri");
mCameraUri = Uri.parse(imgUri);
}
super.onRestoreInstanceState(savedInstanceState);
}