Kotlin+Jetpack 实现一键式下载

简介: Kotlin+Jetpack 实现一键式下载

首先看一下效果:


2019082413041482.gif


使用方式:


fileUrl.setText("https://kotlinlang.org/docs/kotlin-docs.pdf")
        fileName.setText("Kotlin-Docs.pdf")
        downloadButton.setOnClickListener {
            DownLoadLaunch.create(this, fileUrl.text.toString(),
                    fileName.text.toString(), object : OnStateListener {
                override fun start() {
                    Toast.makeText(context, "开始下载", Toast.LENGTH_LONG).show()
                }
                override fun process(value: Int) {
                    downloadButton.text = "Downloading $value"
                }
                override fun error(throwable: Throwable) {
                    Toast.makeText(context, "下载出错:${throwable.message}", Toast.LENGTH_LONG).show()
                    downloadButton.text = "DownLoad"
                }
                override fun donal(file: File) {
                    downloadButton.text = "下载成功"
                    downloadPath.setText(file.absolutePath)
                    Toast.makeText(context, "下载完成:" + file.path, Toast.LENGTH_LONG).show()
                }
            })
        }


只需要指定 url 和 文件的名字,在调用一个方法,就可以实现文件的下载。并且当前活动销毁时,就会停止下载


下面看一下使用方法:


1,导入依赖:


implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation "androidx.core:core-ktx:1.2.0"
    //协程基础库
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
    //协程 Android 库,提供 UI 调度器
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"
    //liveData + ViewModel + lifecycle
    implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0-alpha01"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-alpha01"
    implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:2.3.0-alpha01"
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha01"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-alpha01"


2,创建 DownLoadManager


object DownLoadManager {
    //创建文件
    private val downloadDirectory by lazy {
        File(ContextTools.context.filesDir, "download").also { it.mkdirs() }
    }
  //状态
    sealed class DownloadStatus {
        object None : DownloadStatus()
        class Progress(val value: Int) : DownloadStatus()
        class Error(val throwable: Throwable) : DownloadStatus()
        class Donel(val file: File) : DownloadStatus()
    }
  //下载
    fun download(url: String, fileName: String): Flow<DownloadStatus> {
        val file = File(downloadDirectory, fileName)
        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 total = body.contentLength()
                    //当前值
                    var emittedProcess = 0L
                    file.outputStream().use { output ->
                        body.byteStream().use { input ->
                            input.copyTo(output) { bytesCopied ->
                                //计算百分比
                                val progress = bytesCopied * 100 / total
                                //当前的值大于上一次的就进行通知
                                if (progress - emittedProcess > 1) {
                                    //发射,外部的 collect 会执行
                                    emit(DownloadStatus.Progress(progress.toInt()))
                                    emittedProcess = progress
                                }
                            }
                        }
                    }
                    //下载完成
                    emit(DownloadStatus.Donel(file))
                }
            } else {
                throw Exception(response.message)
            }
        }.catch {
            file.delete()
            emit(DownloadStatus.Error(it))
            //保留最新的值
        }.conflate()
    }
}


//重写了一些copyTo 方法,增加了一个参数,用来回调下载进度
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
}


上面主要使用了 flow 进行下载,不了解的可以先百度一下


3,创建 DownloadModel ,用来进行下载


class DownloadModel : ViewModel() {
    val downloadStateLiveData =
            MutableLiveData<DownLoadManager.DownloadStatus>(DownLoadManager.DownloadStatus.None)
    suspend fun download(url: String, fileName: String) {
        DownLoadManager.download(url, fileName)
                .flowOn(Dispatchers.IO)
                .collect {
                    //发送数据
                    downloadStateLiveData.value = it
                }
    }
}


4,创建 DownloadLaunch,入口类,我们调用这个类的方法进行下载


interface OnStateListener {
    fun start()
    fun process(value: Int)
    fun error(throwable: Throwable)
    fun donal(file: File)
}
object DownLoadLaunch {
    private val mDownloadModel: DownloadModel = DownloadModel()
    fun create(
            owner: LifecycleOwner,
            url: String,
            fileName: String,
            stateListener: OnStateListener
    ) {
        //这里的 Lambda 会被多次调用,当 liveData 发送消息后 lambda会得到执行
        mDownloadModel.downloadStateLiveData.observe(owner) { status ->
            when (status) {
                DownLoadManager.DownloadStatus.None -> {
                    //启动协程
                    owner.lifecycleScope.launch {
                        stateListener.start()
                        //下载
                        mDownloadModel.download(url, fileName)
                    }
                }
                is DownLoadManager.DownloadStatus.Progress -> {
                    stateListener.process(status.value)
                }
                is DownLoadManager.DownloadStatus.Error -> {
                    stateListener.error(status.throwable)
                }
                is DownLoadManager.DownloadStatus.Donel -> {
                    stateListener.donal(status.file)
                }
            }
        }
    }
}


5,进行使用


<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/fileUrl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="文件路径"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:textColor="#000000"
        android:textSize="25sp" />
    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/fileName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:layout_marginTop="20dp"
        android:paddingRight="20dp"
        android:hint="文件名字"
        android:textColor="#000000"
        android:textSize="25sp"
     />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/downloadButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="DownLoad"
        android:layout_marginTop="20dp"
        android:textAllCaps="false" />
    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/downloadPath"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:textColor="#000000"
        android:layout_marginTop="20dp"
        android:textSize="25sp"
        android:textStyle="bold" />
</androidx.appcompat.widget.LinearLayoutCompat>
class DownLoadActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_down_load)
        init()
    }
    @SuppressLint("SetTextI18n")
    private fun init() {
        fileUrl.setText("https://kotlinlang.org/docs/kotlin-docs.pdf")
        fileName.setText("Kotlin-Docs.pdf")
        downloadButton.setOnClickListener {
            DownLoadLaunch.create(this, fileUrl.text.toString(),
                    fileName.text.toString(), object : OnStateListener {
                override fun start() {
                    Toast.makeText(context, "开始下载", Toast.LENGTH_LONG).show()
                }
                override fun process(value: Int) {
                    downloadButton.text = "Downloading $value"
                }
                override fun error(throwable: Throwable) {
                    Toast.makeText(context, "下载出错:${throwable.message}", Toast.LENGTH_LONG).show()
                    downloadButton.text = "DownLoad"
                }
                override fun donal(file: File) {
                    downloadButton.text = "下载成功"
                    downloadPath.setText(file.absolutePath)
                    Toast.makeText(context, "下载完成:" + file.path, Toast.LENGTH_LONG).show()
                }
            })
        }
    }
}


相关文章
|
5月前
|
存储 缓存 Android开发
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【远程url】音频,搭配Okhttp库进行下载和缓存,播放完随机播放下一首
这是一个Kotlin项目,使用Jetpack Compose和ExoPlayer框架开发Android应用,功能是播放远程URL音频列表。应用会检查本地缓存,如果文件存在且大小与远程文件一致则使用缓存,否则下载文件并播放。播放完成后或遇到异常,会随机播放下一首音频,并在播放前随机设置播放速度(0.9到1.2倍速)。代码包括ViewModel,负责音频管理和播放逻辑,以及UI层,包含播放和停止按钮。
|
5月前
|
安全 Java Android开发
安卓开发中的新趋势:Kotlin与Jetpack的完美结合
【6月更文挑战第20天】在不断进化的移动应用开发领域,Android平台以其开放性和灵活性赢得了全球开发者的青睐。然而,随着技术的迭代,传统Java语言在Android开发中逐渐显露出局限性。Kotlin,一种现代的静态类型编程语言,以其简洁、安全和高效的特性成为了Android开发中的新宠。同时,Jetpack作为一套支持库、工具和指南,旨在帮助开发者更快地打造优秀的Android应用。本文将探讨Kotlin与Jetpack如何共同推动Android开发进入一个新的时代,以及这对开发者意味着什么。
|
20天前
|
存储 前端开发 测试技术
Kotlin教程笔记-使用Kotlin + JetPack 对旧项目进行MVVM改造
Kotlin教程笔记-使用Kotlin + JetPack 对旧项目进行MVVM改造
|
2月前
|
安全 Java Android开发
探索安卓应用开发的新趋势:Kotlin和Jetpack Compose
在安卓应用开发领域,随着技术的不断进步,新的编程语言和框架层出不穷。Kotlin作为一种现代的编程语言,因其简洁性和高效性正逐渐取代Java成为安卓开发的首选语言。同时,Jetpack Compose作为一个新的UI工具包,提供了一种声明式的UI设计方法,使得界面编写更加直观和灵活。本文将深入探讨Kotlin和Jetpack Compose的特点、优势以及如何结合使用它们来构建现代化的安卓应用。
57 4
|
4月前
|
存储 移动开发 Android开发
使用kotlin Jetpack Compose框架开发安卓app, webview中h5如何访问手机存储上传文件
在Kotlin和Jetpack Compose中,集成WebView以支持HTML5页面访问手机存储及上传音频文件涉及关键步骤:1) 添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,考虑Android 11的分区存储;2) 配置WebView允许JavaScript和文件访问,启用`javaScriptEnabled`、`allowFileAccess`等设置;3) HTML5页面使用`<input type="file">`让用户选择文件,利用File API;
|
5月前
|
JavaScript Java Android开发
kotlin安卓在Jetpack Compose 框架下跨组件通讯EventBus
**EventBus** 是一个Android事件总线库,简化组件间通信。要使用它,首先在Gradle中添加依赖`implementation &#39;org.greenrobot:eventbus:3.3.1&#39;`。然后,可选地定义事件类如`MessageEvent`。在活动或Fragment的`onCreate`中注册订阅者,在`onDestroy`中反注册。通过`@Subscribe`注解方法处理事件,如`onMessageEvent`。发送事件使用`EventBus.getDefault().post()`。
|
5月前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
5月前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
5月前
|
监控 Android开发 数据安全/隐私保护
安卓kotlin JetPack Compose 实现摄像头监控画面变化并录制视频
在这个示例中,开发者正在使用Kotlin和Jetpack Compose构建一个Android应用程序,该程序 能够通过手机后置主摄像头录制视频、检测画面差异、实时预览并将视频上传至FTP服务器的Android应用
|
5月前
|
安全 网络安全 API
kotlin安卓开发JetPack Compose 如何使用webview 打开网页时给webview注入cookie
在Jetpack Compose中使用WebView需借助AndroidView。要注入Cookie,首先在`build.gradle`添加WebView依赖,如`androidx.webkit:webkit:1.4.0`。接着创建自定义`ComposableWebView`,通过`CookieManager`设置接受第三方Cookie并注入Cookie字符串。最后在Compose界面使用这个自定义组件加载URL。注意Android 9及以上版本可能需要在网络安全配置中允许第三方Cookie。