kotlin--Flow文件下载

学习了kotlin后,我们将它运用到实际开发中,结合Flow实现文件下载

最终效果:

项目使用了Navigation框架:Activity+Fragment的方式

导入依赖:

    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    //依赖协程核心库 ,提供Android UI调度器
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1"
    //依赖当前平台所对应的平台库 (必须)
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'

并启用DataBinding

        dataBinding {
            enabled = true
        }

fragment的创建、Navigation Graph的连接等操作就不介绍了

1.首先实现下载工具类,包含状态和下载实现

使用密封类定义状态:

package com.aruba.flowapplyapplication.download

import java.io.File

/**
 * 下载状态
 * Created by aruba on 2021/9/19.
 */
sealed class DownloadStatus {
    data class Progress(val progress: Int) : DownloadStatus()
    data class Err(val t: Throwable) : DownloadStatus()
    data class Done(val file: File) : DownloadStatus()
}

静态方法方式定义下载管理类:

package com.aruba.flowapplyapplication.download

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

/**
 * Created by aruba on 2021/9/19.
 */
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 { body ->
                    //文件大小
                    val totalLength = body.contentLength().toDouble()
                    //写文件
                    file.outputStream().run {
                        val input = body.byteStream()
                        input.copyTo(this) { currentLength ->
                            //当前下载进度
                            val process = currentLength / totalLength * 100
                            emit(DownloadStatus.Progress(process.toInt()))
                        }
                    }

                    emit(DownloadStatus.Done(file))
                }
            } else {
                throw IOException(response.toString())
            }
        }.catch {
            file.delete()
            emit(DownloadStatus.Err(it))
        }.flowOn(Dispatchers.IO)
    }
}

InputStream添加扩展函数实现字节拷贝

package com.dongnaoedu.flowpractice.utils

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

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
}

2.定义ViewModel

使用LiveData定义进度属性,并实现下载按钮的点击事件,由于Flow的collect函数为挂起函数,需要使用协程作用域,我们直接使用viewModelScope

package com.aruba.flowapplyapplication.viewmodel

import android.app.Application
import android.content.Context
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.lifecycle.*
import com.aruba.flowapplyapplication.download.DownloadManager
import com.aruba.flowapplyapplication.download.DownloadStatus
import com.aruba.flowapplyapplication.download.DownloadStatus.Progress
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import java.io.File

/**
 * Created by aruba on 2021/9/19.
 */
class DownloadViewModel(val context: Application) : AndroidViewModel(context) {
    private var progressData = MutableLiveData<Int>()
    val progress = progressData

    private val url: String = "http://10.254.219.178:8080/test.rar"

    fun downloadClick(v: View) {
        viewModelScope.launch {
            progressData.value = 0
            val file = File(context.getExternalFilesDir(null), "test.rar")
            DownloadManager.download(url, file).collect {
                when (it) {
                    is Progress -> {
                        Log.i("progress", "progress: $it.progress")
                        progressData.value = it.progress
                    }
                    is DownloadStatus.Done -> {
                        progressData.value = 100
                        Toast.makeText(context, "下载完成", Toast.LENGTH_SHORT).show()
                    }
                    is DownloadStatus.Err ->
                        Toast.makeText(context, it.toString(), Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}
3.DataBinding和ViewModel绑定
package com.aruba.flowapplyapplication

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.aruba.flowapplyapplication.databinding.FragmentFlowDownBinding
import com.aruba.flowapplyapplication.viewmodel.DownloadViewModel

class FlowDownloadFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val fragmentFlowDownBinding = DataBindingUtil.inflate<FragmentFlowDownBinding>(
            layoutInflater,
            R.layout.fragment_flow_down,
            container,
            false
        )

        val downloadViewModel = ViewModelProvider(
            this,
            ViewModelProvider.AndroidViewModelFactory(requireActivity().application)
        ).get(DownloadViewModel::class.java)
        fragmentFlowDownBinding.downloadViewModel = downloadViewModel
        fragmentFlowDownBinding.lifecycleOwner = this;

        return fragmentFlowDownBinding.root
    }

}

文件下载就完成了,代码量相比Java可以自行感受下

Demo地址:https://gitee.com/aruba/flow-apply-application.git
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容