//扩展方法 读写文件并返回,下载进度
inline fun InputStream.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE, progress: (Long)-> Unit): Long {
var bytesCopied: Long = 0
val buffer = ByteArray(bufferSize)
var bytes = read(buffer)
while (bytes >= 0) {
out.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = read(buffer)
progress(bytesCopied)
}
return bytesCopied
}
sealed class DownloadStatus{
data class Progress(val value:Int):DownloadStatus() //返回下载进度
data class Error(val throwable: Throwable):DownloadStatus() //返回失败错误
data class Done(val file: File):DownloadStatus() //返回结果
}
此类为枚举类,启得是封装的作用
object DownloadManager {
fun download(url: String, file: File): Flow<DownloadStatus> {
return flow {
val request = Request.Builder().url(url).get().build()
val response = OkHttpClient.Builder().build().newCall(request).execute() //使用OKHttp来进行网络请求
if (response.isSuccessful) { //是否请求成功
response.body()!!.let { body ->
val contentLength = body.contentLength()//下载内容总长度
file.outputStream().use { output ->
val input = body.byteStream()
var emittedProcess = 0L
input.copyTo(output) { bytesCopied ->
val progress = bytesCopied * 100 / contentLength //计算下载进度
if (progress - emittedProcess > 5) {
delay(100)
emit(DownloadStatus.Progress(progress.toInt()))//发送下载进度
emittedProcess=progress
}
}
}
}
emit(DownloadStatus.Done(file)) //返回结果
} else {
throw IOException(response.toString()) //没有请求成功抛出异常
}
}.catch {
file.delete()//发生异常 删除错误文件
emit(DownloadStatus.Error(it))//并返回错误
}.flowOn(Dispatchers.IO)//切换子线程
}
}
单例类,封装下载代码。
class Fragmentb : Fragment() {
val url:String="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fclubimg.club.vmall.com%2Fdata%2Fattachment%2Fforum%2F202005%2F15%2F230954fsa0bsgqep8zu2j9.jpg&refer=http%3A%2F%2Fclubimg.club.vmall.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637312693&t=4d8c9a64de10cb68fb26d027a0ee4225"
private val mBinding: FragmentFragmentbBinding by lazy {
FragmentFragmentbBinding.inflate(layoutInflater)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return mBinding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//这个协程和fragment的生命周期绑定
lifecycleScope.launchWhenCreated {
context?.apply {
// 外部文件目录路径
val file=File(getExternalFilesDir(null)?.path,"pic.jpg")
DownloadManager.download(url,file).collect {
status->
when(status){
is DownloadStatus.Progress->{ //下载进度
mBinding.apply {
progressBar.progress=status.value
tvProgress.text="${status.value}%"
}
}
is DownloadStatus.Error->{ //下载出错
Toast.makeText(context,"下载错误",Toast.LENGTH_SHORT).show()
}
is DownloadStatus.Done->{ //下载完成
mBinding.apply {
progressBar.progress=100
tvProgress.text="100&"
}
Toast.makeText(context,"下载完成",Toast.LENGTH_SHORT).show()
}else->{
Log.d("ning","下载失败")
}
}
}
}
}
}
}
在fragment中引用并给UI反馈。
效果演示:
?
|