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卡当中

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

猜你喜欢

转载自blog.csdn.net/mp624183768/article/details/126801060