Android下载安装Apk
程序员文章站
2022-06-25 09:39:43
1.自定义监听类,以返回下载结果interface DownLoadListener { /** * 下载成功之后的文件 */ fun onDownloadSuccess(file: File) /** * 下载进度 */ fun onDownloading(progress: Int) /** * 下载异常信息 */ fun onDownloadFailed(e:Exception)}进行...
1.自定义监听类,用来返回下载结果
interface DownLoadListener {
/**
* 下载成功之后的文件
*/
fun onDownloadSuccess(file: File)
/**
* 下载进度
*/
fun onDownloading(progress: Int)
/**
* 下载异常信息
*/
fun onDownloadFailed(e:Exception)
}
- 进行文件下载
/**
* @param destFileDir 文件下载目录
* @param response okHttp的返回值
* @param downLoadListener 监听事件,用于返回当前下载进度等
*/
private fun downLoad(
destFileDir: String,
response: Response,
downLoadListener: DownLoadListener
) {
val byte = ByteArray(2048)
var len: Int
val fileOutputStream: FileOutputStream
val file = File(destFileDir)
if (!file.exists()) {
file.mkdirs()
}
val apkFile = File(file, GeneralUtil.apkName)
val input = response.body?.byteStream()
val apkSize: Long = response.body?.contentLength() ?: 0L
println("获取到的apk大小:$apkSize")
fileOutputStream = FileOutputStream(apkFile)
var sum = 0.0
if (apkSize != 0L) {
while ((input?.read(byte).also { len = it!! }) != -1) {
fileOutputStream.write(byte, 0, len)
sum += len
//返回当前的下载进度
downLoadListener.onDownloading((sum / apkSize * 100).toInt())
}
}
//刷新
fileOutputStream.flush()
//返回结果:当前已经下载成功
downLoadListener.onDownloadSuccess(file)
//关闭流
input?.close()
fileOutputStream.close()
}
3.在Activity中监听下载成功或者失败的返回结果
override fun onDownloadSuccess(file: File) {
//下载新版本apk完成
manager.cancel(1)
//跳转到新的activity,这个activity用来做安装apk的操作
OpenApkFile.startOpenApkFile(this)
}
//上一次更新通知栏的时间
private var lastTime: Long = 0L
override fun onDownloading(progress: Int) {
//动态更新进度
if (lastTime == 0L) {
lastTime = System.currentTimeMillis()
}
//与上一次更新通知栏相隔大于1s再进行更新,否则压力过大
if (System.currentTimeMillis() - lastTime > 1000) {
lastTime = System.currentTimeMillis()
//notificationView为自定义的通知栏的布局
notificationView.setProgressBar(R.id.progress, 100, progress, false)
notificationView.setTextViewText(R.id.content, "$progress%")
//manager为NotificationManager
manager.notify(1, notification)
}
}
override fun onDownloadFailed(e: Exception) {
//下载失败,需要删除下载失败后的文件
val file = File(GeneralUtil.getDownLoadApkPathWithApkName(this))
file.deleteOnExit()
sendMessage(handler, HANDLERTYPE.FAIL.type, "新版本下载失败")
manager.cancel(1)
}
4.下载完成之后跳转到安装apk的界面
class OpenApkFile : AppCompatActivity() {
private var apkFileIntent: Intent? = null
companion object {
fun startOpenApkFile(context: Context) {
context.startActivity(Intent(context, OpenApkFile::class.java))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
apkFileIntent = getApkFileIntent(GeneralUtil.getDownLoadApkPathWithApkName(this), this)
if (apkFileIntent != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//版本高于26需要申请权限来安装apk
if (!this.packageManager.canRequestPackageInstalls()) {
AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("暂未开启权限,需要您开启权限安装最新版本,以获取更好的体验!")
.setNeutralButton("确定") { dialogInterface, _ ->
run {
val parse = Uri.parse("package:$packageName")
val intent =
Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, parse)
startActivityForResult(intent, 1)
dialogInterface.dismiss()
}
}
.setNegativeButton("取消") { dialogInterface, _ ->
run {
Toast.makeText(this, "已拒绝安装", Toast.LENGTH_SHORT).show()
dialogInterface.dismiss()
finish()
}
}
.create().show()
} else {
//已获得权限直接安装
startActivity(apkFileIntent)
finish()
}
} else {
//版本低于26则直接安装
startActivity(apkFileIntent)
finish()
}
} else {
//无法获取到uri,抛出了异常
AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("无法获取安装包,请联系管理员获取帮助")
.setNeutralButton("确定") { dialogInterface, _ ->
run {
dialogInterface.dismiss()
finish()
}
}
.create().show()
}
}
private fun getApkFileIntent(param: String?, context: Context): Intent? {
try {
println("文件所在地址:$param")
val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(
context,
context.applicationContext.packageName.toString() + ".provider",
File(param!!)
)
} else {
Uri.fromFile(File(param!!))
}
println("编码之后:$uri")
return Intent(Intent.ACTION_VIEW).run {
addCategory(Intent.CATEGORY_DEFAULT)
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// grantUriPermission(packageName,uri,Intent.FLAG_GRANT_READ_URI_PERMISSION)
setDataAndType(uri, "application/vnd.android.package-archive")
this
}
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
1 -> {
startActivity(apkFileIntent)
finish()
}
else -> {
}
}
}
}
5.FileProvider需要设置路径的xml
文件存放于res目录下的xml中:
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<files-path
name="aaa"
path="Apk/"/>
</paths>
</resources>
此段代码代表的路径为:data/data/com.包名/files/Apk/,name字段可以随便取名,不影响,path为data/data/com.包名/files下的子目录;
6.在AndroidManifest.xml注册FileProvider;
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
本文地址:https://blog.csdn.net/weixin_45379305/article/details/107297630