Android6.0和7.0上遇到的坑以及解决方法
来源:互联网 发布:linux开源代码 编辑:程序博客网 时间:2024/05/21 01:56
android系统的版本已经更新到了8.0了。根据统计版本的分布已经从过去的2.x推进到4.x以上了。所以开发中已经几乎可以不考虑2.x等版本了。
然后像6.0以上的份额也越来越多。所以开发中是有必要考虑6.0以上版本的。
现在比较新的版本中,6.0(API23 VERSION_CODES M )和7.0(API24 VERSION_CODES N)的安全性大大提高。对权限的要求也高了。所以以前4.X、5.X的上运行没问题的程序,到了6.X,7.X上都可能会出现一些问题。
这是我前些天在我的手机上测试时候发现的。之前用的都是4.4测试,后来换7.0测试了。
第一个坑:6.0以上 特殊的权限需要动态请求获取,不再是清单文件申请就ok的了。
我的代码中有在内部储存中创建目录的代码。创建目录后接下来的一些文件操作才能成功。之前在4.4上运行没有问题。然后到7.0上总是出错。一开始是摸不着头脑~咋回事?莫非是手机管家类的禁止了?还是代码出现什么问题?后面发现是创建目录失败了。
String path=Environment.getExternalStorageDirectory() + File.separator + "dirPath" + File.separator;File file=new File(path);if(!file.exists()){ //创建目录 boolean mkdirs=file.mkdirs(); Log.e(tag,"----"+mkdirs+"----");}
log打印false。然后就是查找资料。发现6.0以上写文件的权限是需要动态请求的,否则默认就是没有这个权限的。所以,通过发送权限请求的代码就可以解决这个问题了。
public static final int PERMISSIONS_REQUEST_CODE = 1; /** * 发起一个写文件的权限请求 */ public static boolean setApi23(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { /** * API23以上版本需要发起写文件权限请求 */ ActivityCompat.requestPermissions(activity, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_CODE); return true; } else { return false; } }
封装成了一个方法。调用这个方法就可以弹出一个请求权限的dialog了。因为android的dialog都不会阻塞线程,如果在用户没有同意授权之前就执行了代码,仍然会出错。所以需要判断一下,而且需要一个回调接口。
示例代码:
//activity onCreate中if(!setApi23(this)){ //api23以下!正常执行}//用户操作后的回调方法@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { //上面请求时候的请求码 case FileUtils.PERMISSIONS_REQUEST_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager .PERMISSION_GRANTED) { Log.e(tag,"获取成功"); //正常执行代码 } else { Log.e(tag,"获取失败"); //干点啥好?... } break; } }
6.0上对用户的安全考虑,这会让一些比较涉及用户隐私的权限都暴露给用户知道。是一个比较赞的设计。而且代码修改也是比较简单。不像7.0上的另外一个坑。害我花了好长时间才搞定。
第二个坑:7.0以上 和外部分享文件必须要通过provider
这个是对应用的安全考虑,因为在7.0之前,比如调用系统应用打开文件都是通过 Uri.fromFile(file);获得的。而7.0后如果需要让外部应用通过Intent访问自己应用所有的文件,必须通过一个FileProvider类来获得Intent。就是通过四大组件的方式来实现。
这个android已经写好了。
第一步:
在清单文件中注册
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.xxx.xxxx.xxxxx" android:exported="false" android:grantUriPermissions="true"><meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/></provider>
这个已经是固定模式了。除了android:authorities和android:resource这两个属性可以自定义外,其他都得按照这个形式。比如exported要求必须为false,为true的话会报安全异常;grantUriPermissions:true表示授予 URI 临时访问权限。这里的authorities填的是我的应用包名。
还有一点需要注意。这个provider一定要写在Application标签内。如果粗心写在了外面就会报一个空指针异常。
然后还必须创建这个xml文件。xml文件如下:
<?xml version="1.0" encoding="utf-8"?><paths> <external-path name="xxoo" path=""/> <!--<files-path/>代表目录 Context.getFilesDir()<external-path/>代表目录: Environment.getExternalStorageDirectory()<cache-path/>代表的目录: getCacheDir();--></paths>
比较坑人的地方就是这个xml文件了。一开始不知道name和path属性代表什么意思。所以不知道这xml该怎么写。网上看文章全都是清一色的复制粘贴。连文字都一模一样。都没有说清楚这个xml文件的name和path的意义。究竟是path填什么,然后name是否代表目录的名称啊都懵逼了~最后看到这篇文章 Android7.0须知–应用间共享文件(FileProvider) 中说的一句话我才明白这个name和path属性该怎么用。
“但是,因为有很多时候,图片来源不确定,而且每款手机的相册所在的文件名称也可能不一样,如果一一添加的话,很麻烦,而且容易遗漏,这里,我用了一个简单的方法,很方便。代码如下,这样的话,我可以传递外部存储设备根目录下的任意一张图片了(包括其子目录)
<external-path path="" name="camera_photos" />
看到这我才明白。其实属性中的name是可以不用去管的。见名知意即可。然后path就是根据标签为根目录,它所在的目录路径,而且可以为空。
比如<external-path/>
这个标签代表 Environment.getExternalStorageDirectory()
假如path=”/image”
那么最终就是指定了 Environment.getExternalStorageDirectory()+/image
所以当path=””
那就表示 Environment.getExternalStorageDirectory()+""
就是这么回事了。
以上准备好后就可以了。代码中修改依旧是很简单。
/** * 获得调用系统应用打开文件的intent * * @param context * @param file */ public static Intent getFileIntent(Context context, File file) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //因为注册的时候authorities填的是包名... String authority = context.getPackageName(); //7.0上必须这样获得Intent contentUri = FileProvider.getUriForFile(context, authority, file); } else { //7.0以下使用旧版本的方式 contentUri = Uri.fromFile(file); } String MimeType = context.getContentResolver().getType(contentUri); intent.setDataAndType(contentUri, MimeType); return intent; }
同样是封装了一个方法。判断版本分别应付。得到intent后直接startActivity或传递给PendingIntent就行了。
以上就是我在7.0版本遇到的坑和解决办法。当是自己记录一下。懒得截图了~幸好没有懒得发文章。(最坑的是坑人的文章。不说清楚一点~!!吐槽ing→→ [虽然可能我也没说清楚←←])
总体来讲虽然一些原来普通的操作变得需要一些手续才能完成,但这会使Android系统变得越来越好。对开发者和用户而言都是一个好消息。毕竟我也是Android的用户~
- Android6.0和7.0上遇到的坑以及解决方法
- 使用FDFullscreenPopGesture遇到的坑以及解决方法
- android6.0开发中遇到的权限坑
- 关于Android6.0/7.0权限,以及7.0调用相机和切图的解决办法
- Mysql安装以及遇到的问题和解决方法
- android6.0不支持Httpclient的解决方法
- android6.0以后删除HttpClient的解决方法
- 在CentOS 7上遇到的一些问题,以及自己的解决方法
- android6.0以及以上的权限处理
- VC6.0 下安装SDK遇到的问题以及解决方法
- 上传本地仓库到github上时遇到的一些问题以及解决方法
- android6.0的坑
- Android6.0上的重要变化
- 今天遇到的几个问题以及解决方法
- 学习中遇到的问题以及解决方法
- iOS5里面遇到的兼容性问题以及解决方法
- 记录遇到的问题以及解决方法
- 常用工具遇到的错误以及解决方法
- 优化 -坐标上升法
- 轮播图
- 2017-11-20 白银解说
- 获取一个数二进制序列中所有的偶数位和奇数位,分别输出二进制序列。
- Monitoring SQL Statements with Real-Time SQL Monitoring (文档 ID 1380492.1)
- Android6.0和7.0上遇到的坑以及解决方法
- matlab-table行数不同(已解决)
- DSS 代码分析【EventThread与EventContext】
- centos7 firewall 防火墙 命令
- JAVA多线程和并发基础面试问答
- 拿百度offer的三面经历
- 用construct2制作游戏(进阶)
- Java IO
- daily summary