[Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
来源:互联网 发布:厦大吴春明 知乎 编辑:程序博客网 时间:2024/04/30 12:03
我们先看一个并不是异步线程加载的例子,使用 Handler+Runnable模式。
这里为何不是新开线程的原因请参看这篇文章:Android Runnable 运行在那个线程 这里的代码其实是在UI 主线程中下载图片的,而不是新开线程。
我们运行下面代码时,会发现他其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。
package
ghj1976.AndroidTest;
import
java.io.IOException;
import
java.net.URL;
import
android.app.Activity;
import
android.graphics.drawable.Drawable;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.SystemClock;
import
android.util.Log;
import
android.widget.ImageView;
public
class
MainActivity
extends
Activity {
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadImage(
"http://www.baidu.com/img/baidu_logo.gif"
, R.id.imageView1);
loadImage(<img id=
"\"aimg_NXDEd\""
onclick=
"\"zoom(this,"
this
.src,=
""
0
,=
""
0
)\
"="
" class="
\"zoom\"
" file="
\"http:
//www.chinatelecom.com.cn/images/logo_new.gif\"" onmouseover="\"img_onmouseoverfunc(this)\"" lazyloadthumb="\"1\"" border="\"0\"" alt="\"\"" src="\"http://www.chinatelecom.com.cn/images/logo_new.gif\"" lazyloaded="true">",
R.id.imageView2);
loadImage("http:
//cache.soso.com/30d/img/web/logo.gif, R.id.imageView3);
loadImage(
"http://csdnimg.cn/www/images/csdnindex_logo.gif"
,
R.id.imageView4);
loadImage(
"http://images.cnblogs.com/logo_small.gif"
,
R.id.imageView5);
}
private
Handler handler =
new
Handler();
private
void
loadImage(
final
String url,
final
int
id) {
handler.post(
new
Runnable() {
public
void
run() {
Drawable drawable =
null
;
try
{
drawable = Drawable.createFromStream(
new
URL(url).openStream(),
"image.gif"
);
}
catch
(IOException e) {
Log.d(
"test"
, e.getMessage());
}
if
(drawable ==
null
) {
Log.d(
"test"
,
"null drawable"
);
}
else
{
Log.d(
"test"
,
"not null drawable"
);
}
// 为了测试缓存而模拟的网络延时
SystemClock.sleep(
2000
);
((ImageView) MainActivity.
this
.findViewById(id))
.setImageDrawable(drawable);
}
});
}
}
Handler+Thread+Message模式
这种模式使用了线程,所以可以看到异步加载的效果。
核心代码:
package
ghj1976.AndroidTest;
import
java.io.IOException;
import
java.net.URL;
import
android.app.Activity;
import
android.graphics.drawable.Drawable;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.Message;
import
android.os.SystemClock;
import
android.util.Log;
import
android.widget.ImageView;
public
class
MainActivity
extends
Activity {
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadImage2(
"http://www.baidu.com/img/baidu_logo.gif"
, R.id.imageView1);
loadImage2(
"http://www.chinatelecom.com.cn/images/logo_new.gif"
,
R.id.imageView2);
loadImage2(
"http://cache.soso.com/30d/img/web/logo.gif"
, R.id.imageView3);
loadImage2(
"http://csdnimg.cn/www/images/csdnindex_logo.gif"
,
R.id.imageView4);
loadImage2(
"http://images.cnblogs.com/logo_small.gif"
,
R.id.imageView5);
}
final
Handler handler2 =
new
Handler() {
@Override
public
void
handleMessage(Message msg) {
((ImageView) MainActivity.
this
.findViewById(msg.arg1))
.setImageDrawable((Drawable) msg.obj);
}
};
// 采用handler+Thread模式实现多线程异步加载
private
void
loadImage2(
final
String url,
final
int
id) {
Thread thread =
new
Thread() {
@Override
public
void
run() {
Drawable drawable =
null
;
try
{
drawable = Drawable.createFromStream(
new
URL(url).openStream(),
"image.png"
);
}
catch
(IOException e) {
Log.d(
"test"
, e.getMessage());
}
// 模拟网络延时
SystemClock.sleep(
2000
);
Message message = handler2.obtainMessage();
message.arg1 = id;
message.obj = drawable;
handler2.sendMessage(message);
}
};
thread.start();
thread =
null
;
}
}
这时候我们可以看到实现了异步加载, 界面打开时,五个ImageView都是没有图的,然后在各自线程下载完后才把图自动更新上去。
Handler+ExecutorService(线程池)+MessageQueue模式能开线程的个数毕竟是有限的,我们总不能开很多线程,对于手机更是如此。
这个例子是使用线程池。Android拥有与Java相同的ExecutorService实现,我们就来用它。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
线程池的信息可以参看这篇文章:Java&Android的线程池-ExecutorService 下面的演示例子是创建一个可重用固定线程数的线程池。
核心代码
package
ghj1976.AndroidTest;
import
java.io.IOException;
import
java.net.URL;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
android.app.Activity;
import
android.graphics.drawable.Drawable;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.Message;
import
android.os.SystemClock;
import
android.util.Log;
import
android.widget.ImageView;
public
class
MainActivity
extends
Activity {
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadImage3(
"http://www.baidu.com/img/baidu_logo.gif"
, R.id.imageView1);
loadImage3(
"http://www.chinatelecom.com.cn/images/logo_new.gif"
,
R.id.imageView2);
loadImage3(
"http://cache.soso.com/30d/img/web/logo.gif"
,
R.id.imageView3);
loadImage3(
"http://csdnimg.cn/www/images/csdnindex_logo.gif"
,
R.id.imageView4);
loadImage3(
"http://images.cnblogs.com/logo_small.gif"
,
R.id.imageView5);
}
private
Handler handler =
new
Handler();
private
ExecutorService executorService = Executors.newFixedThreadPool(
5
);
// 引入线程池来管理多线程
private
void
loadImage3(
final
String url,
final
int
id) {
executorService.submit(
new
Runnable() {
public
void
run() {
try
{
final
Drawable drawable = Drawable.createFromStream(
new
URL(url).openStream(),
"image.png"
);
// 模拟网络延时
SystemClock.sleep(
2000
);
handler.post(
new
Runnable() {
public
void
run() {
((ImageView) MainActivity.
this
.findViewById(id))
.setImageDrawable(drawable);
}
});
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
});
}
}
这里我们象第一步一样使用了 handler.post(new Runnable() { 更新前段显示当然是在UI主线程,我们还有 executorService.submit(new Runnable() { 来确保下载是在线程池的线程中。
Handler+ExecutorService(线程池)+MessageQueue+缓存模式下面比起前一个做了几个改造:
- 把整个代码封装在一个类中
- 为了避免出现同时多次下载同一幅图的问题,使用了本地缓存
封装的类:
package
ghj1976.AndroidTest;
import
java.lang.ref.SoftReference;
import
java.net.URL;
import
java.util.HashMap;
import
java.util.Map;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
android.graphics.drawable.Drawable;
import
android.os.Handler;
import
android.os.SystemClock;
public
class
AsyncImageLoader3 {
// 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
public
Map<String, SoftReference<Drawable>> imageCache =
new
HashMap<String, SoftReference<Drawable>>();
private
ExecutorService executorService = Executors.newFixedThreadPool(
5
);
// 固定五个线程来执行任务
private
final
Handler handler =
new
Handler();
/**
*
* @param imageUrl
* 图像url地址
* @param callback
* 回调接口
* <a href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\"" target="\"_blank\"">@return</a> 返回内存中缓存的图像,第一次加载返回null
*/
public
Drawable loadDrawable(
final
String imageUrl,
final
ImageCallback callback) {
// 如果缓存过就从缓存中取出数据
if
(imageCache.containsKey(imageUrl)) {
SoftReference<Drawable> softReference = imageCache.get(imageUrl);
if
(softReference.get() !=
null
) {
return
softReference.get();
}
}
// 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
executorService.submit(
new
Runnable() {
public
void
run() {
try
{
final
Drawable drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl,
new
SoftReference<Drawable>(
drawable));
handler.post(
new
Runnable() {
public
void
run() {
callback.imageLoaded(drawable);
}
});
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
});
return
null
;
}
// 从网络上取数据方法
protected
Drawable loadImageFromUrl(String imageUrl) {
try
{
// 测试时,模拟网络延时,实际时这行代码不能有
SystemClock.sleep(
2000
);
return
Drawable.createFromStream(
new
URL(imageUrl).openStream(),
"image.png"
);
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
// 对外界开放的回调接口
public
interface
ImageCallback {
// 注意 此方法是用来设置目标对象的图像资源
public
void
imageLoaded(Drawable imageDrawable);
}
}
说明:
final参数是指当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。参看:Java关键字final、static使用总结
这里使用SoftReference 是为了解决内存不足的错误(OutOfMemoryError)的,更详细的可以参看:内存优化的两个类:SoftReference 和 WeakReference
前段调用:
package
ghj1976.AndroidTest;
import
android.app.Activity;
import
android.graphics.drawable.Drawable;
import
android.os.Bundle;
import
android.widget.ImageView;
public
class
MainActivity
extends
Activity {
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadImage4(
"http://www.baidu.com/img/baidu_logo.gif"
, R.id.imageView1);
loadImage4(
"http://www.chinatelecom.com.cn/images/logo_new.gif"
,
R.id.imageView2);
loadImage4(
"http://cache.soso.com/30d/img/web/logo.gif"
,
R.id.imageView3);
loadImage4(
"http://csdnimg.cn/www/images/csdnindex_logo.gif"
,
R.id.imageView4);
loadImage4(
"http://images.cnblogs.com/logo_small.gif"
,
R.id.imageView5);
}
private
AsyncImageLoader3 asyncImageLoader3 =
new
AsyncImageLoader3();
// 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
private
void
loadImage4(
final
String url,
final
int
id) {
// 如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行
Drawable cacheImage = asyncImageLoader3.loadDrawable(url,
new
AsyncImageLoader3.ImageCallback() {
// 请参见实现:如果第一次加载url时下面方法会执行
public
void
imageLoaded(Drawable imageDrawable) {
((ImageView) findViewById(id))
.setImageDrawable(imageDrawable);
}
});
if
(cacheImage !=
null
) {
((ImageView) findViewById(id)).setImageDrawable(cacheImage);
}
}
}
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Android开发 Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Android Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Android Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式机制
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- 解决通过this.class.getResource()得到的URL中含有空格为乱码的问题
- PHP抓取页面的几种方式
- 路由器连接路由器设置方法
- JVM的常用参数
- #include< >和#include“ ”的区别
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- android-摇一摇实例
- UVALive 4327 Parade(hdu 2490 Parade)
- POJ2823_Slidingwindow_solution
- redis 持久化与备份策略
- jsp学习之旅的错误记录01
- 晨会如何更好的描述自己的工作
- jQuery 双击事件(dblclick)时,不触发单击事件(click)
- starting with mongodb 2013/8/23