Android 原生DownloadManager下载使用

实体类

data class DownloadInfo(
    val packageName: String,//下载apk时是包名   下载rom时是fileId
    val url: String,
    val md5: String,
    val size: Long,
    val packageType: PackageType
)

接口类

interface IDownloadControl {
    /**
     * 开始下载
     */
    fun startDownload(
        mAndroidInfos: List<AndroidPackageInfo>,
        apkInfos: List<ApkPackageInfo>,
        listener: DownloadListener
    )

    /**
     * 检测是否继续下载 用来判断上一次下载是否完成
     */
    fun isContinueDownload(): Boolean

    /**
     * 停止下载任务
     */
    fun stop()
}

整个下载类

class DownloadControl : IDownloadControl {
    private val TAG = "DownloadControl"
    private var mTimer: Timer? = null
    private var mCurrentDownloadId: Long? = null//当前下载id

    private var mCurrentDownloadPkgName: String? = null//当前下载的pkgName
    private var mDownloadListener: DownloadListener? = null
    private var mTotalDownloadSize = 0L
    private var mCurrentDownloadSize = 0.0//当前下载大小
    @Volatile
    private var isCancelDownload=false

    companion object {
        private var mDownloadManager: DownloadManager =
            OTA_SDK.application.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        private var mDefaultDestinationPath =
            Environment.DIRECTORY_DOWNLOADS//存储地址path  默认/sdcard/Download 根据DownloadManager 下载机制是不能直接外部地址的
        private var mDefaultPath = "sdcard/Download/"
        private var mDownloadInfoList = mutableListOf<DownloadInfo>()
    }

    override fun startDownload(
        mAndroidInfos: List<AndroidPackageInfo>,
        apkInfos: List<ApkPackageInfo>,
        listener: DownloadListener
    ) {
        isCancelDownload=false
        mDownloadInfoList.clear()
        mAndroidInfos.forEach {
            val item=DownloadInfo(it.fileId,it.url,it.md5,it.size,PackageType.ROM)
            mDownloadInfoList.add(item)
        }
        apkInfos.forEach {
            val item=DownloadInfo(it.packageName,it.url,it.md5,it.size,PackageType.APK)
            mDownloadInfoList.add(item)
        }
        mTotalDownloadSize = getTotalSize()
        mDownloadListener = listener
        checkFinish()
    }

    override fun isContinueDownload(): Boolean {
        return false
    }

    /**
     * 检测 所有urls 是否都下载完了 下载完成一个移除一个
     */
    private fun checkFinish() {
        if (mDownloadInfoList.isEmpty()) {
            LogUtil.d("checkFinish > urls is empty !", TAG)
            mDownloadListener?.success()
            return
        }
        download(mDownloadInfoList[0])
    }

    /**
     * 获取总的 下载量
     */
    private fun getTotalSize(): Long {
        var totalSize = 0L
        mDownloadInfoList.forEach {
            totalSize += it.size
        }
        return totalSize
    }


    private fun download(info: DownloadInfo) {
        mCurrentDownloadPkgName = info.packageName
        LogUtil.d("current download package name is $mCurrentDownloadPkgName")
        val uri = Uri.parse(info.url)
        val fileName = File(uri.path).name
        //获取当前已经相同名字的下载的文件
        val destFile = DirectoryManager.getDownloadDirFile(fileName)
        //下载验证 验证是否已经被下载过
        val verifyResult = CommonUtils.verifyFileMd5(info.md5, destFile)
        LogUtil.d("downloadRequest > md5 verify $verifyResult", TAG)
        if (verifyResult) {
            //md5 验证通过
            //更新当前下载进度
            mCurrentDownloadSize += info.size
            val percent = getProgress(mTotalDownloadSize, mCurrentDownloadSize.toLong())
            mDownloadListener?.progress(percent)
            //移除当前已经下载完成的
            mDownloadInfoList.removeAt(0)
            //继续检查还有没有需要下载
            UpdateDbManager.updatePath(
                mCurrentDownloadPkgName!!,
                destFile.absolutePath
            ) {
                checkFinish()
            }
        } else {
            //md5 验证不通过 重新下载
            downloadRequest(uri, fileName)
        }
    }

    /**
     * 下载请求
     */
    private fun downloadRequest(uri: Uri, fileName: String) {
        LogUtil.d("downloadRequest > start new download", TAG)
        val request = DownloadManager.Request(uri)
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)
        request.setDestinationInExternalPublicDir(mDefaultDestinationPath, fileName)
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE)
        request.setAllowedOverMetered(true)
        request.setAllowedOverRoaming(true)
        mCurrentDownloadId = mDownloadManager.enqueue(request)
        startTimer()
    }

    /**
     * 定时任务刷新 下载结果
     */
    private fun startTimer() {
        mTimer?.cancel()
        mTimer = Timer()
        mTimer?.schedule(object : TimerTask() {
            override fun run() {
                checkDownloadStatus()
            }
        }, 0, 500L)
    }

    /**
     * 检测下载进度
     */
    private fun checkDownloadStatus() {
        if (isCancelDownload){
            LogUtil.d("cancel download return",TAG)
            return
        }
        if (mCurrentDownloadId == null) {
            LogUtil.d("checkDownloadStatus > download fail , downloadId is null", TAG)
            mDownloadListener?.fail("")
            return
        }

        val query = DownloadManager.Query().setFilterById(mCurrentDownloadId!!)
        val cursor = mDownloadManager.query(query)

        if (cursor.moveToFirst()) {
            //下载状态
            val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
            var reason = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_REASON))
            val localUri =
                cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
            val itemBytesDownload =
                cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
            val itemTotalSize =
                cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
            cursor.close()
            reason = if ("placeholder".equals(reason)) "" else reason
            LogUtil.d("checkDownloadStatus > download status:$status, reason:$reason", TAG)
            when (status) {
                DownloadManager.STATUS_RUNNING -> {
                    // 下载中
                    val itemPercent = getProgress(itemTotalSize, itemBytesDownload)
                    LogUtil.d("itemPercent > $itemPercent $localUri", TAG)
                    val itemDownload = mCurrentDownloadSize + itemBytesDownload
                    val percent = getProgress(mTotalDownloadSize, itemDownload.toLong())
                    mDownloadListener?.progress(percent)

                }

                DownloadManager.STATUS_PAUSED -> {
                    // 下载暂停
                    mDownloadListener?.paused()
                }

                DownloadManager.STATUS_SUCCESSFUL -> {
                    // 下载完成
                    LogUtil.d("checkDownloadStatus > download success $localUri", TAG)
                    mTimer?.cancel()
                    //下载完成 判断目标文件是否需要 移动到目标文件
                    val sourceFile = File(Uri.parse(localUri).path)
                    val destFile = DirectoryManager.getDownloadDirFile(sourceFile.name)
                    //复制到目标文件
                    copyFile(sourceFile, destFile)
                    val destFileAbsolutePath=destFile.absolutePath

                    //删除复制前下载的文件  保留复制文件
                    cleanDefaultDownload()
                    //下载成功一个移除一个 然后刷新保存LastDownloadInfo
                    mCurrentDownloadSize += mDownloadInfoList[0].size
                    mDownloadInfoList.removeAt(0)

                    UpdateDbManager.updatePath(
                        mCurrentDownloadPkgName!!,
                        destFileAbsolutePath
                    ) {
                        checkFinish()
                    }
                }

                DownloadManager.STATUS_FAILED -> {
                    // 下载失败
                    LogUtil.d("checkDownloadStatus > download fail $localUri", TAG)
                    mTimer?.cancel()
                    mDownloadListener?.fail(reason)
                }

//                    ERROR_UNKNOWN -> {
//                        //COLUMN_ERROR_CODE的值,当下载完成时出现不适合任何其他错误代码的错误。
//                    }
//                    ERROR_FILE_ERROR -> {
//                        //当出现不适合任何其他错误代码的存储问题时
//                    }
//                    ERROR_UNHANDLED_HTTP_CODE->{
//                        //收到下载管理器无法处理的HTTP代码时COLUMN_REASON的值。
//                    }
//                    ERROR_HTTP_DATA_ERROR->{
//                        //在HTTP级别接收或处理数据时发生错误时
//                    }
//                    ERROR_TOO_MANY_REDIRECTS->{
//                        //重定向过多时COLUMN_REASON的值。
//                    }
//                    ERROR_INSUFFICIENT_SPACE->{
//                        //存储空间不足时COLUMN_REASON的值。通常,这是因为SD卡已满。
//                    }
//                    ERROR_DEVICE_NOT_FOUND->{
//                        //未找到外部存储设备时COLUMN_REASON的值。通常,这是因为未安装SD卡。
//                    }
//                    ERROR_CANNOT_RESUME->{
//                        //COLUMN_REASON的值,此时可能发生了一些暂时性错误,但我们无法恢复下载。
//                    }
//                    ERROR_FILE_ALREADY_EXISTS->{
//                        //请求的目标文件已存在时COLUMN_REASON的值(下载管理器不会覆盖现有文件)。
//                    }
//                    PAUSED_WAITING_TO_RETRY -> {
//                        //COLUMN_REASON的值,当由于发生某些网络错误而暂停下载,并且下载管理器正在等待重试请求时
//                    }
//                    PAUSED_WAITING_FOR_NETWORK ->{
//                        //当下载正在等待网络连接进行时,COLUMN_REASON的值。
//                    }
//                    PAUSED_QUEUED_FOR_WIFI ->{
//                        //当下载超过移动网络上下载的大小限制并且下载管理器正在等待Wi-Fi连接进行时,COLUMN_REASON的值。
//                    }
//                    PAUSED_UNKNOWN ->{
//                        //由于其他原因暂停下载时COLUMN_REASON的值。
//                    }
                else -> {
                    LogUtil.d("other status $status,$reason", TAG)
                }
            }
        }
    }

    private fun delete(sourceFile: File){
        if (sourceFile.exists()) {
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    Files.delete(sourceFile.toPath())
                }
                LogUtil.d("File deleted successfully", TAG)
            } catch (e: IOException) {
                e.printStackTrace()
                LogUtil.e("${Log.getStackTraceString(e)}",TAG)
            }
        } else {
            LogUtil.d("File does not exist", TAG)
        }

    }

    /**
     * 将文件sourceFile 复制到destFile
     */
    private fun copyFile(sourceFile: File, destFile: File) {
        val inputStream = FileInputStream(sourceFile)
        val outputStream = FileOutputStream(destFile)
        val buffer = ByteArray(1024)
        var length: Int

        while (inputStream.read(buffer).also { length = it } > 0) {
            outputStream.write(buffer, 0, length)
        }

        inputStream.close()
        outputStream.close()
    }

    private fun getProgress(totalSize: Long, currentSize: Long): Double {
        try {
            if (totalSize <= 0) {
                LogUtil.d(" getProgress total size is : $totalSize", TAG)
                return 0.0
            }
            var progress = (currentSize / totalSize.toDouble())
            progress = if (progress > 1.0) 1.0 else if (progress < 0.0) 0.0 else progress
            return BigDecimal(progress).setScale(2, BigDecimal.ROUND_HALF_UP).toDouble()
        } catch (e: Exception) {
            e.printStackTrace()
            return 0.0
        }
    }

    private fun cleanDefaultDownload() {
        File(mDefaultPath).apply {
            val deleteSuccess = this.deleteRecursively()
            LogUtil.d("delete sdcard/Download success $deleteSuccess", TAG)
            if (!deleteSuccess) {
                ShellCmdUtil.execCommand("rm -r ${this.absolutePath}", true).apply {
                    LogUtil.d(
                        " shell cmd delete sdcard/Download file ${this?.first}  ${this?.second}",
                        TAG
                    )
                }
            }
        }
    }

    override fun stop() {
        isCancelDownload=true
        mTimer?.cancel()
        mCurrentDownloadId?.let {
            try {
                //先取消下载
                mDownloadManager.remove(it)
            }catch (e:Exception){
                e.printStackTrace()
                LogUtil.e("${Log.getStackTraceString(e)}",TAG)
            }

        }
        //删除复制前下载的文件  保留复制文件
        cleanDefaultDownload()
        MemoryManager.cleanDownloadPackage()
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值