Android使用AsyncTask完成一个简单的下载进度条

2020-05-27  本文已影响0人  番茄tomato

参考文章:https://www.jianshu.com/p/ee1342fcf5e7
这是一个使用AsyncTask完成的模拟下载进度条:

模拟下载进度条
MainActivity布局如下:非常简单,就是button,textview,progressbar,button从上到下排列
    <Button
        android:id="@+id/startBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Start"
        />

    <TextView
        android:id="@+id/downloadHintText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="等待开始下载数据"
        />
    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:max="100"
        android:progress="0" />
    <Button
        android:id="@+id/cancelBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="cancel"
        />

然后是DownloadTask代码,这里需要继承于AsyncTask,并确定三个范形:

//第一个参数是执行异步操作传入的参数 Unit表示不需要参数
//第二个参数是进度条表示
//第三个参数是异步操作结束后 返回的参数 这里是布尔型表示是否成功
class DownloadTask() : AsyncTask<Unit, Int, Boolean>() {
    private var TAG = "下载任务"
    //控制界面
    var progressBar: ProgressBar? = null
    var downloadHint:TextView?=null
    var context: Context? = null
    //进度条最大长度
    var progressBarMax=10000
    override fun onPreExecute() {
        super.onPreExecute()
        //异步操作执行之前 可以显示加载框等操作

        downloadHint?.text="准备开始下载"
    }

    override fun doInBackground(vararg params: Unit?): Boolean {
        //后台线程执行的内容
        //执行完毕后 返回内容
        return try {

            startDownload()//开始下载
            true//下载成功没有出错 则返回true表示下载成功
        } catch (e: Exception) {
            false//下载出错则返回false表示下载失败
        }
    }

    override fun onProgressUpdate(vararg values: Int?) {
        //更新显示进度条进度
        super.onProgressUpdate(*values)
        Log.d(TAG, "显示进度: ${values[0].toString()}")

        //这里将进度以百分数的形势表示出来
        val df = DecimalFormat("0.00")
        var hintContent= ((values[0]?.toDouble())?.div(progressBarMax))?.times(100)
        downloadHint?.text="下载中,进度${df.format(hintContent)}%"

        //设置最大长度 并显示进度条
        progressBar?.max=progressBarMax //最大长度10000 显示内容就是 12-->0.12%
        progressBar?.progress = values[0] ?: 0//需要捕捉空指针 如果是空就显示0

    }

    override fun onPostExecute(result: Boolean?) {
        //执行完毕 可以隐藏关闭进度条
        super.onPostExecute(result)
        if(result!!){
            downloadHint?.text="下载完成"
        }else{
            downloadHint?.text="下载失败"
        }
    }

        //调用cancel后 这个方法会被执行
    override fun onCancelled() {
        super.onCancelled()
       downloadHint?.text="下载取消"
    }


    //在这个方法中进行下载并计算加载进
    private fun startDownload() {
        Log.d(TAG, "开始下载")
        var progress:Int=0
        var fileSize:Double =0.0//下载中的文件
        var allFileSize :Double= 10000.0//总共需要下载的文件大小

        while (true) {
            if(isCancelled){break}//在循环中 任务被取消 就退出

            sleep(1)//控制下载速度 1毫秒下载1个单位
            fileSize += 1
            progress= ((fileSize/allFileSize)*progressBarMax).toInt()

            publishProgress(progress)

            if (fileSize >= allFileSize) {
                break
            }
        }
    }
}

接下来就是在MainActivity中让DownloadTask跑起来:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var downloadTask=DownloadTask()
        //绑定View
        downloadTask.progressBar=progressBar//要动态修改的加载框
        downloadTask.context=this//传入上下文 可以在downloadTask中显示toast
        downloadTask.downloadHint=downloadHintText
        startBtn.setOnClickListener {
            //task智能被执行一次 再次点击会崩溃 这里可以做好判断
            downloadTask.execute()
        }
        cancelBtn.setOnClickListener {
            //点击取消
            downloadTask.cancel(true)
        }
    }
}

代码非常简单,注释也写的很清楚,就不一一说明了。
这里提几个注意点:

1. downloadTask.execute()

这个方法只能被执行一次,再次执行会报错,所以在点击下载按钮时可以进行一下判断

2.downloadTask.cancel(true)

https://www.cnblogs.com/xqxacm/p/5227159.html
执行这个方法后,onCancelled()会很快的被调用,这里可以修改ui提示任务终止,但是其实任务并没有停下,只是isCancelled被标记为true了,想让任务真正的停下,需要增加具体的逻辑判断

        while (true) {
            if(isCancelled){break}//在循环中 任务被取消 就退出
             .....
        }

为了避免界面销毁后任务仍然进行,可以在界面生命周期onDestroy中调用cancel(true)

3.计算和显示百分比下载进度

下载进度=已下载的文件大小/总文件大小 //例如等于0.5
但是progressBar只能使用整数表示进度,例如进度条总大小progressBarMax=10000,进度progress就是1~10000
所以需要将刚刚计算出的0.5乘以progressBarMax得到进度progress=5000
这时候就是progressBar就刚好进行到一半了。

但是这里还在需要在上方显示 50.00% 文字提示,直接根据progress=5000和progressBarMax=10000计算即可,相处得0.5,再乘100,加上“%”,不就是50%了。
但其实错了,在Kotlin和JAVA中,Int之间的运算只能得Int。Int相除有小数时直接舍弃掉小数点后的部分,所以这里progress/progressBarMax=0,需要将其中一个使用toDouble()方法转化为Double数据,再计算得0.50000000,然后乘100得50.0000000,保留两位小数加上百分号:50.00%

        //这里将进度以百分数的形势表示出来
        val df = DecimalFormat("0.00")//保留两位小数
        var hintContent= ((values[0]?.toDouble())?.div(progressBarMax))?.times(100)
        downloadHint?.text="下载中,进度${df.format(hintContent)}%"
4.注意一些非空判断

这里是手动绑定view,注意非空判断,还有进度数值等其他地方,就不一一举例了

上一篇下一篇

猜你喜欢

热点阅读