Android 使用Retrofit+协程实现超简单大文件下载并回显进度条

news2024/11/14 3:33:02

安卓自带的进度条弹窗过时了,这里简单创建一个进度条弹窗

drawable文件夹创建progress_dialog_bg_style.xml一个圆角白色背景样式

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp"/>
    <solid android:color="@color/white" />
</shape>

创建alert_dialog_download_progress.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="500dp"
    android:layout_height="240dp"
    android:padding="20dp"
    android:orientation="vertical"
    android:gravity="center"
    android:background="@drawable/progress_dialog_bg_style">

    <TextView
        android:id="@+id/d_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:layout_marginBottom="50dp"
        android:text="标题" />

    <ProgressBar
        android:id="@+id/d_progress_bar"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:max="100"
        android:layout_height="wrap_content"/>

</LinearLayout>

创建弹窗工具类,使用刚才创建好的布局

object DialogUtil {
    /**
     * 下载进度条弹窗
     */
    fun showDownloadProgress(
        context: Context,
        title: String? = null
    ): AlertDialog = context.let {
        AlertDialog.Builder(it).create().apply {
            // 设置点击dialog的外部能否取消弹窗
            setCanceledOnTouchOutside(false)
            // 设置能不能返回键取消弹窗
            setCancelable(false)
            show()
            window?.run {
                setLayout(
                    600,
                    200
                )
            }
            setContentView(
                View.inflate(it, R.layout.alert_dialog_download_progress, null).apply {
                    // 设置成顶层视图
                    bringToFront()
                    title?.let { text ->
                        findViewById<TextView>(R.id.d_title).text = text
                    }
                }
            )
        }
    }
}

简单封装一个下载工具类

先定义一个下载参数实体DownloadDTO

import okhttp3.ResponseBody
import java.io.File

/**
 * 下载参数
 */
data class DownloadDTO (
    val filename: String,
    val filepath: String,
    val body: ResponseBody,
    val callback: DownloadCallback
) {
    // 下载回调接口,用来返回下载情况
    interface DownloadCallback {
        fun onSuccess(file: File)
        fun onProgress(progress: Int)
        fun onFailure(e: Exception)
    }
}

编写下载工具类DownloadFileUtil,用到了挂起函数必须在协程中使用

object DownloadFileUtil {

    /**
     * 文件下载
     */
    suspend fun download(dto: DownloadDTO) = coroutineScope {
        async(Dispatchers.IO) {
            try {
                val filepath = File(dto.filepath)
                if (!filepath.exists()) {
                    filepath.mkdirs()
                }
                val file = File(filepath.canonicalPath, dto.filename)
                if (file.exists()) {
                    file.delete()
                }
                try {
                    val buffer = ByteArray(1024)
                    val contentLength: Long = dto.body.contentLength()
                    var lastProgress = 0
                    dto.body.byteStream().use { input ->
                        FileOutputStream(file).use { fos ->
                            var length: Int
                            var sum: Long = 0
                            while (input.read(buffer).also { length = it } != -1) {
                                fos.write(buffer, 0, length)
                                sum += length.toLong()
                                val progress = (sum * 100 / contentLength).toInt()
                                if (progress > lastProgress) {
                                    lastProgress = progress
                                    dto.callback.onProgress(progress)
                                }
                            }
                            fos.flush()
                        }
                    }
                    dto.callback.onSuccess(file)
                    LogUtil.yd("DownloadFileUtil.download filepath: ${file.path}")
                } catch (e: Exception) {
                    if (file.exists()) {
                        file.delete()
                    }
                    dto.callback.onFailure(e)
                }
            } catch (e: Exception) {
                dto.callback.onFailure(e)
            }
        }
    }.await()
}

开始使用写好的工具来下载文件

在ApiService 中添加下载接口

import okhttp3.ResponseBody
import retrofit2.http.*

interface ApiService {
    /**
     * 下载文件
     */
    @Streaming
    @GET
    suspend fun downloadFile(@Url fileUrl: String): ResponseBody
}

 编写具体调用下载接口的代码

// 开头说的文章有HttpRequest的封装过程
HttpRequest.executeAsync {
    // 开始请求,这里链接用的是自己服务器上的就不放出来了
    val downloadFile = it.downloadFile("http://xxxx/xxx.rar")
    // 显示进度条弹窗
    val dialog = DialogUtil.showDownloadProgress(this@MainActivity, "正在下载...")
    val view = dialog.findViewById<ProgressBar>(R.id.d_progress_bar)
    delay(500)
    // 下载并返回进度
    DownloadFileUtil.download(
          DownloadDTO(
              "文件名.rar",
              // 下载保存路径
               "${applicationContext.filesDir.absolutePath}${File.separator}test${File.separator}",
              downloadFile,
              object : DownloadDTO.DownloadCallback {
                  override fun onSuccess(file: File) { 
                      // 下载完成
                      dialog.cancel()
                  }

                  override fun onProgress(progress: Int) {
                      // 更新下载进度
                      view.progress = progress
                  }

                  override fun onFailure(e: Exception) {
                      // 下载失败
                      dialog.cancel()
                      e.printStackTrace()
                  }
              }
          )
      )
}

别忘了加上网络请求权限

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

可以看到已经在下载了,下载完成后可以如图打开目录 

 

找到自己APP的包名点开进入下载目录,可以看到文件已经被下载到指定的位置 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/464745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IPTV系统架构的分析与研究

1 引言   IPTV业务是伴随着宽带互联网的飞速发展而兴起的一项新兴的互联网增值业务,它利用宽带互联网的基础设施&#xff0c;以家用电视机和电脑作为主要终端 &#xff0c;利用网络机顶盒(STB,Set -TopBox) &#xff0c;通过互联网协议来传送电视信号.提供包括 电视节 目在 内…

嵌入式51单片机05-中断与定时器系列

文章目录 中断与定时器一、中断系统与定时器1. 中断简单介绍2. 定时器简单介绍 二、中断系列代码1. 中断操作&#xff08;中断控制LED灯亮灭&#xff09;&#xff08;1&#xff09;仿真电路图&#xff08;2&#xff09;源代码&#xff08;3&#xff09;实验结果 2. 中断操作&am…

c++强制类型转换:

强制类型转换&#xff1a;1. const属性用const_cast。 案例&#xff1a; 说明&#xff1a;该变量可以将变量的const 的属性去掉。如该案例&#xff0c;转换后修改x的值是合法的。2. 基本类型转换用static_cast。 案例&#xff1a; 说明&#xff1a;一般用在(1)基本类型&#xf…

新黑马头条项目经验(黑马)

swagger (1)简介 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(API Documentation & Design Tools for Teams | Swagger)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0c;有利于团队协作 接…

微服务知识

Spring Cloud Netfix&#xff1a;Eureka&#xff0c;Ribbon&#xff0c;Feign&#xff0c;Hystrix&#xff0c;Zuul | Gateway&#xff0c;Config Spring Colud Alibaba&#xff1a;Nacos&#xff0c;Sentinel&#xff0c;Seata Nacos通过Ribbon实现负载均衡&#xff0c;Ribb…

【java笔记】java多线程

目录 一、概念 1.1 什么是进程&#xff1f; 1.2 什么是线程&#xff1f; 1.3 什么事多线程&#xff1f; 1.4 进程和线程的关系 二、线程对象的生命周期 三、实现线程有两种方式 3.1 继承 java.lang.Thread&#xff0c;重写 run方法 3.2 实现 java.lang.Runnable 接口…

材料写作素材:关于“大”排比句40例

1.一轮思想政治“大督查”&#xff0c;一轮政策落实“大检查”&#xff0c;一次非公企业“大走访”&#xff0c;一次问题线索“大起底”&#xff0c;一批典型案例“大曝光”。 2.在重大风险挑战面前豁得出去、顶得上去&#xff0c;在重大困难考验面前迎难而上、敢于胜利&#…

【计网】WebSocket协议

目录 一、背景 二、WebSocket握手过程 三、SpringBoot中使用WebSocket协议 1、服务器 2、客户端 一、背景 一般的web开发以请求响应为主即客户端发送一个请求&#xff0c;服务器返回一个响应&#xff0c;这就使得类似聊天等需求基于HTTP协议进行实现时比较消费资源&#xf…

大数据之Hadoop分布式文件系统HDFS

目录&#xff1a; 一、介绍二、HDFS 设计原理三、原理图形介绍四、HDFS 常用 shell 命令五、HDFS相关JavaAPI 一、介绍 HDFS &#xff08;Hadoop Distributed File System&#xff09;是 Hadoop 下的分布式文件系统&#xff0c;具有高容错、高吞吐量等特性&#xff0c;可以部署…

厨电新十年,不可逆的行业分化与老板电器的数字进化

“人生就像滚雪球&#xff0c;最重要之事是发现湿雪和长长的山坡。”股神巴菲特的这句名言&#xff0c;让坡是否长、雪是否厚成为人们评价一个行业、一家公司的标准之一。 家电行业&#xff0c;厨电曾是最后一块“坡长雪厚”之地&#xff0c;投资者也对相关企业给出了相当的热…

FIR半带滤波器

FIR半带滤波器 半带滤波器原理&#xff1a; CIC滤波器是一种适合于工作在高采样率条件下的滤波器。 半带滤波器是一种非常适合于2倍抽取的FIR滤波器。 半带滤波器可以使2倍抽取的每秒乘法次数比一般线性相位的FIR滤波器减少近1/2。 半带滤波器是一种实现数字下变频的高效数…

暗藏的比特币白皮书已删除 苹果其实与Web3“格格不入”?

据悉&#xff0c;Apple已从其最新的Mac OS Ventura beta中删除了比特币白皮书&#xff0c;虽然该公司从未对白皮书的存在提供任何官方解释&#xff0c;但许多人猜测这是对加密货币日益增长的重要性的认可。 4月上旬&#xff0c;科技专家Andy Baio偶然发现&#xff0c;自己的Mac…

浅述 国产仪器 1652AM任意波形发生器

1652AM任意波形发生器是一款多通道、多功能的任意波形发生器。它在兼顾了输出信号高质量的同时&#xff0c;实现了高通道密度。它可与其它通用或专用模块化测试仪器构成综合测试系统或平台&#xff0c;支持众多解决方案&#xff0c;包括量子计算机调控信号生成、大规模MIMO信号…

活动目录(Active Directory)安全审计

延迟响应变化的影响可能会使原本应该微不足道的颠簸滚雪球变成无法弥补的损害。这在 Windows Active Directory 环境中更为重要&#xff0c;因为这种延迟造成的损害可能会使组织损失数百万美元&#xff01;在这种情况下&#xff0c;需要一个警惕的警报系统&#xff0c;该系统可…

WPF学习

一、了解WPF的框架结构 &#xff08;第一小节随便看下就可以&#xff0c;简单练习就行&#xff09; 1、新建WPF项目 xmlns&#xff1a;XML的命名空间 Margin外边距&#xff1a;左上右下 HorizontalAlignment&#xff1a;水平位置 VerticalAlignment&#xff1a;垂直位置 2…

性能测试开始前的需求调研

之前的博客聊聊性能测试开始前的准备工作&#xff0c;聊了一些关于性能测试开始前要做的准备工作。这篇博客&#xff0c;来谈谈性能测试开始前的需求调研阶段&#xff0c;我们要做什么&#xff0c;关注那些Point。。。 一、基本信息 信息类型说明项目名称项目归属的业务线&am…

低代码应用开发平台,让数据管理更简单!

在数据管理越来越规范化的今天&#xff0c;低代码应用开发平台也获得了进步和发展的机会和空间。想要将企业内部的数据资源做好系统管理&#xff0c;从而为各个时期的经营决策提供强有力的证据和基础&#xff0c;低代码应用开发平台就是其中备受喜爱的工具。本文主要从它的特点…

重磅!阿里云云原生合作伙伴计划全新升级:加码核心权益,与伙伴共赢新未来

在今天的 2023 阿里云合作伙伴大会上&#xff0c;阿里云智能云原生应用平台运营&生态业务负责人王荣刚宣布&#xff1a; “阿里云云原生合作伙伴计划”全新升级。他表示&#xff1a; 云原生致力于帮助企业客户最大限度的减轻运维工作&#xff0c;更好的实现敏捷创新&#x…

MQTT 协议

MQTT 简介 MQTT是一种基于客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范&#xff0c;易于实现。这些特点得它对很多场景来说都是很有的选择&#xff0c;特别是对于受限的环境如机器与机器的通信&#xff08;M2M&#xff09;以及物联网…

RAC集群节点2异常时节点1的database实例无法提供服务问题的分析

在客户的数据库RAC集群环境中&#xff0c;节点2发生了异常&#xff0c;最终通过重启解决。在节点2发生异常的10分钟左右时间内&#xff0c;由于RAC集群节点2异常&#xff0c;此时节点1的database实例无法提供服务问题&#xff0c;程序操作报超时&#xff1b; 对此现象&#xf…