Android 应用内安装 APK,兼容 Android7.0之前与之后的系统

问题难点描述

Android 应用内安装 APK, 如果是 Android7.0及以上系统,需要处理好文件权限共享问题; Android7.0以前的系统,需要临时赋予某个文件特殊的公共读权限. 另外, Android 7.0 以下的系统,也可以把文件放在外部存储目录中,但是我不想因为一个简单的 Feature,再去处理一堆用户文件系统读写权限的各种状态问题,于是就最终采用了一种有点取巧的策略.

具体解决方法:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
fun install(mContext: Context, apkLocalPath:String) {
val apkFile = File(apkLocalPath)
//安装
val install = Intent(Intent.ACTION_VIEW)
//判断是否是android 7.0及以上
if (Build.VERSION.SDK_INT>=24) {
//7.0获取存储文件的uri
val uri = FileProvider.getUriForFile(mContext, "$PACKAGENAME.fileprovider", apkFile)
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
//赋予临时权限
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
//设置dataAndType
install.setDataAndType(uri, "application/vnd.android.package-archive")
} else {
val fileName = "tmp.apk"
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
var isSuccessCopyApk = false
try {
/* 此文件使用较为开放的文件权限, 以保证 apk 包,能被系统应用安装程序找到和使用. */
outputStream = openFileOutput(fileName,
MODE_WORLD_READABLE or MODE_WORLD_WRITEABLE)
inputStream = FileInputStream(apkFile)
inputStream.copyTo(outputStream, ResourceManager.bufferSize)
isSuccessCopyApk = true
} catch (e: Exception){
LogUtils.logE("$e")
} finally {
inputStream?.close()
outputStream?.flush()
outputStream?.close()
apkFile.delete()
}
if (isSuccessCopyApk){
val fileLocation = File(filesDir, fileName)
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
install.setDataAndType(Uri.fromFile(fileLocation), "application/vnd.android.package-archive")
}
}
mContext.startActivity(install)
}

file_paths.xml 中的文件写法:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="download" path="download/apk" />
</paths>

参考文章