一、背景
根據后端返回的url下載地址,去執行文件下載,將文件保存到SD卡。這里使用Retrofit網絡框架。
二、代碼實現
2.1、定義一個DownloadFileService
interface DownloadFileService {@Streaming@GETsuspend fun downloadFile(@Url fileUrl: String):ResponseBody
}
2.2、定義一個FileDownloadClient
private var mDownliadService: DownloadFileService? = nullfun 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
interface DownloadCallback {fun onSuccess(srcApkTarPath:String)fun onProgress(progress: Int)fun onFailure(msg:String?)
}
2.4、定義一個FileDownloadManager,執行文件下載
class FileDownloadManager {private val TAG = "FileDownloadManager_"companion object {private var singleInstance: FileDownloadManager? = nullget() {// 懶漢模式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 + fileNameval outputStream = FileOutputStream(fileTargetPath)val buf = ByteArray(1024)var downLoadFileSize = 0var lastProgress = 0do {val numread = inputStream.read(buf)if (numread == -1) {break}outputStream.write(buf, 0, numread)downLoadFileSize += numreadval progressValue =((downLoadFileSize * 100f) / length).toInt()//相同的進度值只回調一次if (progressValue > lastProgress) {lastProgress = progressValuedownloadCallback?.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)}}
}