Android使用AsyncTask完成一个简单的下载进度条
参考文章: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,注意非空判断,还有进度数值等其他地方,就不一一举例了