Android线程模型
Android中的线程模型
这篇文章将讨论Android应用程序中使用的线程模型,并讨论如何确保应用程序最佳的UI呈现(通过创建工作者线程来处理耗时的操作,而不是在主线程里处理)。这篇文章还将阐述与运行在主线程中的UI组件交互的API以及创建托管的工作者线程的API。
UI线程
当应用程序启动后,系统创建了一个叫做“main”的线程。主线程,也叫UI线程,非常重要,因为它负责分发事件给构件,包括绘制事件。也是这个线程,在这里才能与Android UI工具包中的组件进行交互。
例如,当你触摸屏幕上的一个按钮时,UI线程会分发一个触摸事件给构件,然后,构件会设定自己为被按下的状态,并抛出一个显示无效的请求给事件队列。UI线程队列请求并通知构件绘制自己。
单线程模型会导致性能低下,除非你的程序很好地实现。特别是,当所有的操作都在单一的线程中进行,耗时的操作(如网络访问、数据查询)会阻塞UI。在耗时操作执行时,没有任何事件可以分发,包括绘制的事件。从用户的视觉来看,应用程序被挂起了。更糟糕的是,如果UI线程阻塞超过一定的时间(现在大约是5秒钟),系统会给用户呈现一个糟糕的“应用程序无响应”(ANR)对话框。
如果你想看这有多糟糕,你可以写一个简单的应用程序,在一个Button的OnClickListener函数中调用Thread.sleep(2000)。按钮在回到它正常状态之前,保持被按下的状态2秒钟。当这种情况发生时,用户很容易认为应用程序慢。
总之,对于应用程序UI的响应性来说,保证UI线程不被阻塞是至关重要的。如果你有耗时的操作,你应该确保在另外的线程(后台或工作者线程)中执行。
下面有一个例子,点击事件处理函数中,从网络上下载一个图片,并显示到ImageView上:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap(b);
}
}).start();
}
乍一看,这段代码能很好的解决你的问题,因为它不会阻塞UI线程。遗憾的是,它违背了UI的单线程模型:Android UI工具包不是线程安全的,必须在UI线程中进行操作。在上面的代码片段里,ImageView是在工作者线程中操作的,因此,这会引发可拍的问题。跟踪和修正这些Bug可能是困难且耗时的。
Android提供了一些方法,能在其它线程中访问UI线程。你可能对其中的一些已经很熟悉了,但这里是一份较为全面的列表:
· Activity.runOnUiThread(Runnable)
· View.post(Runnable)
· View.postDelayed(Runnable, long)
· Handler
你可以使用这些类和方法中的任一来修正上面的例子代码:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(b);
}
});
}
}).start();
}
不幸的是,这些类和方法可能会使你的代码变得更加复杂并难以阅读。特别是,当你实现一个复杂的操作,而在这个操作中,需要频繁地更新UI。
为了解决这个问题,Android 1.5和它之后的平台提供了一个通用的类——AsyncTask,其简化了长时间运行任务的创建过程,而这些任务还能做到与UI进行交互。
在Android 1.0和1.1上,也有与AsyncTask类似的东西,叫做UserTask。它提供了相同的API,而你需要做的只是拷贝其中的代码。
AsyncTask的目的是帮助你管理线程。我们之前的例子可以很容易用AsyncTask进行改写:
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
如你所见,AsyncTask必须继承使用。此外,还必须记住的是AsyncTask的实例必须在UI线程中创建并只能执行一次。你可以阅读AsyncTask的文档来全面了解如何使用,但这里,只是简要地描述它以及它的工作过程:
· 你可以使用泛型来指定参数、进度值和最终结果的类型
· doInBackground() 方法自动在工作者线程中执行
· onPreExecute(),onPostExecute()和onProgressUpdate()在主线程中调用
· doInBackground()中返回的值会发送给onPostExecute()方法
· 你可以在doInBackground()中随时调用publishProgress()来执行onProgressUpdate()
· 你可以任何时候从任何线程中取消任务
除了官方的文档,你还可以参考几个复杂例子的源代码,如Shelves(ShelvesActivity.java和AddBookActivity.java)和Photostream(LoginActivity,PhotostreamActivity.java和ViewPhotoActivity.java)。我们强烈地建议你阅读Shelves的源代码,来了解配置变更时任务的保存以及Activity销毁时如何正确地取消任务。
不管你是否使用AsyncTask,在单线程模型中始终要记住两条法则:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包
分享到:
相关推荐
用MFC實現 UI线程,在UI線程中 工作 大量計算,并 用Static 顯示进度。同时把 进度 返回 主线程,让主线程 用进度条 显示进度,达到 工作的时候 主线程 可以自由拖动。
MFC UI 线程 ,以及对UI线程发送消息。CWinThread的使用,以及宏 ON_THREAD_MESSAGE ,PostThreadMessage的使用。
dialog库,可以在任意类内调用,子线程或ui线程内均可显示
无界面的UI线程(后台)与前台UI界面线程进行交换数据。也可以理解为能够响应消息的工作线程。此类程序结构可以在博弈类软件中使用,将下棋电脑封装成独立的对象,结构清晰。
C# 跨线程访问UI线程控件 在C#中,由于使用线程和调用UI的线程属于两个不同的线程,如果在线程中直接设置UI元素的属性,此时就会出现跨线程错误。 下面介绍两种解决方案 第一种:使用控件自带的Invoke或者...
ndroid异步处理一:使用Thread+Handler实现非UI线程更新UI界面
MFC创建UI线程,具体可参看博客: http://blog.csdn.net/u010839382/article/details/52983902
Thread 达到跨线程更新UI 虽然使用Dispatcher.Invoke 和模拟winform 里面的DoEvent 但是运行中关闭还是会有异常,而且耗资源高; 第二种 : DispatcherTimer 失败:UI还是会卡顿; 第三种 : Timer 建议使用、资源...
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面。
010_android 之UI线程阻塞及其优化视频教材,讲解的比较详细,有兴趣的可以学习下哦。
WinForm后台线程与UI线程通讯 实现进度条变化
OkHttpDemo 对OkHttp进行简单的封装,基本实现简单的网络post请求,进行Gson处理,直接返回Bean。文件上传支持多文件和参数一起提交,还有文件下载,都支持进度在UI线程返回
创建worker线程 线程互斥 临界 信号 事件 UI线程等各种线程例子
网上关于工作线程和UI线程的理解趋于理论。制作一个小的实例帮助大家理解 不好意思,发错了资源,我再重新发一个啊!
ui界面线程的简单实现原理
C#经常会遇到UI线程被占用导致的界面卡顿,控件反应缓慢,局部停顿导致全界面停顿,这都是因为单一UI线程导致的,现在使用C#自动生成winform都是单一UI线程,想要多UI线程只能自己手动添加
安卓UI线程机制 ,在子线程中访问UI
Android ,非MainActivity类中的子线程和UI线程通讯,并且获取子线程传递的信息,更新UI界面。需要使用okHttp类库
线程机制
运用 委托和事件解决UI线程和后台线程问题,运用 委托和事件解决UI线程和后台线程问题