接锅太急?DownloadManager助你一臂之力

栏目: Android · 发布时间: 5年前

内容简介:在Android开发中,许多时候都会用到大文件下载功能,例如app更新、缓存视频文件等。之前我们开发团队里布置了一次作业,写的是关于利用RandomAcceseeFile和service来实现一个下载器,需要考虑到例如异步、后台下载、暴露回调接口、自动安装等策略,对于初级Android工程师来说,项目比较赶的时候可能就会影响效率。那么,有没有一个库能够简单粗暴地完成这些任务呢?当然,那就是Google官方的DownloadManager

在Android开发中,许多时候都会用到大文件下载功能,例如app更新、缓存视频文件等。之前我们开发团队里布置了一次作业,写的是关于利用RandomAcceseeFile和service来实现一个下载器,需要考虑到例如异步、后台下载、暴露回调接口、自动安装等策略,对于初级Android工程师来说,项目比较赶的时候可能就会影响效率。那么,有没有一个库能够简单粗暴地完成这些任务呢?

当然,那就是Google官方的DownloadManager

介绍

DownloadManager

我们看看官方对于两个主要内部类的介绍:

Nested classes description
DownloadManager.Query This class may be used to filter download manager queries.
DownloadManager.Request This class contains all the information necessary to request a new download.

顾名思义DownloadManager.Query主要用于查询下载的信息,DownloadManager.Request主要用于发起一个下载请求(其中可以添加下载的配置,例如Header等信息)

基本使用

1.设置权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
复制代码

2. 构造Request对象

val url = "http://hongyan.cqupt.edu.cn/app/com.mredrock.cyxbs.apk?version=44.0"//下载地址
        val uri: Uri = Uri.parse(url)//转变为Uri
        val request:DownloadManager.Request = DownloadManager.Request(uri)//构造request实例
复制代码

3. 配置Request的信息

request.setTitle("下载任务")
        request.setDescription("下载掌上重邮app中...")
        request.allowScanningByMediaScanner()
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, "cyxbs")
        //...
复制代码

部分配置:

  • addRequestHeader(String header,String value) 添加请求头
  • allowScanningByMediaScanner() 表示允许MediaScanner扫描到这个文件,默认不允许
  • setAllowedNetworkTypes(int flags) 设置下载时的网络条件,默认任何网络都可以下载,可选配置:NETWORK_BLUETOOTH、NETWORK_MOBILE、NETWORK_WIFI。
  • setAllowedOverRoaming(Boolean allowed) 漫游状态下是否可以下载
  • setNotificationVisibility(int visibility) 设置下载完成或下载时是否发布通知
  • setTitle(CharSequence):设置Notification的title
  • setDescription(CharSequence):设置Notification的message
  • setDestinationInExternalFilesDir 设置路径为应用程序外部文件目录
  • setDestinationInExternalPublicDir 设置路径为外部存储目录
  • setDestinationUri 设置路径
  • setMimeType(String mimeType) 设置MIME内容类型

4.获取DownloadManager实例

val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
复制代码

5.加入下载队列

val downloadId = downloadManager.enqueue(request)
复制代码

6.其他操作

//remove方法可以用来取消一个准备进行的下载,中止一个正在进行的下载,或者删除一个已经完成的下载。
downloadManager.remove(downloadId)//移除请求队列,取消下载
复制代码

监听DownloadManager的广播

DownloadManager会在完成时发送一条 ACTION_DOWNLOAD_COMPLETE 的广播,这时我们只需要创建一个BroadCastReceiver就能接收到下载完成的信息

创建BroadCastReceiver的子类来接收广播

class DownLoadFinishReceiver : BroadcastReceiver(){
        override fun onReceive(context: Context?, intent: Intent?) {
            intent?.let {
                val action = intent.action
                if (action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) {
                    //下载完成操作
                } else if (action == DownloadManager.ACTION_NOTIFICATION_CLICKED) {
                    //点击Notification的操作 例如暂停操作
                }
            }
        }
    }
复制代码

动态注册广播接收器

val downLoadFinishReceiver = DownLoadFinishReceiver()
        val intentFilter = IntentFilter()
        intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
        intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED)
        registerReceiver(downLoadFinishReceiver, intentFilter)
复制代码

别忘了在onDestroy里解除

override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(downLoadFinishReceiver)
    }
复制代码

Query的使用

query主要用于查询下载的信息,DownloadManager类中内置了很多的字段可以供Query来查询,例如状态、文件下载路径等。

Public method description
setFilterById(long... ids) 仅包括给定id的下载文件
setFilterByStatus(int flags) 仅包含状态与给定状态匹配的下载文件

结合Cursor,我们可以查询到DownloadManager里有的信息,包括下载文件的Uri,下载状态等。具体字段可以进入DownloadManager源码中查看。

查询下载状态

private fun queryStatus(){
        val query = DownloadManager.Query().setFilterById(downloadId)
        val cursor = manager.query(query)
        if(cursor!=null){
            if(cursor.moveToFirst()){
                val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
                when(status){
                    DownloadManager.STATUS_RUNNING->{
                        //下载中
                    }
                    DownloadManager.STATUS_FAILED->{
                        //下载失败
                    }
                    DownloadManager.STATUS_PAUSED->{
                        //下载暂停
                    }
                    DownloadManager.STATUS_PENDING->{
                        //下载延迟
                    }
                    DownloadManager.STATUS_SUCCESSFUL->{
                        //下载成功
                    }
                }
            }
        }
        cursor.close()
    }
复制代码

查询下载的文件的部分信息

这里DownloadManager封装好了两种查询的方法,其内部都是使用Query+Cursor的组合实现的。

manager.getUriForDownloadedFile(downloadId)
        //下载完成的文件的Uri,你可以拿到这个uri去操作他,例如apk安装
        manager.getMimeTypeForDownloadedFile(downloadId)
        //下载完成的文件的media type
复制代码

你也可以查询其他信息

DownloadManager.COLUMN_ID//下载id
DownloadManager.COLUMN_TITLE//下载文件的题目
DownloadManager.COLUMN_LOCAL_URI//下载文件的uri
DownloadManager.COLUMN_STATUS//下载文件的状态
DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR//目前文件的大小
DownloadManager.COLUMN_TOTAL_SIZE_BYTES//文件的总大小
复制代码

多说一句,若想回调进度,你可以根据上面的 文件目前大小 / 文件总大小 来得到,刷新进度可以使用Hanlder+Timer(仅供参考)

自动安装Apk

首先列出兼容7.0的方法,因为7.0时引入了"StrictMode Api"政策,禁止向你的应用外公开uri,如果Intent跳转到应用外,则会出现FileUriExposedException,所以说我们要添加一个flag去获得临时授权

private fun installApk(downloadId: Long) {
        val uri = manager.getUriForDownloadedFile(downloadId)
        val intent = Intent(Intent.ACTION_VIEW)
        if (uri != null) {
            intent.setDataAndType(uri,"application/vnd.android.package-archive")
            if ((Build.VERSION.SDK_INT >= 24)) {//版本是否在7.0以上
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                //对目标apk的uri临时授权 使得有权限打开该Uri指向的Apk
            }
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

            if (intent.resolveActivity(packageManager) != null) {
                startActivity(intent)
            } else {
                Log.e("DownloadManager","自动安装失败")
            }
        }else{
            Log.e("DownloadManager","下载失败")
        }
    }
复制代码

其次,8.0时禁止安装未知来源的apk,直接安装会闪退,所以说我们加入这条权限,就能跳转到一个让用户手动允许安装未知来源的界面。

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
复制代码

自动更新

(流程图练手)

接锅太急?DownloadManager助你一臂之力

后话

因为Request要设置的参数比较多,适合Builder模式,所以说大家可以写一个RequsetBuilder来配置Request对象,比较美观,简单修改如下。

class RequestBuilder {
    private lateinit var request:DownloadManager.Request
    private lateinit var uri: Uri
    private lateinit var context:Context

    fun with(context: Context):RequestBuilder{
        this.context = context
        return this
    }

    fun downloadUrl(url:String):RequestBuilder{
        this.uri = Uri.parse(url)
        this.request = DownloadManager.Request(uri)
        return this
    }


    fun setTitle(title: String):RequestBuilder{
        request.setTitle(title)
        return this
    }

    fun setDescription(description: String):RequestBuilder{
        request.setDescription(description)
        return this
    }

    fun allowScanningByMediaScanner():RequestBuilder{
        request.allowScanningByMediaScanner()
        return this
    }

    fun setNetworkType(networkType:Int):RequestBuilder{
        request.setAllowedNetworkTypes(networkType)
        return this
    }

    fun setNotificationVisibility(visibility:Int):RequestBuilder{
        request.setNotificationVisibility(visibility)
        return this
    }

    fun setDefaultDestination(subPath:String):RequestBuilder{
        request.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, subPath)
        return this
    }

    fun build():DownloadManager.Request{
        return request
    }
}
复制代码

此外,记得缓存downloadId,否则退出界面之后id就丢失了。


以上所述就是小编给大家介绍的《接锅太急?DownloadManager助你一臂之力》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Designing Web Navigation

Designing Web Navigation

James Kalbach / O'Reilly Media / 2007-8-15 / USD 49.99

Thoroughly rewritten for today's web environment, this bestselling book offers a fresh look at a fundamental topic of web site development: navigation design. Amid all the changes to the Web in the pa......一起来看看 《Designing Web Navigation》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具