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

在Android開發中,許多時候都會用到大文件下載功能,例如app更新、緩存視頻文件等。以前咱們開發團隊里布置了一次做業,寫的是關於利用RandomAcceseeFile和service來實現一個下載器,須要考慮到例如異步、後臺下載、暴露回調接口、自動安裝等策略,對於初級Android工程師來講,項目比較趕的時候可能就會影響效率。那麼,有沒有一個庫可以簡單粗暴地完成這些任務呢?

固然,那就是Google官方的DownloadManagerandroid

介紹

DownloadManager緩存

咱們看看官方對於兩個主要內部類的介紹:
bash

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.設置權限app

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
複製代碼

2. 構造Request對象dom

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")
        //...
複製代碼

部分配置:ide

  • 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實例ui

val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
複製代碼

5.加入下載隊列this

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"/>
複製代碼

自動更新

(流程圖練手)

image

後話

由於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就丟失了。

最後

基本內容就這麼多了,可能有些地方寫得很差,有不正確的地方歡迎你們指出

相關文章
相關標籤/搜索