Android 根据Url使用Retrofit框架进行文件下载

一、背景

根据后端返回的url下载地址,去执行文件下载,将文件保存到SD卡。这里使用Retrofit网络框架。

二、代码实现

2.1、定义一个DownloadFileService
Kotlin 复制代码
interface DownloadFileService {
    @Streaming
    @GET
    suspend fun downloadFile(@Url fileUrl: String):ResponseBody
}
2.2、定义一个FileDownloadClient
Kotlin 复制代码
private var mDownliadService: DownloadFileService? = null

    fun getUploadFileService() :DownloadFileService{
        if(mDownliadService==null){
            val okHttpClient = OkHttpClient.Builder()
                .callTimeout(0, TimeUnit.SECONDS)//0 代表不考虑请求的超时 这里的超时是指整个请求过程的超时,如果设置太短,任务还没执行完,会以超时结束任务,这里尤其要注意
                .connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false)// 重连
                .followRedirects(false)// 重定向
                //.addInterceptor(RequestInterceptor(authorization,requestId,offset,uploadType))
                //.addInterceptor(RemoveContentLengthInterceptor())
                //.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                .build()

            val retrofit: Retrofit = Retrofit.Builder()
                .baseUrl(DeviceUtil.getInstance().getServerIp())
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build()
            mDownliadService= retrofit.create(DownloadFileService::class.java)
       }

        return mDownliadService!!
    }
2.3、定义一个DownloadCallback
Kotlin 复制代码
interface DownloadCallback {
    fun onSuccess(srcApkTarPath:String)
    fun onProgress(progress: Int)
    fun onFailure(msg:String?)
}
2.4、定义一个FileDownloadManager,执行文件下载
Kotlin 复制代码
class FileDownloadManager {

    private val TAG = "FileDownloadManager_"

    companion object {
        private var singleInstance: FileDownloadManager? = null
            get() {
                // 懒汉模式
                if (null == field) {
                    field = FileDownloadManager()
                }
                return field
            }

        @Synchronized // 添加注解,线程同步,线程安全
        fun getInstance(): FileDownloadManager {
            return singleInstance!! // 表示非空时执行
        }
    }


    fun doDownloadFile(
        appName:String,
        packageName: String,
        downUrl: String,
        fileSize: Long,
        fileMd5:String,
        downloadCallback: DownloadCallback
    ) {

        try {
            val coroutineScope = CoroutineScope(Dispatchers.Default)
            coroutineScope.launch(Dispatchers.IO) {
                val response = FileDownloadClient.getUploadFileService().downloadFile(downUrl)
                val length = response.contentLength()
                XLogUtil.d("${TAG}doDownloadFile result appName:$appName,,,packageName:$packageName,,,length:$length,,,fileSize:$fileSize,,,downUrl:$downUrl")

                if (length > 0) {
                    val folder = File(ConstantUtil.DOWNLOAD_FOLDER_PATH)
                    if (!folder.exists()) {
                        folder.mkdirs()
                    }

                    // 使用输入流保存响应体到文件
                    val inputStream = response.byteStream()
                    val fileName = "$packageName.tar"

                    var fileTargetPath =
                        ConstantUtil.DOWNLOAD_FOLDER_PATH + File.separator + fileName

                    val outputStream = FileOutputStream(fileTargetPath)

                    val buf = ByteArray(1024)
                    var downLoadFileSize = 0
                    var lastProgress = 0
                    do {
                        val numread = inputStream.read(buf)
                        if (numread == -1) {
                            break
                        }
                        outputStream.write(buf, 0, numread)
                        downLoadFileSize += numread

                        val progressValue =
                            ((downLoadFileSize * 100f) / length).toInt()

                          //相同的进度值只回调一次
                        if (progressValue > lastProgress) {
                            lastProgress = progressValue
                            downloadCallback?.apply {
                                onProgress(progressValue)
                            }
                        }
                    } while (true)


                    outputStream.flush()
                    // 关闭文件输出流和输入流
                    outputStream.close()
                    //inputStream.close()
                    val localFileMd5=FileUtil.getInstance().getFileMd5(fileTargetPath)
                    if(localFileMd5==fileMd5){
                        downloadCallback?.onSuccess(fileTargetPath)
                    }else{
                        downloadCallback?.onFailure("文件 md5 不一致,localFileMd5:$localFileMd5,,,fileMd5:$fileMd5")
                    }
                }else{
                    downloadCallback?.onFailure("读取文件len=0")
                }
            }
        } catch (e: Exception) {
            XLogUtil.e("${TAG}doDownloadFile Exception appName:$appName,,,packageName:$packageName,,,Exception:${e.message},,,fileSize:$fileSize,,,downUrl:$downUrl")
            downloadCallback?.onFailure(e.message)
        }
    }
}
相关推荐
二流小码农1 小时前
鸿蒙开发:正则中的match和matchAll
android·ios·harmonyos
小龙Aaron2 小时前
滑动冲突
android
念丶小宇4 小时前
Android笔记之项目引用第三方库(如:Github等)
android·笔记·github
wayne2144 小时前
Android开发必备技能:数据序列化全解析(含代码示例)
android
苏啵曼4 小时前
鸿蒙如何显示临时数据?在预览(@Preview)时
android·前端·harmonyos
帅次4 小时前
Flutter TextFormField 完全手册与设计最佳实践
android·flutter·ios·小程序·kotlin·gradle·android studio
金是沉默。5 小时前
android初学
android
氦客5 小时前
kotlin知识体系(三) : Android Kotlin 中的函数式编程实践指南
android·开发语言·kotlin
金是沉默。5 小时前
android......
android
是阿建吖!6 小时前
【MySQL】索引
android·数据库·mysql