Android调用相机无法执行onActivityResult或data为null

最近项目中调用系统相机时遇到了这么个问题:红米手机无法执行onActivityResult回调,部分华为手机无法获取到照片数据,最后各种分析,各种网上搜索资料折腾了好久,最后终于把问题解决,当看到问题的最终原因时,我也是醉了,简直就是坑爹坑大发了。

最开始用红米手机调用系统相机的时,当我拍照完成点击那个勾勾始终无法返回到前面的界面上,也就是无法调用onActivityResult函数取到拍照的照片,在网上搜索了一圈,没找到解决办法,都说是红米的系统问题,无法解决,项目比较紧张,刚好客户的手机全部是统一型号的华为荣耀6Plus,这个问题丢着暂时没管了。
后来现场同事批量测试机器时,发现有3台手机可以拍照,但是在onActivityResult回调中获取到的照片数据为null,但是其他的同型号手机又没这个问题,这问题看着真是坑爹啊。

远程调试代码后发现,我在调用系统相机时设置的照片存放目录无法创建成功,我调用系统相机的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void takePicture() {
//照片存放目录
File file = new File(mPicDirectory);
if(!file.exists()) {//目录不存在则创建该目录及其不存在的父目录
file.mkdirs();
}
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mPicName = getPictureName();//获取照片名称
mPicPath = mPicDirectory + mPicName;//照片存储路径
//将照片保存到mPicPath位置
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mPicPath)));
startActivityForResult(intent, CODE_RESULT_TAKE_PHOTO);
}

我调试的时候在onActivityResult回调里通过照片路径获取Bitmap对象的时候发现获取到的Bitmap对象一直为null,所以就猜想是不是照片没保存成功,我在文件管理系统里去找这个照片的时候发现,连照片存储目录mPicDirectory都不存在,我再次跟踪代码发现file.mkdirs()始终返回的都是false。看来问题的最终原因是这个:无法创建目录和文件(华为手机系统在拍照输出的时候,如果输出文件路径不存在时竟然没有任何提示和异常,这点对于发现问题的根源有点困难)。然后我又去检查了权限、手机设置里面的权限等所有有可能涉及的问题,结果依然没有效果。既然这些都不是导致目录创建失败的原因,那会不会是目录路径本身不正确呢,我的目录路径是这样获取的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
String mPicDirectory = FileManager.getPath() + Configuration.SYS_PHOTO_PATH;
//FileManager的部分代码是这样的,这个代码是以前的同事写的,没有细看过:
public static String getPath(){
boolean sdCardExist = Environment.getExternalStorageState()
.equals(android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在
return !sdCardExist ? MOBILE_PATH : SD_PATH;
}
//sdcard路径
private static String SD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
//应用数据路径
public static final String MOBILE_PATH = Environment.getDataDirectory().getAbsolutePath();
//这里有一段静态代码块
static{
File f = new File("/storage/sdcard1");
if(f.exists()){
long size = getFreeSize("/storage/sdcard1");
long sd0Size = getFreeSize("/storage/sdcard0");
if(size > sd0Size){
SD_PATH = "/storage/sdcard1";
}
}
}

当我看到上面的静态代码块的时候,我立马就知道了问题的原因,静态代码块中做了一个剩余空间判断,选取的是空间比较大的那个sdcard路径,而且代码都是写死的
Environment.getExternalStorageDirectory().getAbsolutePath()系统这个代码是获取手机里的内置SDCard的路径,我的红米手机自己又额外插了一张内存卡,这个是手机的外接SDCard,用Environment.getExternalStorageDirectory()获取到只是手机自带的外置SDCard路径,不能获取自己插入的内存卡路径,我的手机通过Environment.getExternalStorageDirectory()方式获取的路径是/storage/sdcard0(我手机内置的SDCard),而通过可用空间比较之后发现该卡的可用空间少于/storage/sdcard1(我自己插入的SDCard),所以最终路径变成了/storage/sdcard1,而在Android4.4以上后,不允许三方App对外接的SDCard进行操作,因此我们队最后得到的这个目录是没有写入权限的,所以我创建照片存储目录的时候一直失败,所以拍照的时候照片数据没有地方可以输出,华为手机是直接体现在照片数据无法获取,而红米手机则是直接不允许返回到前面的调用界面。

Google在Android中限制三方App操作外接SDCard的原文如下:

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions.

后来想了想,调用相机这么简单的一个功能,红米的系统应该不可能给阉割掉,所以出了问题还是老老实实的先找自己代码的问题吧。至于那个外接SDCard的写入权限还有待寻找答案。

系统内置SDCard写入、删除、读写权限:

1
2
3
4
//文件创建、删除权限
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
//文件读写权限,只针对于手机内存的SDCard,对外接SDCard无效
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

原创文章,本文采用知识共享署名 2.5(中国大陆许可协议)进行许可,欢迎转载,但转载请注明来自ittiger.cn,并保证转载后文章内容的完整性。本人(laohu)保留所有版权相关权利。



评论