实现apk下载及安装.
注意Apk下载文件的地址必须是https的,否则会失败(安卓默认网络连接都必须是https的)
实现效果- 操作界面效果图
- 下载中进度展示图(需要手动下拉顶部栏)
/**
* apk下载工具类
*/
class DownloadUtils(private val context: Context) {
//下载器
private var downloadManager: DownloadManager? = null
//下载的ID
private var downloadId: Long = 0
private var filePath: String? = null
private lateinit var mFileName: String
private var isDownloading = false
//下载apk
fun downloadAPK(url: String, fileName: String): Boolean {
if (isDownloading) {
return false
}
mFileName = fileName
//创建下载任务
val request = DownloadManager.Request(Uri.parse(url))
//移动网络情况下是否允许漫游
request.setAllowedOverRoaming(false)
//在通知栏中显示,默认就是显示的
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
request.setTitle(getAppName(context))
request.setDescription("新版本下载中...")
request.setVisibleInDownloadsUi(true)
//设置下载的路径
val file = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)
if (file.exists()) {
file.delete()
}
request.setDestinationUri(Uri.fromFile(file))
filePath = file.absolutePath
//获取DownloadManager
if (downloadManager == null) downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
//将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
if (downloadManager != null) {
downloadId = downloadManager!!.enqueue(request)
}
isDownloading = true
//注册广播接收者,监听下载状态
context.registerReceiver(receiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
return true
}
//广播监听下载的各个状态
private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
checkStatus(context)
}
}
//检查下载状态
private fun checkStatus(context: Context) {
val query = DownloadManager.Query()
//通过下载的id查找
query.setFilterById(downloadId)
val cursor = downloadManager!!.query(query)
if (cursor.moveToFirst()) {
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
when (status) {
DownloadManager.STATUS_PAUSED -> {
}
DownloadManager.STATUS_PENDING -> {
}
DownloadManager.STATUS_RUNNING -> {
}
DownloadManager.STATUS_SUCCESSFUL -> {
//下载完成安装APK
installAPK()
cursor.close()
context.unregisterReceiver(receiver)
isDownloading = false
}
DownloadManager.STATUS_FAILED -> {
Toast.makeText(this.context, "下载失败", Toast.LENGTH_SHORT).show()
cursor.close()
context.unregisterReceiver(receiver)
isDownloading = false
}
}
}
}
private fun installAPK() {
val intent = Intent(Intent.ACTION_VIEW)
// 由于没有在Activity环境下启动Activity,设置下面的标签
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
//Android 7.0以上要使用FileProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val file = File(filePath)
//参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致 参数3 共享的文件
val apkUri = FileProvider.getUriForFile(context, "com.cxyzy.fileprovider", file)
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
} else {
intent.setDataAndType(Uri.fromFile(File(Environment.DIRECTORY_DOWNLOADS, mFileName)), "application/vnd.android.package-archive")
}
context.startActivity(intent)
}
private fun getAppName(context: Context): String {
try {
val packageManager = context.packageManager
val packageInfo = packageManager.getPackageInfo(context.packageName, 0)
val labelRes = packageInfo.applicationInfo.labelRes
return context.resources.getString(labelRes)
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
}
注:上述代码通过SoulPermission实现动态申请权限,需要在build.gradle中增加依赖:implementation 'com.qw:soulpermission:1.3.0'
- 权限声明
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 访问网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 安装APK权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
完整源代码