Android多线程分析之五:使用AsyncTask异步下载图像
Android多线程分析之五:使用AsyncTask异步下载图像
在本系列文章的第一篇《Android多线程分析之一:使用Thread异步下载图像》中,曾演示了如何使用 Thread 来完成异步任务。Android 为了简化在 UI 线程中完成异步任务(毕竟 UI 线程是 app 最重要的线程),实现了一个名为 AysncTask 的模板类。使用 AysncTask 能够在异步任务进行的同时,将任务进度状态反馈给 UI 线程(如让 UI 线程更新进度条)。正是由于它与 UI 线程紧密相关,使用的时候要就有一些限制,AysncTask 必须在 UI 线程中创建,并在 UI 线程中启动(通过调用其 execute() 方法);此外,AysncTask 设计的目的是用于一些耗时较短的任务,如果是耗时较长的任务不推荐使用 AysncTask。
可以用简化记忆 “三参数,四步骤” 来学习 AysncTask。 即带有三个模板参数 <Params, Progress, Result>,四个处理步骤:onPreExecute,doInBackground,onProgressUpdate,onPostExecute。
三参数:
Params 是异步任务所需的参数类型,也即 doInBackground(Params... params) 方法的参数类型;Progress 是指进度的参数类型,也即 onProgressUpdate(Progress... values) 方法的参数类型;
Result 是指任务完成返回的参数类型,也即 onPostExecute(Result result) 或 onCancelled(Result result) 方法的参数类型。
如果某一个参数类型没有意义或没有被用到,传递 void 即可。
四步骤:
protected void onPreExecute():在 UI 线程中运行,在异步任务开始之前被执行,以便 UI 线程完成一些初始化动作,如将进度条清零;
protected abstract Result doInBackground(Params... params):在后台线程中运行,这是完成异步任务的地方,它是抽象接口,子类必须提供实现;
protected void onProgressUpdate(Progress... values):在 UI 线程中运行,在异步任务执行的过程中可以通过调用 void publishProgress(Progress... values) 方法通知 UI 线程在 onProgressUpdate 方法内更新进度状态;
protected void onPostExecute(Result result):在 UI 线程中运行,当异步任务完成之后被执行,以便 UI 线程更新任务完成状态。
AysncTask 支持取消异步任务,当异步任务被取消之后,上面的步骤四就不会被执行了,取而代之将执行 onCancelled(Result result),以便 UI 线程更新任务被取消之后的状态。谨记:上面提到的这些方法都是回调函数,不需要用户手动去调用。
以前的 AysncTask 是基于单一后台线程实现的,而从 Android 3.0 起 AysncTask 是基于 Android 的并发库(java.util.concurrent)实现的,本文中不会展开讨论其具体实现,只是演示如何使用 AysncTask。
使用示例:
有了前面的轮廓介绍,再来使用 AysncTask 是非常容易的,下面的例子与 《使用Thread异步下载图像》中的例子非常相似,只不过是使用 AysncTask 来完成异步任务罢了。
这是一个使用 AysncTask 从网络上异步下载图片并在 ImageView 中显示的的简单示例。因为需要访问网络,所以要在 manifest.xml 中添加网络访问权限:
1 2 | < uses-permission android:name="android.permission.INTERNET"> </ uses-permission > |
布局文件很简单,一个 Button,一个 ImageView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dip" > < Button android:id="@+id/LoadButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Load"> </ Button > < ImageView android:id="@+id/ImageVivew" android:layout_width="match_parent" android:layout_height="400dip" android:scaleType="centerInside" android:padding="2dp"> </ ImageView > </ LinearLayout > |
接下来看代码:
首先来看定义:图片的 url 路径,两个消息值以及一些控件:
1 2 3 | private static final String sImageUrl = "http://fashion.qqread.com/ArtImage/20110225/0083_13.jpg" ; private Button mLoadButton; private ImageView mImageView; |
然后来看控件的设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i( "UI thread" , " >> onCreate()" ); mImageView = (ImageView) this .findViewById(R.id.ImageVivew); mLoadButton = (Button) this .findViewById(R.id.LoadButton); mLoadButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { LoadImageTask task = new LoadImageTask(v.getContext()); task.execute(sImageUrl); } }); } |
LoadImageTask 继承自 AysncTask,由这个类去完成异步图片下载任务,并相应地更新 UI 状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | class LoadImageTask extends AsyncTask<String, Integer, Bitmap> { private ProgressDialog mProgressBar; LoadImageTask(Context context) { mProgressBar = new ProgressDialog(context); mProgressBar.setCancelable( true ); mProgressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mProgressBar.setMax( 100 ); } @Override protected Bitmap doInBackground(String... params) { Log.i( "Load thread" , " >> doInBackground()" ); Bitmap bitmap = null ; try { publishProgress( 10 ); Thread.sleep( 1000 ); InputStream in = new java.net.URL(sImageUrl).openStream(); publishProgress( 60 ); Thread.sleep( 1000 ); bitmap = BitmapFactory.decodeStream(in); in.close(); } catch (Exception e) { e.printStackTrace(); } publishProgress( 100 ); return bitmap; } @Override protected void onCancelled() { super .onCancelled(); } @Override protected void onPreExecute() { mProgressBar.setProgress( 0 ); mProgressBar.setMessage( "Image downloading ... %0" ); mProgressBar.show(); Log.i( "UI thread" , " >> onPreExecute()" ); } @Override protected void onPostExecute(Bitmap result) { Log.i( "UI thread" , " >> onPostExecute()" ); if (result != null ) { mProgressBar.setMessage( "Image downloading success!" ); mImageView.setImageBitmap(result); } else { mProgressBar.setMessage( "Image downloading failure!" ); } mProgressBar.dismiss(); } @Override protected void onProgressUpdate(Integer... values) { Log.i( "UI thread" , " >> onProgressUpdate() %" + values[ 0 ]); mProgressBar.setMessage( "Image downloading ... %" + values[ 0 ]); mProgressBar.setProgress(values[ 0 ]); } }; |
在 LoadImageTask 中,前面提到的四个步骤都涉及到了:
首先在任务开始之前在 onPreExecute() 方法中设置进度条的初始状态(UI线程);然后在下载线程中执行 doInBackground() 以完成下载任务,并在其中调用 publishProgress() 来通知 UI 线程更新进度状态;UI 线程在 onProgressUpdate() 中得知进度,并更新进度条(UI线程);最后下载任务完成,UI 线程在 onPostExecute() 中得知下载好的图像,并更新UI显示该图像(UI线程)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架