Android多线程分析之一:使用Thread异步下载图像
Android多线程分析之一:使用Thread异步下载图像
打算整理一下对 Android Framework 中多线程相关知识的理解,主要集中在 Framework 层的 Thread, Handler, Looper, MessageQueue, Message, AysncTask,当然不可避免地要涉及到 native 方法,因此也会分析 dalvik 中和线程以及消息处理相关的代码:如 dalvik 中的 C++ Thread 类以及 MessageQueue 类。本文将从一个使用 Thread 的简单 应用入手,引入 Thread 这个话题,接下来的几篇文章会依次介绍前面提到的那些主题。
这是一个使用 Android Thread 从网络上异步下载图片并在 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 4 5 6 7 8 | private static final String sImageUrl = "http://fashion.qqread.com/ArtImage/20110225/0083_13.jpg" ; private static final int MSG_LOAD_SUCCESS = 0 ; private static final int MSG_LOAD_FAILURE = 1 ; private Button mLoadButton; private ProgressDialog mProgressBar; private ImageView mImageView; |
然后来看控件的设置:
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 | protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i( "UI thread" , " >> onCreate()" ); mProgressBar = new ProgressDialog( this ); mProgressBar.setCancelable( true ); mProgressBar.setMessage( "Image downloading ..." ); mProgressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mProgressBar.setMax( 100 ); 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) { mProgressBar.setProgress( 0 ); mProgressBar.show(); new Thread() { @Override public void run() { Log.i( "Load thread" , " >> run()" ); Bitmap bitmap = loadImageFromUrl(sImageUrl); if (bitmap != null ) { Message msg = mHandler.obtainMessage(MSG_LOAD_SUCCESS, bitmap); mHandler.sendMessage(msg); } else { Message msg = mHandler.obtainMessage(MSG_LOAD_FAILURE, null ); mHandler.sendMessage(msg); } } }.start(); } }); } |
loadImageFromUrl 是一个从网络下载 Bitmap 的 static 函数:
1 2 3 4 5 6 7 8 9 10 11 12 | static Bitmap loadImageFromUrl(String uil) { Bitmap bitmap = null ; try { InputStream in = new java.net.URL(sImageUrl).openStream(); bitmap = BitmapFactory.decodeStream(in); in.close(); } catch (Exception e) { e.printStackTrace(); } return bitmap; } |
mHandler 是主线程也就是 UI 线程中处理自定义消息的 Handler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | private Handler mHandler= new Handler(){ @Override public void handleMessage(Message msg) { Log.i( "UI thread" , " >> handleMessage()" ); switch (msg.what){ case MSG_LOAD_SUCCESS: Bitmap bitmap = (Bitmap) msg.obj; mImageView.setImageBitmap(bitmap); mProgressBar.setProgress( 100 ); mProgressBar.setMessage( "Image downloading success!" ); mProgressBar.dismiss(); break ; case MSG_LOAD_FAILURE: mProgressBar.setMessage( "Image downloading failure!" ); mProgressBar.dismiss(); break ; } } }; |
纵观上面的代码,当点击 load 按钮时,会创建一个匿名 Thread,并调用其 start() 启动运行线程,在这个线程中进行图像下载并解码成 Bitmap,然后通过 Handler 向 UI 线程发送消息以通知下载结果。这都是在匿名 Thead 中处理的。主线程也就是 UI 线程收到消息之后,会分发给 Handler,在它的 handleMessage 方法中根据消息 id 来处理下载结果,要么成功要么失败,并相应地更新 UI。运行该示例,你可以从 logcat 中看到 UI thread 和 Load thread 的线程id 是不同的,因为它们是两个独立的线程。
在匿名线程下载完毕之后,为什么不直接在这个线程的 run() 中更新 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 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架