超详细DownloadManager使用,兼容到版本8.0

最近用到了软件更新,感觉自己写更新比较麻烦,还要定义通知栏的进度效果,想了一下还是使用系统自带的DownloadManager好了,但这个坑还是挺多的,还要搞兼容,现在来总结一下。

步骤

  1. 申请权限

文件下载和保存需要网络跟写存储卡权限

   <uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

8.0针对未知来源应用,在应用权限设置的“特殊访问权限”中,加入了“安装其他应用”的设置,这主要是为了防止应用内引导用户安装其他无关应用,特别是针对一些流氓应用会比较有效。添加下面权限后就可以正常跳转安装界面

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
  1. 写下载监听

我们可以定义一个BroadcastReceiver

class DownloadReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {if (intent.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) {val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)if (SavePicNoLogService.downloadId == id) {installApk(context, id)}} else if (intent.action == 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 ,跳转到下载中心val viewDownloadIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)viewDownloadIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASKcontext.startActivity(viewDownloadIntent)}}private fun installApk(context: Context, downloadApkId: Long) {val dManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManagerval install = Intent(Intent.ACTION_VIEW)val downloadFileUri = dManager.getUriForDownloadedFile(downloadApkId)if (downloadFileUri != null) {Log.d("DownloadManager", downloadFileUri.toString())install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive")if ((Build.VERSION.SDK_INT >= 24)) {//判读版本是否在7.0以上install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) //添加这一句表示对目标应用临时授权该Uri所代表的文件}install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)if (install.resolveActivity(context.packageManager) != null) {context.startActivity(install)} else {L.e("自动安装失败,请手动安装")val service = context as SavePicNoLogServiceservice.showErrorTip("下载完成,请点击下拉列表的通知手动安装")}} else {Log.e("DownloadManager", "download error")}}
}

这里要注意的地方是7.0版本系统会有个文件路径的限制,我们需要添加一个FLAG_GRANT_READ_URI_PERMISSION来取得目标文件使用权限。

还要在代码中注册监听和在onDestory方法中销毁监听

private fun initDownLoadBroadcastReceiver() {L.i("init DownLoadBroadcastReceiver...")downloadBroadcastReceiver = DownloadReceiver()val intentFilter = IntentFilter()intentFilter.addAction("android.intent.action.DOWNLOAD_COMPLETE")intentFilter.addAction("android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED")registerReceiver(downloadBroadcastReceiver, intentFilter)
}override fun onDestroy() {super.onDestroy()unregisterReceiver(downloadBroadcastReceiver)
}

或者在清单文件中注册也行

<receiver android:name=".receiver.DownloadReceiver"><intent-filter><action android:name="android.intent.action.DOWNLOAD_COMPLETE" /><action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" /></intent-filter>
</receiver>
  1. 封装DownloadManager下载工具类
    封装一个工具类方便以后使用
class DownloadManagerUtil(private val mContext: Context) {/*** 可能会出错Cannot update URI: content://downloads/my_downloads/-1* 检查下载管理器是否被禁用*/fun checkDownloadManagerEnable():Boolean {try {val state = mContext.packageManager.getApplicationEnabledSetting("com.android.providers.downloads")if (state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {val packageName = "com.android.providers.downloads"try {val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)intent.data = Uri.parse("package:$packageName")mContext.startActivity(intent)} catch (e: ActivityNotFoundException) {e.printStackTrace()val intent = Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)mContext.startActivity(intent)}return false}} catch (e:Exception) {e.printStackTrace()return false}return true}fun download(url: String, title: String, desc: String): Long {val uri = Uri.parse(url)val req = DownloadManager.Request(uri)//设置允许使用的网络类型,这里是移动网络和wifi都可以req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE or DownloadManager.Request.NETWORK_WIFI)//下载中和下载完后都显示通知栏req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)//设置文件的保存的位置[三种方式]// 第一种 file:///storage/emulated/0/Android/data/your-package/files/Download/update.apkreq.setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, "$title.apk")//第二种 file:///storage/emulated/0/Download/update.apk
//        req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk")//第三种 自定义文件路径
//        req.setDestinationUri()//禁止发出通知,既后台下载
//        req.setShowRunningNotification(false);//通知栏标题req.setTitle(title)//通知栏描述信息req.setDescription(desc)//设置类型为.apkreq.setMimeType("application/vnd.android.package-archive")// 设置为可被媒体扫描器找到req.allowScanningByMediaScanner()// 设置为可见和可管理req.setVisibleInDownloadsUi(true)//获取下载任务IDval dm = mContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManagerreturn try {dm.enqueue(req)} catch (e: Exception) {Toast.makeText(mContext, "找不到下载文件", Toast.LENGTH_SHORT).show()-1}}/*** 下载前先移除前一个任务,防止重复下载** @param downloadId*/fun clearCurrentTask(downloadId: Long) {val dm = mContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManagertry {dm.remove(downloadId)} catch (ex: IllegalArgumentException) {ex.printStackTrace()}}
}

要注意的地方:
如果禁用了下载管理器,调用enqueue方法可能出现的错误:Cannot update URI: content://downloads/my_downloads/-1
这里面的checkDownloadManagerEnable方法是用来检测下载管理器是否被禁用,如果禁用了主动开启。

  1. 使用
downloadBroadcastReceiver = DownloadReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction("android.intent.action.DOWNLOAD_COMPLETE")
intentFilter.addAction("android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED")
registerReceiver(downloadBroadcastReceiver, intentFilter)
btn_download.setOnClickListener {val dm = DownloadManagerUtil(this)if (dm.checkDownloadManagerEnable()) {if (MyApplication.downloadId != 0L) {dm.clearCurrentTask(MyApplication.downloadId) // 先清空之前的下载}MyApplication.downloadId = dm.download(url, title, desc)}else{Toast.makeText(this@MainActivity,"请开启下载管理器",Toast.LENGTH_SHORT).show()}
}

例子代码已经放到Github了

链接: https://github.com/StarsAaron/DownloadManagerDemo.

Android笔记系列--超详细DownloadManager使用,兼容到版本8.0相关推荐

  1. Linux 学习笔记之超详细基础linux命令 Part 3

    Linux学习笔记之超详细基础linux命令 by:授客 QQ:1033553122 ---------------------------------接Part 2----------------- ...

  2. Spring框架学习笔记,超详细!!(4)

    Java小白开始学习Spring框架,一方面,跟着视频学习,并记录下学习笔记,方便以后复习回顾.另一方面,发布学习笔记来约束自己,学习路程还很遥远,继续加油坚持!!!希望能帮助到大家! 另外还有我的牛 ...

  3. android:自己动手编译Android源码(超详细)

    自己动手编译Android源码(超详细) 涅槃1992 2016.06.20 02:12* 字数 4330 阅读 86819评论 89喜欢 339赞赏 7 在Android Studio代码调试一文中 ...

  4. 小米电视系统统计服务器,史上最全!小米电视4 各系列超详细对比

    原标题:史上最全!小米电视4 各系列超详细对比 小米电视作为智能电视的新秀,以优秀的品牌号召力.良好的口碑,当然免不了小米一直以来的价格攻势,逐渐成为电视领域的领头羊,目前市面上包含小米电视4.4A. ...

  5. stm32正常运行流程图_STM32单片机学习笔记(超详细整理143个问题,学习必看)...

    原标题:STM32单片机学习笔记(超详细整理143个问题,学习必看) 1.AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中2>1,意思是APB2接高速设备 2.Stm32f ...

  6. Linux操作系统笔记(超详细)

    [历史文章] Ubuntu在进行更新系统时出现Hash校验和不符的问题 Linux常用的命令总结(一)--ls命令与cd命令 Linux命令(二)mkdir命令与rmdir命令 Linux命令(三)t ...

  7. Android Studio安装超详细步骤(包括SDK安装不成功,模拟器无法创建等问题)

    本文主要介绍CPU为AMD锐龙和英特尔两种类型在安装中出现的一些问题,两种解决的方案不同,所以首先查看属于哪种,然后找相对应的安装方法. Android Studio的安装需要准备两个安装文件,一个是 ...

  8. Android笔记系列--动画

    篇幅比较长,耐心点看,总结了Android中有关动画的细节,包括一些使用的小例子. 源码地址: https://github.com/StarsAaron/AnimationTestDemo/tree ...

  9. Android实战——Activity超详细学习笔记

    一.Activity简介 Activity是四大组件中最重要的一个,也是平时开发中接触最多的.与Activity启动行为相关的就是它的启动模式,Standard.SingleTop.SingleTas ...

最新文章

  1. Inception GoogLeNet
  2. 【代码笔记】Web-JavaScript-JavaScript调试
  3. 史上最‘牛’杀毒软件之麦咖啡
  4. sqlite查询空日期类型_SQLite比较日期
  5. 使用Kylin导入JDBC数据源遇到的问题
  6. 使用Flash Pro CC 输出动画--html5-canvas
  7. ADO:用代码调用存储过程
  8. 做游戏,学编程(C语言) 17 猪小弟
  9. ELK 架构之 Elasticsearch 和 Kibana 安装配置
  10. 和shopee哪个好_shopify和虾皮shopee哪个好?虾皮shopee电商哪个平台好
  11. iTween使用小坑
  12. 计算机网络-网络安全
  13. 中国传统的节日(端午节)
  14. 中年网易,生存像是一种幸运
  15. 卷积神经网络的参数计算
  16. 如何用手机语音助手控制电脑
  17. MATLAB如何读取文件某一行的内容,如何读取文件的某一行
  18. 快速插入100w条数据
  19. 【组合逻辑电路】如何从真值表中求解逻辑函数?
  20. 6、春节到了,小明期盼收到压岁钱。他想:如果收到的钱超过1000元,那么捐助失学儿童;收到的钱在500~1000之间,那么购买航模;如果收到的钱不足500元,那么购买百科全书。

热门文章

  1. Kotlin 解构声明
  2. SpringBoot向数据库中插入英文没问题,中文报错乱码问题
  3. 2021年施工员-市政方向-通用基础(施工员)考试内容及施工员-市政方向-通用基础(施工员)新版试题
  4. svn log命令的使用
  5. 五星级酒店客房无线网络覆盖WOC方案及施工图片
  6. 数据结构之单链表的增删查改等操作画图详解
  7. python获取登录按钮_Python:Selenium模拟Chrome浏览器抓取淘宝商品信息
  8. 轻轻松松挂机月赚3000不是梦
  9. XyTalk企业即时通讯IM开始开源
  10. 青云战歌mysql_青云战歌数据库修改