Android Kotlin Flow下载图片并回调进度和错误

Posted 安果移不动

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Kotlin Flow下载图片并回调进度和错误相关的知识,希望对你有一定的参考价值。

 用到了依赖

  implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.5.1'
    implementation 'com.google.android.material:material:1.6.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    implementation "androidx.activity:activity-ktx:1.5.1"
    implementation "androidx.fragment:fragment-ktx:1.5.2"

    implementation "androidx.room:room-runtime:2.4.3"
    implementation "androidx.room:room-ktx:2.4.3"
    kapt "androidx.room:room-compiler:2.4.3"

    implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
    implementation "androidx.navigation:navigation-ui-ktx:2.5.2"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"

    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"

    implementation "androidx.legacy:legacy-support-v4:1.0.0"

    implementation "com.squareup.retrofit2:retrofit:2.9.0"
    implementation "com.squareup.retrofit2:converter-gson:2.9.0"

 没用到的也有。。不做剔除了

 权限

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

 下载核心类

package com.example.android_flow_practice.download

import com.example.android_flow_practice.utils.copyTo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import okhttp3.Dispatcher
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.io.IOException

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()
            if (response.isSuccessful) 
                response.body()!!.let  responseBody ->
                    val total = responseBody.contentLength()
                    //文件读写操作
                    file.outputStream().use  output ->
                        val input = responseBody.byteStream()
                        var emittedProgress = 0L
                        input.copyTo(output)  bytes ->
                            val progress = bytes * 100 / total
                            if (progress - emittedProgress > 5) 
                                delay(100)
                                emit(DownLoadStatus.Progress(progress.toInt()))
                                emittedProgress = progress;
                            

                        

                    

                
                emit(DownLoadStatus.Done(file))
             else 
                throw IOException(response.toString())
            
        .catch 
            file.delete()
            emit(DownLoadStatus.Error(it))

        .flowOn(Dispatchers.IO)

    

核心类用到了一个 

DownLoadStatus 的密封类
package com.example.android_flow_practice.download

import java.io.File

//密封类 成员都是他的子类
sealed class DownLoadStatus 

    object None : DownLoadStatus()
    data class Progress(val value: Int) : DownLoadStatus()

    data class Error(val throwable: Throwable) : DownLoadStatus()

    data class Done(val file: File) : DownLoadStatus()


用到了一个下载帮助类 

IOExt.kt
package com.example.android_flow_practice.utils

import java.io.InputStream
import java.io.OutputStream

inline fun InputStream.copyTo(
    out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE, progress: (value: Long) -> Unit
): Long 
    var btyesCopied: Long = 0
    val buffer = ByteArray(bufferSize)
    var bytes = read(buffer)

    while (bytes >= 0) 
        out.write(buffer, 0, bytes)
        btyesCopied += bytes
        bytes = read(buffer)
        progress(btyesCopied)
    
    return btyesCopied;

那其实代码布局也可以放进来

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context=".fragment.HomeFragment">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:max="100" />

    <TextView
        android:id="@+id/tv_progress"
        android:layout_width="wrap_content"

        android:layout_height="wrap_content"
        android:layout_below="@id/progressBar"
        android:layout_centerHorizontal="true"
        android:textSize="20sp"
        tools:text="10%"

        />


</RelativeLayout>

下载对应的代码

package com.example.android_flow_practice.fragment


class DownloadFragment : Fragment() 

    private val mBinding: FragmentDownloadBinding by lazy 
        FragmentDownloadBinding.inflate(layoutInflater)
    

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? 
        // Inflate the layout for this fragment
        return mBinding.root
    

    private val TAG = "DownloadFragment"

    override fun onActivityCreated(savedInstanceState: Bundle?) 
        super.onActivityCreated(savedInstanceState)
        lifecycleScope.launchWhenCreated 
            context?.apply 
                val url = "https://www.xxx.com/xxx.png"
                val path = getExternalFilesDir(null)?.path
                Log.e(TAG, "path $path")
                val file = File(path, "pic.png")
                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(TAG, "下载错误")
                        
                    
                

            
        

    

 这里下了一个图片并存放到sd卡当中

图片网络地址你们替换下吧

以上是关于Android Kotlin Flow下载图片并回调进度和错误的主要内容,如果未能解决你的问题,请参考以下文章

用Kotlin Flow解决Android开发中的痛点问题

用Kotlin Flow解决Android开发中的痛点问题

用Kotlin Flow解决Android开发中的痛点问题

Android Kotlin Paging3 Flow完整教程

Android SingleLiveEvent Redux with Kotlin Flow

Kotlin 协程Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )