热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android单线程模型详解及实例

这篇文章主要介绍了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仅仅是使你能够更容易地遵守这两条准则。

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


推荐阅读
  • 本文详细介绍了MooseFS中的副本管理(Goal)以及文件回收机制。副本管理允许用户设定文件的复制份数,确保数据的安全性和可用性;而文件回收机制则提供了在误删除文件后的恢复途径,通过设置合理的隔离时间,保护重要数据。 ... [详细]
  • 本文深入探讨了Redis中的两种主要持久化方式——RDB(Redis Database)和AOF(Append Only File),并详细解析了两者的实现机制、优缺点以及在实际应用中的选择策略。 ... [详细]
  • Java面向对象编程深入解析
    本文详细探讨了Java中的关键字static、单例模式、main()方法、代码块、final关键字、抽象类与方法、模板方法设计模式、接口、内部类等内容,旨在帮助读者深入理解和掌握Java面向对象编程的核心概念。 ... [详细]
  • NFC OMA 接口访问优化
    本文探讨了NFC设备中OMA接口的访问方式,特别是针对IC制造商提供的NFC swp-sim访问与NFC服务提供商对eSe(嵌入式安全元件)访问的不同处理方法。文中提出了几种解决方案以解决由此产生的双SmartcardService运行问题。 ... [详细]
  • 一面问题:MySQLRedisKafka线程算法mysql知道哪些存储引擎,它们的区别mysql索引在什么情况下会失效mysql在项目中的优化场景&# ... [详细]
  • Java集合框架源码解读(1)——ArrayList、LinkedList和Vector
    java.util.List接口是JavaCollectionsFramework的一个重要组成部分,List接口的架构图如下:本文将通过剖析List接 ... [详细]
  • 本文继续探讨 Redis 分布式锁的高级特性,重点分析超时问题和可重入性的实现,以及如何通过不同的策略处理锁冲突。 ... [详细]
  • 本文详细介绍了SYNTEC系统的参数调整方法,包括参数修改、软限位检查、机床原点设定、刀库调整、四轴控制、机床刚性和反向间隙调整,以及气压和润滑油参数设置等关键步骤。 ... [详细]
  • 2017成都物流技术创新峰会:深入探讨区块链应用
    2017年3月30日,第二届物流技术大会在成都成功举办,会上特别举办了关于区块链技术及其在物流行业应用的专题讨论,深入分析了区块链技术的发展历程、现状及未来趋势。 ... [详细]
  • 本文详细介绍如何在Windows 10教育版中关闭防火墙,包括具体的操作步骤及注意事项。 ... [详细]
  • 本文详细介绍了CAS(Compare and Swap)与AQS(AbstractQueuedSynchronizer),其中CAS是一种用于多线程环境中避免因锁机制导致性能下降的硬件层面原子操作,而AQS则是Java并发包中提供的一种通过FIFO队列实现的底层同步框架。 ... [详细]
  • 深入解析数据库连接池的类型及参数配置
    本文详细介绍了数据库连接池的三大类型及其各自的特性,并深入探讨了连接池的关键参数配置,帮助开发者更好地理解和利用数据库连接池技术。 ... [详细]
  • 深入理解Java中的阻塞队列BlockingQueue
    本文详细介绍了Java并发编程中的重要组件——阻塞队列(BlockingQueue),探讨了其工作原理、主要特性及常见用法,特别是在处理高并发场景下的应用。 ... [详细]
  • 获取数组中最小的K个数
    本文提供了一种使用Java优先队列(即堆)实现的方法,用于从给定数组中找出最小的K个元素。通过构建一个最大堆来维护这K个最小值,最终返回这些数值。 ... [详细]
  • 本文详细介绍了如何在Apache Shiro框架中实现对并发登录人数的限制,包括配置和自定义过滤器的具体步骤。 ... [详细]
author-avatar
甘民涵
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有