一、创建下载任务,并开始下载
fun downLoadFileOnBackground(
context: Context,
downTitle: String,
downDescription: String,
fileUrl: String,
fileName: String
): Long {
LogUtils.i("文件路径:${fileName}")
val request = DownloadManager.Request(Uri.parse(fileUrl))
//下载中和下载完后都显示通知栏
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//使用系统默认的下载路径 此处为应用内 /android/data/packages ,所以兼容7.0
request.setDestinationInExternalFilesDir(
context,
Environment.DIRECTORY_DOWNLOADS,
fileName
)
//通知栏标题
request.setTitle(downTitle)
//通知栏描述信息
request.setDescription(downDescription)
//设置类型为.apk
request.setMimeType("application/vnd.android.package-archive");
//获取下载任务ID
val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
return dm.enqueue(request)
}
该函数返回的是taskid
二、创建recever监听下载
class DownloadReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
installApk(context, id)
} else if (intent.action.equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
// DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
//获取所有下载任务Ids组
//long[] ids = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
//点击通知栏取消所有下载
//manager.remove(ids);
//Toast.makeText(context, "下载任务已取消", Toast.LENGTH_SHORT).show();
//处理 如果还未完成下载,用户点击Notification ,跳转到下载中心
LogUtils.i("点击消息栏,开始安装")
val viewDownloadIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(viewDownloadIntent)
}
}
private fun installApk(context: Context, downloadApkId: Long) {
LogUtils.i("下载完成,开始安装")
//判断是否允许安装未知来源安装包
if (FileUtils.isCanInstallUnKnowSource()) {
//安装apk
val dManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadFileUri = dManager.getUriForDownloadedFile(downloadApkId)
FileUtils.installApkFile(context, null, downloadFileUri)
} else {
FileUtils.requestInstallUnKnowSource(context)
}
}
}
在activity中注册和取消注册
private val downLoadReceiver by lazy { DownloadReceiver() }
registerReceiver(downLoadReceiver, intentFilter, RECEIVER_NOT_EXPORTED)
unregisterReceiver(downLoadReceiver)
三、fileProvider适配
manifest.xml中
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
xml文件夹下创建file_path.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--增加根目录配置,特别注意:此处的path要为空字符串-->
<root-path
name="root_path"
path="" />
<external-files-path
name="files_download"
path="Download" />
</paths>
因为安装apk文件用到一些系统文件下的内容,所以增加了root-path的访问,
由于未设置root-path的访问,可能在安装apk时提示解析安装包文件出现问题的提示
四、下载完成之后安装apk文件
fun installApkFile(context: Context, file: File?, fileUri: Uri?) {
if (file == null && fileUri == null) {
return
}
//打开安装界面
val intent = Intent(Intent.ACTION_VIEW)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (file != null) {
if (file.name.endsWith(".apk")) {
val apkUri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
FileProvider.getUriForFile(
context,
context.packageName + ".fileProvider",
file
)
} else {
Uri.fromFile(file)
}
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
}
} else if (fileUri != null) {
intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
}
context.startActivity(intent)
}