學(xué)習(xí)了kotlin后,我們將它運(yùn)用到實際開發(fā)中,結(jié)合Flow實現(xiàn)文件下載
最終效果:

項目使用了Navigation框架:Activity+Fragment的方式
導(dǎo)入依賴:
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'
//依賴協(xié)程核心庫 ,提供Android UI調(diào)度器
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1"
//依賴當(dāng)前平臺所對應(yīng)的平臺庫 (必須)
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
并啟用DataBinding
dataBinding {
enabled = true
}
fragment的創(chuàng)建、Navigation Graph的連接等操作就不介紹了
1.首先實現(xiàn)下載工具類,包含狀態(tài)和下載實現(xiàn)
使用密封類定義狀態(tài):
package com.aruba.flowapplyapplication.download
import java.io.File
/**
* 下載狀態(tài)
* 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()
}
靜態(tài)方法方式定義下載管理類:
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 ->
//當(dāng)前下載進(jìn)度
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添加擴(kuò)展函數(shù),實現(xiàn)字節(jié)拷貝:
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定義進(jìn)度屬性,并實現(xiàn)下載按鈕的點擊事件,由于Flow的collect函數(shù)為掛起函數(shù),需要使用協(xié)程作用域,我們直接使用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可以自行感受下