如何测试@c async task 异步异步任务

如何测试@Async异步任务_百度知道C#执行异步操作的几种方式比较和总结
之前写程序的时候在遇到一些比较花时间的操作例如HTTP请求时,总是会new一个Thread处理。对XxxxxAsync()之类的方法也没去了解过,倒也没遇到什么大问题。最近因为需求要求用DevExpress写界面,跑起来后发现比Native控件效率差好多。这才想到之前看到的&金科玉律&:不要在UI线程上执行界面无关的操作,因此集中看了下C#的异步操作,分享一下自己的比较和总结。
0x01 测试方法
IDE:VS2015 Community
.NET版本:4.5
使用函数随机休眠100到500毫秒来模拟耗时任务,并返回任务花费的时间,在UI线程上调用这个方法会造成阻塞,导致UI假死,因此需要通过异步方式执行这个任务,并在信息输出区域显示花费的时间。
主界面中通过各种不同按钮测试不同类型的异步操作
0x02 使用Thread进行异步操作
使用ThreadPool进行异步操作的方法如下所示,需要注意的就是IsBackground默认为false,也就是该线程对调用它的线程不产生依赖,当调用线程退出时该线程也不会结束。因此需要将IsBackground设置为true以指明该线程是后台线程,这样当主线程退出时该线程也会结束。另外跨线程操作UI还是要借助Dispatcher.BeginInvoke(),如果需要阻塞UI线程可以使用Dispatcher.Invoke()。
0x03 使用ThreadPool进行异步操作
ThreadPool(线程池)的出现主要就是为了提高线程的复用(类似的还有访问数据库的连接池)。线程的创建是开销比较大的行为,为了达到较好的交互体验,开发中可能会大量使用异步操作,特别是需要频繁进行大量的短时间的异步操作时,频繁创建和销毁线程会在造成很多资源的浪费。而通过在线程池中存放一些线程,当需要新建线程执行操作时就从线程池中取出一个已经存在的空闲线程使用,如果此时没有空闲线程,且线程池中的线程数未达到线程池上限,则新建一个线程,使用完成后再放回到线程池中。这样可以极大程度上省去线程创建的开销。线程池中线程的最小和最大数都可以指定,不过多数情况下无需指定,CLR有一套管理线程池的策略。ThreadPool的使用非常简单,代码如下所示。跨线程操作UI仍需借助Dispatcher。
0x04 使用Task进行异步操作
Task进行异步操作时也是从线程池中获取线程进行操作,不过支持的操作更加丰富一些。而且Task&T&可以支持返回值,通过Task的ContinueWith()可以在Task执行结束后将返回值传入以进行操作,但在ContinueWith中跨线程操作UI仍需借助Dispatcher。另外Task也可以直接使用静态方法Task.Run&T&()执行异步操作。
0x05 使用async/await进行异步操作
这个是C#5中的新特性,当遇到await时,会从线程池中取出一个线程异步执行await等待的操作,然后方法立即返回。等异步操作结束后回到await所在的地方接着往后执行。await需要等待async Task&T&类型的函数。详细的使用方法可参考相关资料,测试代码如下所示。异步结束后的会返回到调用线程,所以修改UI不需要Dispatcher。
也可以把TestTask包装成async方法,这样就可以使用上图中注释掉的两行代码进行处理。包装后的异步方法如下所示:
async/await也是从线程池中取线程,可实现线程复用,而且代码简洁容易阅读,异步操作返回后会自动返回调用线程,是执行异步操作的首选方式。而且虽然是C#5的新特性,但C#4可以通过下载升级包来支持async/await。
0x06 关于效率
以上尝试的方法除了直接使用Thread之外,其他几种都是直接或间接使用线程池来获取线程的。从理论上来分析,创建线程时要给线程分配栈空间,线程销毁时需要回收内存,创建线程也会增加CPU的工作。因此可以连续创建线程并记录消耗的时间来测试性能。测试代码如下所示:
当测试Thread时每次测试在连续创建线程时内存和CPU都会有个小突起,不过在线程结束后很快就会降下去,在我的电脑上连续创建100个线程大概花费120-130毫秒。如图所示:
测试结果:
使用基于线程池的方法创建线程时,有时第一次会稍慢一些,应该是线程池内线程不足,时间开销在0-15毫秒,第一次创建内存也会上升。后面再测试时时间开销为0毫秒,内存表现也很平稳,CPU开销分布比较平均。测试结果如图所示:
在执行异步操作时应使用基于线程池的操作,从代码的简洁程度和可读性上优先使用async/await方式。对于较老的.NET版本可以使用Task或ThreadPool。符合以下情况的可以使用Thread:
1、线程创建后需要持续工作到主线程退出的。这种情况下就算使用线程池线程也不会归还,实现不了复用,可以使用Thread。
2、线程在主线程退出后仍需要执行的,这种情况使用线程池线程无法满足需求,需要使用Thread并制定IsBackground为false(默认)。
0x08 相关下载
测试程序代码在:/durow/TestArea/tree/master/AsyncTest/AsyncTest
阅读(...) 评论()异步任务神器 Celery 简明笔记 - 简书
下载简书移动应用
写了138741字,被412人关注,获得了442个喜欢
异步任务神器 Celery 简明笔记
异步任务是web开发中一个很常见的方法。对于一些耗时耗资源的操作,往往从主应用中隔离,通过异步的方式执行。简而言之,做一个注册的功能,在用户使用邮箱注册成功之后,需要给该邮箱发送一封激活邮件。如果直接放在应用中,则调用发邮件的过程会遇到网络IO的阻塞,比好优雅的方式则是使用异步任务,应用在业务逻辑中触发一个异步任务。
实现异步任务的工具有很多,其原理都是使用一个任务队列,比如使用。
除了redis,还可以使用另外一个神器---Celery。Celery是一个异步任务的调度工具。它是Python写的库,但是它实现的通讯协议也可以使用ruby,php,javascript等调用。异步任务除了消息队列的后台执行的方式,还是一种则是跟进时间的计划任务。下面将会介绍如何使用celery实现这两种需求。
Celry broker 和 backend
最早学习celery的时候,冒出了一个rabbitmq,又冒出一个redis。当时一头雾水。实际上这正是celery的设计奥妙。简单来说,rabbitmq是一个采用Erlang写的强大的消息队列工具。在celery中可以扮演broker的角色。那么什么是broker?
broker是一个消息传输的中间件,可以理解为一个邮箱。每当应用程序调用celery的异步任务的时候,会向broker传递消息,而后celery的worker将会取到消息,进行对于的程序执行。好吧,这个邮箱可以看成是一个消息队列。那么什么又是backend,通常程序发送的消息,发完就完了,可能都不知道对方时候接受了。为此,celery实现了一个backend,用于存储这些消息以及celery执行的一些消息和结果。对于 brokers,官方推荐是rabbitmq和redis,至于backend,就是数据库啦。为了简单起见,我们都用redis。
Getting Starting
使用celery包含三个方面,其一是定义任务函数,其二是运行celery服务,最后是客户应用程序的调用。
创建一个文件 tasks.py
输入下列代码:
from celery import Celery
brokers = 'redis://127.0.0.1:6379/5'
backend = 'redis://127.0.0.1:6379/6'
app = Celery('tasks', broker=broker, backend=backend)
def add(x, y):
return x + y
上述代码导入了celery,然后创建了celery实例app,实力话的过程中,指定了任务名tasks(和文件名一致),传入了broker和backend。然后创建了一个任务函数add。
下面就启动celery服务
在当前命令行终端运行:
celery -A tasks worker
--loglevel=info
此时会看见一对输出。包括注册的任务啦。
下面客户端程序如何调用呢?打开一个命令行,进入Python环境
In [0]:from tasks import add
In [1]: r = add.delay(2, 2)
In [2]: add.delay(2, 2)
Out[2]: &AsyncResult: 6fdb0629-4beb-4eb7-be47-f22be1395e1d&
In [3]: r = add.delay(3, 3)
In [4]: r.re
In [4]: r.ready()
Out[4]: True
In [6]: r.result
In [7]: r.get()
在celery命令行可以看见celery执行的日志:
[ 21:37:06,086: INFO/MainProcess] Task proj.tasks.add[76beb980-0f55-4629-a4fb-4a] succeeded in 0.86s: 6
打开 backend的redis,也可以看见celery执行的信息。
现在时在python环境中调用的add函数,实际上通常在应用程序中调用这个方法。需要注意,如果把返回值赋值给一个变量,那么原来的应用程序也会被阻塞,需要等待异步任务返回的结果。因此,实际使用中,不需要把结果赋值。
上述的使用是简单的配置,下面介绍一个更健壮的方式来使用celery。首先创建一个python包,celery服务,姑且命名为proj。目录文件如下:
├── __init__.py
├── celery.py
# 创建 celery 实例
├── config.py
# 配置文件
└── tasks.py
# 任务函数
首先是 celery.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from celery import Celery
app = Celery('proj', include=['proj.tasks'])
app.config_from_object('proj.config')
if __name__ == '__main__':
app.start()
这一次创建 app,并没有直接指定 broker 和 backend。而是在配置文件中。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/5'
BROKER_URL = 'redis://127.0.0.1:6379/6'
剩下的就是tasks.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from proj.celery import app
def add(x, y):
return x + y
使用方法也很简单,在proj的同一级目录执行celery:
celery -A proj worker -l info
现在使用任务也很简单,直接在客户端代码调用 proj.tasks 里的函数即可。
一种常见的需求是每隔一段时间执行一个任务。配置如下
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/5'
BROKER_URL = 'redis://127.0.0.1:6379/6'
CELERY_TIMEZONE = 'Asia/Shanghai'
from datetime import timedelta
CELERYBEAT_SCHEDULE = {
'add-every-30-seconds': {
'task': 'proj.tasks.add',
'schedule': timedelta(seconds=30),
'args': (16, 16)
注意配置文件需要指定时区。这段代码表示每隔30秒执行 add 函数。
一旦使用了 scheduler, 启动 celery需要加上-B 参数
celery -A proj worker -B -l info
计划任务当然也可以用crontab实现,celery也有crontab模式。修改 config.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/5'
BROKER_URL = 'redis://127.0.0.1:6379/6'
CELERY_TIMEZONE = 'Asia/Shanghai'
from celery.schedules import crontab
CELERYBEAT_SCHEDULE = {
# Executes every Monday morning at 7:30 A.M
'add-every-monday-morning': {
'task': 'tasks.add',
'schedule': crontab(hour=7, minute=30, day_of_week=1),
'args': (16, 16),
总而言之,scheduler的切分度更细,可以精确到秒。crontab模式就不用说了。当然celery还有更高级的用法,比如多个机器使用,启用多个worker并发处理等。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
欢迎大家踊跃投稿
· 2460人关注
生活在程序员圈中,确是伪程序员!
· 316人关注
· 45人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:& 本篇体验如何等待所有异步任务完成、等待任意一个异步任务完成,以及异步任务完成时的处理。 & 等待一组任务的完成使用Task.WhenAll方法。 &Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
Task task3 = Task.Delay(TimeSpan.FromSeconds(3));
await Task.WhenAll(task1, task2, task3);
如果所有任务的结果类型相同且全部完成,Task.WhenAll返回每个任务执行结果的数组。
&Task task1 = Task.FromResult(1);
Task task2 = Task.FromResult(2);
Task task3 = Task.FromResult(3);
int[] results = await Task.WhenAll(task1, task2, task3);
foreach(var item in results)
Console.WriteLine(item);
举个例子,提供一个url的集合,要求根据这个url集合去远程下载对应的内容,写一个方法。
&static async Task&string& DownloadAllAsync(IEnumerable&string& urls)
var httpClient = new HttpClient();
//定义每一个ulr的使用方法
var downloads = urls.Select(url =& httpClient.GetStringAsync(url));
//下载真正开始
Task&string&[] downloadTasks = downloads.ToArray();
//异步等待
string[] hmtls = await Task.WhenAll(downloadTasks);
return string.Concat(htmls);
如果在等待所有任务完成的过程中有异常发生怎么办呢?
如果想在等待过程中捕获异常,那么应该把WhenAll方法放在try语句块中;如果想在所有任务完成后捕获异常,那就应该把WhenAll方法返回的Task类型放在try语句块中。
先模拟两个异步异常。
&static async Task ThrowNotImplementedExceptionAsync()
throw new NotImplementedException();
static async Task ThrowInvalidOperationExceptionAsync()
throw new InvalidOperationException();
首先来看等待结果出来时的异常处理。
&stati async Task ObserveOneExceptionAsync()
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrwoInvalidOperationExceptionAsync();
await Task.WhenAll(task1, ask2);
cach(Exception ex)
再来看等所有结果出来后的异常处理。
&static async Task ObserveAllExceptionAsync()
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrwoInvalidOperationExceptionAsync();
Task allTasks = Task.WhenAll(task1, task2);
await allT
catch(Eexception ex)
等待任意一个任务的完成使用WhenAny方法。
比如有2个任务,通过2个url获取异步远程内容。
&private static async Task&int& DownloadAsync(string url1, url2)
var httpClient = new HttpClient();
Task&byte[]& download1 = httpClient.GetByteArrayAsync(url1);
Task&byte[]& download2 = httpClient.GetByteArrayAsync(url2);
//等待任意一个任务完成
Task&byte[]& completedTask = await Task.WhenAny(download1, download2);
byte[] data = await completedT
return data.L
任务完成时如何处理呢?
思路有2个,一个是根据我们安排的顺序出结果,还有一个是根据任务本身出结果的先后顺序自然输出结果。
首先来一个异步方法。
&static async Task&int& DelayAsync(int val)
await Task.Delay(TimeSpan.FromSeconds(val));
再写一个手动部署任务顺序的方法。
&static async Task ProcessTasksAsync()
//创建任务队列
Task&int& task1 = DelayAsync(2);
Task&int& task2 = DelayAsync(3);
Task&int& task3 = DelayAsync(1);
//手动安排任务的顺序
var tasks = new[]{task1, task2, task3};
//按顺序遍历任务列表,逐一输出结果
foreach(var task in tasks)
var result =
Console.Write(result);
输出结果为231,是根据我们手动安排任务的顺序输出结果的。
如果我们想输出123呢?即按照任务的不同让结果自然发生。
思路是:以异步的方式处理输出结果。
可以写一个针对每个任务的异步方法。
&static async Task AwaitAndProessAync(Task&int& task)
var result =
Console.Write(result);
现在修改ProcessTasksAsync方法如下:
&static async Task ProcessTasksAsync()
//创建任务队列
Task&int& task1 = DelayAsync(2);
Task&int& task2 = DelayAsync(3);
Task&int& task3 = DelayAsync(1);
//手动安排任务的顺序
var tasks = new[]{task1, task2, task3};
var processingTasks = (from t in tasks
select AwaitAndProessAync(t)).ToArray();
await Task.WhenAll(processingTasks);
当然,也可以这样修改ProcessTasksAsync方法。
static async Task ProcessTasksAsync()
//创建任务队列
Task&int& task1 = DelayAsync(2);
Task&int& task2 = DelayAsync(3);
Task&int& task3 = DelayAsync(1);
//手动安排任务的顺序
var tasks = new[]{task1, task2, task3};
var processingTasks = tasks.Select( async t =& {
var result =
Console.Write(result);
}).ToArray();
await Task.WhenAll(processingTasks);
参考资料:C#并发编程经典实例
阅读(...) 评论()
我的公众号:新语新世界,欢迎关注。android 杂类(26)
本人工作已经一年多了,具体点说已经一年多3个月了,实习的早,过早的受到了社会的蹂躏。今年6月多份毕业了,然后就到了一个比较大的公司,具体名字就不说了,就是妹子超级超级多。。。。在学校学的是游戏,cx之类的,但是鬼使神差的毕业后跟着同学就干上了应用,多亏了我的第一个老板--李金波,超级感谢~
好了,废话不多说了,接下来就开启Android多线程与异步任务的学习吧,由于内容有点多,分几篇博客来和大家扯淡~
学习Android当然就避免不了学习java,java中也有多线程还有线程之间的同步问题等等~设计到的知识也是相当庞大的,建议小伙伴们看看传智播客张孝祥的java多线程同步那几个视频,讲的是相当牛逼~,可惜祥哥已经走了,为啥干程序员的都活不长!时间充足的话,我后面也会分享线程同步的心得,当然得感谢祥哥,祝愿祥哥一切安好~
我们将分5部分来讲解Android的多线程与异步任务
1:java多线程基础
(1):ThreadPoolExecutor
(2):ScheduleExecutor
(3):线程同步(synchronized,lock,semaphore)
2:在Android中使用的线程
3:在用Handler异步时不可或缺的组件
4:使用AsyncTask快速实现异步任务
5:检测程序中是否有需要使用多线程的地方
其中第一部分,在最后面的篇章中我争取会给大家讲解java中的多线程的使用和线程之间的同步的问题,让各位大侠见笑了,因为吃的饭还没有你们吃的盐多~、
本篇博客我们着重讲解第二个部分----在Android中使用的线程
在引出这个话题的时候,我们要想到三个问题:
1:为何使用多线程?
2:在Android中如何使用多线程或者异步操作?
3:多线程和界面之间的交互
首先来看第一个问题--为何使用多线程?我们使用多线程往往是涉及到一些复杂的IO操作(文件操作,网络操作,数据库操作),复杂运算,定时操作。想必大家都碰到过应用程序弹出“无响应”的对话框,那是因为在Android主线程中进行了超时的操作,过多的阻塞了主线程就会导致应用出现ANR的Waring~
下面给大家举一个为什么要使用多线程的小例子:假如我们现在有一个需求,需要点击按钮的时候出现一个Dialog(对话框),这个对话框显示5秒钟后就自动消失,我们怎么实现呢?如果我们在按钮的点击事件中先调用Dialog.show();然后调用Thread.sleep(5000);最后调用Dialog.dismiss();是不是就可以了呢?答案是否定的!因为你会发现这样的现象,我们好像从始至终就没有看见Dialog~,这是为什么呢?
Android中的绘制都是线程不安全的,很多人就会疑问线程不安全的那还不乱了套?这个不用担心了,因为Android只有一个绘制线程,那就是UI线程(主线程)。只存在一个线程就不存在什么线程安全不安全的问题了~回到我们的问题,由于Android中控件的绘制是在UI线程中,当我们调用Dialog.show()这句代码的时候,距离Dialog绘制出来还有一定的时间间隔,而此时我们调用了Thread.sleep(5000);让主线程休眠了5秒钟,哥啊,主线程都休息了,还绘制个毛线啊!当然就看不见了~所以我们需要开启一个线程去让Dialog经过5秒钟去消失掉~(当然也有其他的方法~)
明白了为何使用多线程的问题,我们接下来看一下使用多线程或者异步操作的几种方式
2:Handler
3:AsyncTask
4:AsyncTaskLoader 这个类是API Level 11 中有的,也就是3.0(不过V4包中有~)
值得一提的是AsyncTaskLoader的子类CursorLoader这个用来关联数据库来进行操作也是不错的~具体的会再讲解
相信大家用的最多的就是Hander和AsyncTaskLoader了,本人平时用的最多的也就是这两个~,现在的项目中涉及到了聊天才用到了CursorLoader,不过感觉还凑活~
大家不要着急~,Handler和AsyncTask后面会重点剖析~第一篇就给大家说一个大概的轮廓,大家知道有这么几种方式就可以了,不要着急啊,么么哒~
最后说一下第三个小问题:多线程如何和界面交互呢?Android中提供的方式有:
1:Activity.runOnUIThread(Runnable);
2:View.post(Runnable); &View.postDelay(Runnable);
3:Handler
4:AsyncTask
线程和界面交互的时候我们要严格明确下面两个原则:
(1)不要阻塞UI线程
(2)不要在UI线程外更新界面
1:对于耗时操作,我们应该放到非主线程中运行,从而避免阻塞主线程~
2:为了保证良好的用户体验,建议对超过50ms的操作,都使用线程处理~
为什么超过了50ms都要放在线程中处理呢?不是导致应用程序无响应是10秒钟吗?为大家算一下1秒钟=1000ms,1000ms/50ms=20 fps ,也就是1秒钟刷新的帧数为20帧,这是人眼感觉不到很卡顿的帧数,假如低于了20帧,就会感觉滑动页面之类的操作很卡顿~所以低于50ms对于用户体验还是很重要的~
下面给大家贴出一个小例子,方便大家的理解,上代码喽~
首先贴出布局文件的代码~
&?xml version=&1.0& encoding=&utf-8&?&
&LinearLayout xmlns:android=&/apk/res/android&
android:orientation=&vertical&
android:layout_width=&fill_parent&
android:layout_height=&fill_parent&&
&Button android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:text=&从主线程加载&
android:id=&@+id/loadFromMainThread&/&
&Button android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:text=&从异步线程加载1&
android:id=&@+id/loadFromOtherThread1&/&
&Button android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:text=&从异步线程加载2&
android:id=&@+id/loadFromOtherThread2&/&
&Button android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:text=&利用Asyncthread加载图片&
android:id=&@+id/loadFromAsync&/&
&ImageView
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:id=&@+id/image&/&
&/LinearLayout&
父布局为LinearLayout,里面竖直方向摆放了4个按钮,最下面是一个ImageView,用来显示图片~
接下来就是主体代码部分
package com.example.XiaoYuAndroidThread1;
import android.app.A
import android.graphics.drawable.D
import android.os.AsyncT
import android.os.B
import android.util.L
import android.view.V
import android.widget.ImageV
import java.io.IOE
import java.net.URL;
public class MyActivity extends Activity {
private ImageV
public static final String url = &http://pic./files/pic/pic9/201501/apic9211.jpg&;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
image = (ImageView) findViewById(R.id.image);
//直接在主线程中下载图片
findViewById(R.id.loadFromMainThread).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Drawable drawable = loadImageFromNework(url);
image.setImageDrawable(drawable);
//在开启的新线程中下载图片1
findViewById(R.id.loadFromOtherThread1).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Drawable drawable = loadImageFromNework(url);
image.setImageDrawable(drawable);
}).start();
//在开启的新线程中下载图片2
findViewById(R.id.loadFromOtherThread2).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Drawable drawable = loadImageFromNework(url);
image.post(new Runnable() {
public void run() {
image.setImageDrawable(drawable);
}).start();
//利用AsyncTask去下载图片
findViewById(R.id.loadFromAsync).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new DownloadImageTask().execute(url);
private class DownloadImageTask extends AsyncTask&String,Void,Drawable&{
protected Drawable doInBackground(String... params) {
return loadImageFromNework(params[0]);
protected void onPostExecute(Drawable drawable) {
image.setImageDrawable(drawable);
//利用Drawable从网络上加载图片
private Drawable loadImageFromNework(String imageUrl) {
Drawable drawable =
drawable = Drawable.createFromStream(new URL(imageUrl).openStream(), &meinv.jpg&);
} catch (IOException e) {
Log.e(&xiaoyu&, e.getMessage());
if (drawable == null) {
Log.e(&xiaoyu&, &drawable is null&);
Log.e(&xiaoyu&, &drawable not null&);
这里要声明一下,点击第二个按钮的时候,程序会崩溃(这是正常的~),因为在UI线程外更新了ImageView,所以程序崩溃了,这是为了程序的演示效果~
第一个按钮为在主线程中加载图片,当然这张图片很小,假如图片很大的话,会造成主线程的阻塞,造成应用程序ANR~
第三个按钮的实现逻辑和第二个按钮是一样的,将加载网络数据的耗时操作放在了Thread中,但是和第二个按钮不同的是在得到drawable后,通过View的post方法将更新UI的操作放到了主线程中去更新,所以点击第三个按钮就不会导致程序的崩溃。
第四个按钮通过AsyncTask来更新UI,后面会详细剖析~
加载图片的方法也是极其的简单,通过Drawable中的createFromStream这个方法来实现,第一个参数传递一个IoStream,我们通过new Url(url).openStream();来实现,第二个参数没有必要传递,可以传递null,这里随便起了一个名字~
最后别忘记了在AndroidManifist文件中声明 访问网络的权限~
以上就是本篇的全部内容,接下来会为大家继续推出这一系列的博客~
就不给大家截图片了,我这里不是很方便~
测试代码地址:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:48049次
排名:千里之外
原创:16篇
转载:75篇
评论:22条
(1)(2)(3)(2)(5)(7)(3)(3)(16)(8)(10)(4)(5)(7)(6)(6)(3)

我要回帖

更多关于 async 循环 异步 的文章

 

随机推荐