Android 单线程模型详解及实例

Android 单线程模型详解及实例

我们今天将会在这篇文章中为大家详细介绍有关Android单线程模型的相关内容。希望初学者们可以通过本文介绍的内容对这一概念有一个充分的认识,并从中对这一系统有一个深刻的认识。

当第一次启动一个Android程序时,Android会自动创建一个称为“main”主线程的线程。这个主线程(也称为UI线程)很重要,因为它负责把事件分派到相应的控件,其中就包括屏幕绘图事件,它同样是用户与Andriod控件交互的线程。比如,当你在屏幕上按下一个按钮后,UI线程会把这个事件分发给刚按得那个按钮,紧接着按钮设置它自身为被按下状态并向事件队列发送一个无效(invalidate)请求。UI线程会把这个请求移出事件队列并通知按钮在屏幕上重新绘制自身。

Android单线程模型会在没有考虑到它的影响的情况下引起Android应用程序性能低下,因为所有的任务都在同一个线程中执行,如果执行一些耗时的操作,如访问网络或查询数据库,会阻塞整个用户界面。当在执行一些耗时的操作的时候,不能及时地分发事件,包括用户界面重绘事件。从用户的角度来看,应用程序看上去像挂掉了。更糟糕的是,如果阻塞应用程序的时间过长(现在大概是5秒钟)Android会向用户提示一些信息,即打开一个“应用程序没有相应(application not responding)”的对话框。

如果你想知道这有多糟糕,写一个简单的含有一个按钮的程序,并为按钮注册一个单击事件,并在事件处理器中调用这样的代码Thread.sleep(2000)。在按下这个按钮这后恢复按钮的正常状态之前,它会保持按下状态大概2秒钟。如果这样的情况在你编写的应用程序中发生,用户的第一反应就是你的程序运行很慢。

现在你知道你应该避免在UI线程中执行耗时的操作,你很有可能会在后台线程或工作者线程中执行这些耗时的任务,这样做是否正确呢?让我们来看一个例子,在这个例子中按钮的单击事件从网络上下载一副图片并使用ImageView来展现这幅图片。

代码如下:

public void onClick( View v ) {
new Thread( new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap( b );
}
}).start();
}
public void onClick( View v ) {
new Thread( new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap( b );
}
}).start();
}

这段代码好像很好地解决了你遇到的问题,因为它不会阻塞UI线程。很不幸,它违背了Android单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。在这段代码片段中,在一个工作者线程中使用ImageView的方法,这回引起一些很古怪的问题。查处这个问题并修复这个bug会很困难而且也很耗时。

Andriod提供了几种在其他线程中访问UI线程的方法。或许你已经对其中的一些方式很熟悉,但下面是一个更全面的列表:

Activity.runOnUiThread( Runnable )
View.post( Runnable )
View.postDelayed( Runnable, long )
Hanlder

上面的任何一个类或方法都可以修复我们前面代码中出现的问题。

public void onClick( View v ) {
new Thread( new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post( new Runnable() {
mImageView.setImageBitmap( b );
});
}
}).start();
}
public void onClick( View v ) {
new Thread( new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post( new Runnable() {
mImageView.setImageBitmap( b );
});
}
}).start();
}

很不幸的是这些类或方法同样会使你的代码很复杂很难理解。然而当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕。为了解决这个问题,Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。

在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 {
protected Bitmap doInBackground( String... urls ) {
return loadImageFormNetwork( urls[0] );
}
protected void onPostExecute( Bitmap result ) {
mImageView.setImageBitmap( result );
}
}
public void onClick( View v ) {
new DownloadImageTask().execute
( "http://example.com/image.png" );
}
private class DownloadImageTask extends AsyncTask {
protected Bitmap doInBackground( String... urls ) {
return loadImageFormNetwork( urls[0] );
}
protected void onPostExecute( Bitmap result ) {
mImageView.setImageBitmap( result );
}
}

正如你看到的,使用AsyncTask必须要继承它。使用AsyncTask非常重要的是:AsyncTask的实例必须在UI线程中创建而且只能被使用一次。你可以使用预读AsyncTask的文档来来了解如何使用这个类,下面大概地了解一下它是如何工作的:

你可以使用泛型参数制定任务的参数、中间值(progress values)和任何的最终执行结果

doInBackground()方法会自动地在工作者线程中执行

onPreExecute()、onPostExecute()和onProgressUpdate()方法会在UI线程中被调用

doInBackground()方法的返回值会被传递给onPostExecute()方法

在doInBackground()方法中你可以调用publishProgress()方法,每一次调用都会使UI线程执行一次onProgressUpdate()方法

你可以在任何时候任何线程中取消这个任务

除了官方的文档,你可以阅读Shelves和Photostream源代码中的几个复杂的示例。我强烈地推荐阅读Shelves的源代码,它会使你知道如何在配置更改之间持久化任务以及在activity被销毁时正确的取消任务。

不管是否使用AsyncTask,始终记住以下两个关于Android单线程模型的准则:不要阻塞UI线程以及一切Android UI操作都在UI线程中执行。AsyncTask仅仅是使你能够更容易地遵守这两条准则。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

时间: 2017-04-24

Android编程中关于单线程模型的理解与分析

本文讲述了Android编程中关于单线程模型的理解与分析.分享给大家供大家参考,具体如下: 当一个Android程序启动时,Android系统会同时启动一个对应的主线程(Main Thread). 由于这个主线程(Main Thread)主要的任务就是对UI相关的事件进行处理(例如显示文本,处理点击事件,显示图片等),系统对每一个组件的调用都是从主线程中分发出去的,所以又常被称为UI线程. IMP,Android单线程模型的核心原则就是:只能在UI线程(Main Thread)中对UI进行处理.

Android编程中Tween动画和Frame动画实例分析

本文实例讲述了Android编程中Tween动画和Frame动画实现方法.分享给大家供大家参考,具体如下: Animation主要有两种动画模式:Tween动画和Frame动画 Tween动画由四种类型组成 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 res目录下新建anim创建Tween.xml <?xml version="1.0" encoding="utf-8

Android编程中activity的完整生命周期实例详解

本文实例分析了Android编程中activity的完整生命周期.分享给大家供大家参考,具体如下: android中 activity有自己的生命周期,对这些知识的学习可以帮助我们在今后写程序的时候,更好的理解其中遇到的一些错误.这篇文章很长,希望不要耽误大家的时间- 今天不会涉及太多关于activity栈的东西,主要说activity自身的生命周期 区分几个概念 1 Activity 官方解释为 "An Activity is an application component that pro

Android编程中的消息机制实例详解

本文实例讲述了Android编程中的消息机制.分享给大家供大家参考,具体如下: 在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListener { private TextView stateText; private Button btn; @Override public void onCreate(Bundle savedInstanceState)

Android编程中的四大基本组件与生命周期详解

本文实例讲述了Android编程中的四大基本组件与生命周期.分享给大家供大家参考,具体如下: Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity : 应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应. Activity之间通过Intent进行通信.在Intent 的描述结构中,有两个最

Android编程中常用适配器及自定义适配器用法实例分析

本文实例讲述了Android编程中常用适配器及自定义适配器用法.分享给大家供大家参考,具体如下: 一.适配器. 顾名思义,就是把一些数据给弄得适当,适合以便于在View上显示.可以看作是界面数据绑定的一种理解.它所操纵的数据一般都是一些比较复杂的数据,如数组,链表,数据库,集合等.适配器就像显示器,把复杂的东西按人可以接受的方式来展现. 那么适配器是怎么处理得到的数据,并把它显示出来的呢.其实很简单,说白了适配器它也是一个类,在类里面它实现了父类的这几个方法: publicint getCoun

Android编程中沉浸式状态栏的三种实现方式详解

本文实例讲述了Android编程中沉浸式状态栏的三种实现方式.分享给大家供大家参考,具体如下: 沉浸式状态栏 Google从android kitkat(Android 4.4)开始,给我们开发者提供了一套能透明的系统ui样式给状态栏和导航栏,这样的话就不用向以前那样每天面对着黑乎乎的上下两条黑栏了,还可以调成跟Activity一样的样式,形成一个完整的主题,和IOS7.0以上系统一样了. 首先看下效果 首先看下第一种方式 系统的方式沉浸式状态栏实现 步奏一 //当系统版本为4.4或者4.4以上

Android编程中activity启动时出现白屏、黑屏问题的解决方法

本文实例讲述了Android编程中activity启动时出现白屏.黑屏问题的解决方法.分享给大家供大家参考,具体如下: 默认情况下 activity 启动的时候先把屏幕刷成白色,再绘制界面,绘制界面或多或少有点延迟,这段时间中你看到的就是白屏,显然影响用户体验,怎么消除呢? 在 Activity theme 设置style 即可 <style name="AppTheme" parent="android:Theme.Light.NoTitleBar">

Android编程中聊天页面背景图片、标题栏由于键盘引起问题的解决方法

本文实例讲述了Android编程中聊天页面背景图片.标题栏由于键盘引起问题的解决方法.分享给大家供大家参考,具体如下: 在一个群里面有人问到 聊天页面由于键盘弹出来,导致自定义的标题栏不见和背景图片都变形了,然后自己也折腾了一下,在stackOverFlow上面找到了一个解决方法. 解决方法很简单: 1.在AndroidManifest.xml文件里面的Activity配置: 复制代码 代码如下: android:windowSoftInputMode="adjustResize|stateAl

Android编程中出现The connection to adb is down问题的解决方法

本文分析了Android编程中出现The connection to adb is down问题的解决方法.分享给大家供大家参考,具体如下: 1.报错: BUILD FAILED D:\workspace\ganji\build.xml:144: The following error occurred while executing this line: D:\workspace\ganji\build.xml:271: Unable to delete file D:\workspace\g