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仅仅是使你能够更容易地遵守这两条准则。

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

时间: 2024-10-25 16:31:29

Android 单线程模型详解及实例的相关文章

Android 混淆代码详解及实例

  Android 混淆代码详解及实例         为了防止自己的劳动成果被别人窃取,混淆代码能有效防止被反编译,下面来总结以下混淆代码的步骤: 1. 大家也许都注意到新建一个工程会看到项目下边有这样proguard-project.txt一个文件,这个对混淆代码很重要,如果你不小心删掉了,没关系,从其他地方拷贝一个过来 2. 最重要的就是在proguard-project.txt添加混淆的申明了: a. 把所有你的jar包都申明进来,例如: -libraryjars libs/apns_1

Android 文件选择器详解及实例代码_Android

     本文给大家讲解下Android文件选择器的使用.实际上就是获取用户在SD卡中选择的文件或文件夹的路径,这很像C#中的OpenFileDialog控件.        此实例的实现过程很简单,这样可以让大家快速的熟悉Android文件选择器,提高开发效率.        网上曾经见到过一个关于文件选择器的实例,很多人都看过,本实例是根据它修改而成的,但更容易理解,效率也更高,另外,本实例有自己的特点:        1.监听了用户按下Back键的事件,使其返回上一层目录.       

Android 混淆代码详解及实例_Android

为了防止自己的劳动成果被别人窃取,混淆代码能有效防止被反编译,下面来总结以下混淆代码的步骤: 1. 大家也许都注意到新建一个工程会看到项目下边有这样proguard-project.txt一个文件,这个对混淆代码很重要,如果你不小心删掉了,没关系,从其他地方拷贝一个过来 2. 最重要的就是在proguard-project.txt添加混淆的申明了:  a. 把所有你的jar包都申明进来,例如:  -libraryjars libs/apns_1.0.6.jar  -libraryjars lib

Android WebView的详解及实例

Android WebView的详解 Android WebView在android平台上是一个特殊的View, 他能用来显示网页,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器. 在Android手机中内置了一款高性能webkit内核浏览器,在SDK中封装为一个叫做WebView组件.WebKit是Mac OS X v10.3及以上版本所包含的软件框 架(对v10.2.7及以上版本也可通过软件更新获取). 同时,WebKit也是Mac OS X的Safari网页浏览

Android GPS定位详解及实例代码_Android

      GPS定位是智能手机上一个比较有意思的功能,LBS等服务都有效的利用了GPS定位功能.本文就跟大家分享下Android开发中的GPS定位知识.        一.Android基础知识准备        1.Activity类        每一种移动开发环境都有自己的基类.如J2ME应用程序的基类是midlets,BREW的基类是applets,而Android程序的基类是Activity.这个activity为我们提供了对移动操作系统的基本功能和事件的访问.这个类包含了基本的构造

Android语音识别技术详解及实例代码_Android

   今天从网上找了个例子实现了语音识别,个人感觉挺好玩的,就把代码贴出来与大家分享下:          Android中主要通过RecognizerIntent来实现语音识别,其实代码比较简单,但是如果找不到设置,就会抛出异常ActivityNotFoundException,所以我们需要捕捉这个异常.而且语音识别在模拟器上是无法测试的,因为语音识别是访问google云端数据,所以如果手机的网络没有开启,就无法实现识别声音的!一定要开启手机的网络,如果手机不存在语音识别功能的话,也是无法启用

Android ListView position详解及实例代码_Android

我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener.对OnItemClickListener的position和id参数,我相信有些人在这上面走了些弯路.     在使用listview的时候,我们经常会在listview的监听事件中,例如OnItemClickListener(onItemClick)中,或listview的adapter中(getView.getItem.ge

android 通知Notification详解及实例代码

android Notification实例详解 1.使用Builder模式来创建 2.必须要设置一个smallIcon,还可以设置setTicker 3.可以设置 setContentTitle,setContentInfo,setContentText,setWhen 4.可以设置setDefaults(闪屏,声音,震动),通过Notification设置flags(能不能被清除) 5.发送需要获取一个NotificationManager(getSystemService来获取);noti

[Android] IntentService使用详解和实例介绍

IntentService定义 IntentService继承与Service,用来处理异步请求.客户端可以通过startService(Intent)方法传递请求给IntentService.IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来依次处理所有Intent请求对象所对应的任务.     这样以免事务处理阻塞主线程(ANR).执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则**自动停止**Serv