关闭读写SD卡权限后的应用适配问题
今天在做SD卡的代码优化的工作。之前公司的应用是在MainActivity中申请读写SD卡权限,如果用户选择了拒绝,那么直接弹窗提示用户必须赋予SD卡读写权限,否则将直接退出应用。虽然微信等app都是这样的逻辑,但是还是觉得很不友好。在如今这个Android手机的大环境中,SD读写权限没有那么十分严重。
因此,我们将对这里的逻辑进行改造。
1. Android中的内部存储与外部存储
Android SD卡主要有两种存储方式 Internal 、 External Storage
Internal内部存储,应用私有目录
这个目录的特点是:
- 内部存储不需要申请任何权限
- 这个目录始终可用,这个文件夹用于 App 中的 WebView 缓存页面信息,SharedPreferences 和 SQLiteDatabase 持久化应用相关数据等。
- 当用户卸载 App 时,系统自动删除 data/data 目录下对应包名的文件夹及其内容。
对于没有root的手机是没办法看到data/data目录的,但是我们可以通过Androidstudio提供的Device File Explorer来查看。
External Storage外部存储
外部存储又分为 外部私有存储 、外部公有存储
Private files 外部存储空间中的应用私有目录
考虑内部存储空间容量有限,普通用户不能直接直观地查看目录文件等其他原因,Android 在外部存储空间中也提供有特殊目录供应用存放私有文件,文件路径为:
/storage/emulated/0/Android/data/app package name
它的特点是:
默认情况下,系统并不会自动创建外部存储空间的应用私有目录。
宿主 App 可以直接读写内部存储空间中的应用私有目录;而在 4.4 版本开始,宿主 App 才可以直接读写外部存储空间中的应用私有目录,使开发人员无需在 Manifest 文件中或者动态申请外部存储空间的文件读写权限
当用户卸载 App 时,系统也会自动删除外部存储空间下的对应 App 私有目录文件夹及其内容。
自 Android 7.0 开始,系统对应用私有目录的访问权限进一步限制。其他 App 无法通过 file:// 这种形式的 Uri 直接读写该目录下的文件内容,而是通过 FileProvider 访问。
Public files 外部存储空间中的公共目录
这里说的就是我们平时所看到的存储目录了,用户可以随意在里面进行创建删除等操作。这里面保存的大多是一些与应用无关的数据,当应用被卸载,用户仍然希望保留于设备当中的信息。常见如,拍照类应用的图片文件,用户是使用浏览器手动下载的文件等。
在这里读写目录属于Dangerous Permissions危险权限了,如果工程的targetSdkVersion >=23,就要考虑权限问题了 。动态申请权限在这里就不讲了。
说完了Android中内部存储和外部存储的区别,讲一下我是如何改造的。
2. 应用改造
这里我们提示应用升级的案例来说明是如何改造的。
在应用进入的闪屏页初始化中,首先判断是否拥有SD卡,是否获取了读写SD卡权限:
if (!SdCardUtils.isSdCardExist(AppStart.this)) { // 设置应用中保存的根路径 AppConstants.PARENT_FOLD_PATH = getFilesDir().getAbsolutePath(); }else { // 设置应用中保存的根路径 AppConstants.PARENT_FOLD_PATH = Environment .getExternalStorageDirectory() + File.separator + Constants.APP_NAME + File.separator; }
/** * 判断当前设备上SD卡外部存储是否可用,这里只考虑6.0以上版本 */ public static boolean isSdCardExist(Context context){ if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { return false; } boolean isExist = false; isExist = Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED); return isExist; }
如果我们关闭了SD卡读写权限,下载的更新包就会下载到内部存储空间
/** * 构造更新的软件的安装包的保存路径名 */ public static final String buildUpdateAPKPath() { if (!SdCardUtils.isSdCardExist(AppContext.getInstance()) && fileDir != null && fileDir.exists()) { return fileDir.toString() + "/"; } String filePath = FileUtils.buildFilePath(new String[] { SdCardUtils.getSdCardPath(), APP_NAME }); File dir = new File(filePath); if (!dir.exists()) { dir.mkdirs(); } return filePath; }
应用下载完毕,我们查看一下应用目录,发现更新包已经被下载下来了。
然后会调用打开apk文件的intent方法,核心方法如下
private static Intent getApkFileIntent(String updateFilePath) { Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(android.content.Intent.ACTION_VIEW); Uri uri = Uri.fromFile(new File(updateFilePath)); intent.setDataAndType(uri, "application/vnd.android.package-archive"); return intent; }
执行刚才的方法却出现了解析安装包失败的错误。
但是通过拷贝这个apk文件到外部存储目录,然后手动点击打开是没有任何问题的。那之前无法安装是因为什么呢?让我们再看一下下载的目录:
了解Linux目录权限的可以看出这里,我们对这个文件只有读写权限,没有执行权限
Linux的文件权限有以下设定:
- Linux下文件的权限类型一般包括读,写,执行。对应字母为 r、w、x。
- Linux下权限的属组有 拥有者 、群组 、其它组 三种。每个文件都可以针对这三个属组(粒度),设置不同的rwx(读写执行)权限。
- 通常情况下,一个文件只能归属于一个用户和组, 如果其它的用户想有这个文件的权限,则可以将该用户加入具备权限的群组,一个用户可以同时归属于多个组。
知道了问题所在,我们就办法解决了。在打开apk之前,下载成功之后我们需要修改这个文件的权限:
String[] command = {"chmod", "777", updateAPK.getFilePath() }; ProcessBuilder builder = new ProcessBuilder(command); try { builder.start(); } catch (IOException e) { e.printStackTrace(); }
重新运行打包apk,然后下载更新,更新结束后我们发现更新的apk文件的权限已经修改了。
这个时候也可以安装成功了。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
android应用流量监控
https://github.com/JasmineBen/NetworkMonitor 通过NetworkStatsManager和悬浮框技术,实现了一个应用流量监控的APP,通过输入应用包名可以实时检测应用的流量使用情况,从而帮助用户排查应用的网络问题。
- 下一篇
Flutter 11: 图解底部状态栏小尝试
小菜今天来整理一下在学习测试 Flutter 时需用到的底部导航栏 BottomNavigationBar,使用方式很简单,小菜感觉效果比原生的 Android 要好一些。 何为 BottomNavigationBar ? BottomNavigationBar 为底部导航栏控件,可以包含文字标签和图标等基本信息,通常在三到五个之间;据了解,iOS 的规范底部导航栏最多可设置五个,所以大部分应用均在五个以内;现在很多应用都是以底部导航栏 + 中部主内容 Content 方式来展示。 官网建议,BottomNavigationBar 底部导航栏通常与 Scaffold 一起使用,其中它作为Scaffold.bottomNavigationBar 参数提供。 如何应用 BottomNavigationBar ? 与 body 同级的位置添加 BottomNavigationBar,BottomNavigationBarItem 中可以添加文字标签或图标 (Icons/Image) 等,若图片不存在时会显示空白;这样就可以添加底部状态栏内容,文字和图标的样式也可以随意调整;如下: botto...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- MySQL8.0.19开启GTID主从同步CentOS8
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Hadoop3单机部署,实现最简伪集群