关闭读写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文件的权限已经修改了。

这个时候也可以安装成功了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容