欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 异步加载图片分析总结

程序员文章站 2023-11-12 10:01:22
研究了android从网络上异步加载图像,现总结如下: (1)由于android ui更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到...

研究了android从网络上异步加载图像,现总结如下:
(1)由于android ui更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法。

在主线程中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.png");
} catch (ioexception e) {
}
((imageview) lazyloadimageactivity.this.findviewbyid(id)).setimagedrawable(drawable);
}
});
}

上面这个方法缺点很显然,经测试,如果要加载多个图片,这并不能实现异步加载,而是等到所有的图片都加载完才一起显示,因为它们都运行在一个线程中。

然后,我们可以简单改进下,将handler+runnable模式改为handler+thread+message模式不就能实现同时开启多个线程吗?
(2)在主线程中new 一个handler对象,代码如下:
复制代码 代码如下:

final handler handler2=new handler(){
@override
public void handlemessage(message msg) {
((imageview) lazyloadimageactivity.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) {
}
 
message message= handler2.obtainmessage() ;
message.arg1 = id;
message.obj = drawable;
handler2.sendmessage(message);
}
};
thread.start();
thread = null;
}

这样就简单实现了异步加载了。细想一下,还可以优化的,比如引入线程池、引入缓存等,我们先介绍线程池。
(3)引入executorservice接口,于是代码可以优化如下:
在主线程中加入: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");
handler.post(new runnable() {
 
public void run() {
((imageview) lazyloadimageactivity.this.findviewbyid(id)).setimagedrawable(drawable);
}
});
} catch (exception e) {
throw new runtimeexception(e);
}
}
});
}

4)为了更方便使用我们可以将异步加载图像方法封装一个类,对外界只暴露一个方法即可,考虑到效率问题我们可以引入内存缓存机制,做法是建立一个hashmap,其键(key)为加载图像url,其值(value)是图像对象drawable。先看一下我们封装的类
复制代码 代码如下:

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 = drawable.createfromstream(new url(imageurl).openstream(), "image.png");
 
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 {
return drawable.createfromstream(new url(imageurl).openstream(), "image.png");
} catch (exception e) {
throw new runtimeexception(e);
}
}
//对外界开放的回调接口
public interface imagecallback {
//注意 此方法是用来设置目标对象的图像资源
public void imageloaded(drawable imagedrawable);
}
}

这样封装好后使用起来就方便多了。在主线程中首先要引入asyncimageloader3 对象,然后直接调用其loaddrawable方法即可,需要注意的是imagecallback接口的imageloaded方法是唯一可以把加载的图 像设置到目标imageview或其相关的组件上。

在主线程调用代码:
先实例化对象 private asyncimageloader3 asyncimageloader3 = new asyncimageloader3();
调用异步加载方法:
复制代码 代码如下:

//引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
private void loadimage4(final string url, final int id) {
//如果缓存过就会从缓存中取出图像,imagecallback接口中方法也不会被执行
drawable cacheimage = asyncimageloader.loaddrawable(url,new asyncimageloader.imagecallback() {
//请参见实现:如果第一次加载url时下面方法会执行
public void imageloaded(drawable imagedrawable) {
((imageview) findviewbyid(id)).setimagedrawable(imagedrawable);
}
});
if(cacheimage!=null){
((imageview) findviewbyid(id)).setimagedrawable(cacheimage);
}
}

5)同理,下面也给出采用thread+handler+messagequeue+内存缓存代码,原则同(4),只是把线程池换成了thread+handler+messagequeue模式而已。代码如下:
复制代码 代码如下:

public class asyncimageloader {
//为了加快速度,加入了缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在listview时来回滚动)
private map<string, softreference<drawable>> imagecache = new hashmap<string, softreference<drawable>>();
 
/**
*
* @param imageurl 图像url地址
* @param callback 回调接口
* @return 返回内存中缓存的图像,第一次加载返回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();
}
}
 
final handler handler = new handler() {
@override
public void handlemessage(message msg) {
callback.imageloaded((drawable) msg.obj);
}
};
new thread() {
public void run() {
drawable drawable = loadimagefromurl(imageurl);
imagecache.put(imageurl, new softreference<drawable>(drawable));
handler.sendmessage(handler.obtainmessage(0, drawable));
 
}
 
}.start();
/*
下面注释的这段代码是handler的一种代替方法
*/
// new asynctask() {
// @override
// protected drawable doinbackground(object... objects) {
// drawable drawable = loadimagefromurl(imageurl);
// imagecache.put(imageurl, new softreference<drawable>(drawable));
// return drawable;
// }
//
// @override
// protected void onpostexecute(object o) {
// callback.imageloaded((drawable) o);
// }
// }.execute();
return null;
}
 
protected drawable loadimagefromurl(string imageurl) {
try {
return drawable.createfromstream(new url(imageurl).openstream(), "src");
} catch (exception e) {
throw new runtimeexception(e);
}
}
//对外界开放的回调接口
public interface imagecallback {
public void imageloaded(drawable imagedrawable);
}
}

至此,异步加载就介绍完了,下面给出的代码为测试用的完整代码:
复制代码 代码如下:

package com.bshark.supertelphone.activity;
 
import android.app.activity;
import android.graphics.drawable.drawable;
import android.os.bundle;
import android.os.handler;
import android.os.message;
import android.widget.imageview;
import com.bshark.supertelphone.r;
import com.bshark.supertelphone.ui.adapter.util.asyncimageloader;
import com.bshark.supertelphone.ui.adapter.util.asyncimageloader3;
 
import java.io.ioexception;
import java.net.url;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
 
public class lazyloadimageactivity extends activity {
final handler handler=new handler();
final handler handler2=new handler(){
@override
public void handlemessage(message msg) {
((imageview) lazyloadimageactivity.this.findviewbyid(msg.arg1)).setimagedrawable((drawable)msg.obj);
}
};
private executorservice executorservice = executors.newfixedthreadpool(5); //固定五个线程来执行任务
private asyncimageloader asyncimageloader = new asyncimageloader();
private asyncimageloader3 asyncimageloader3 = new asyncimageloader3();
 
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
 
// loadimage("http://www.chinatelecom.com.cn/images/logo_new.gif", r.id.image1);
// loadimage("http://www.baidu.com/img/baidu_logo.gif", r.id.image2);
// loadimage("http://cache.soso.com/30d/img/web/logo.gif", r.id.image3);
// loadimage("http://www.baidu.com/img/baidu_logo.gif", r.id.image4);
// loadimage("http://cache.soso.com/30d/img/web/logo.gif", r.id.image5);
 
loadimage2("http://www.chinatelecom.com.cn/images/logo_new.gif", r.id.image1);
loadimage2("http://www.baidu.com/img/baidu_logo.gif", r.id.image2);
loadimage2("http://cache.soso.com/30d/img/web/logo.gif", r.id.image3);
loadimage2("http://www.baidu.com/img/baidu_logo.gif", r.id.image4);
loadimage2("http://cache.soso.com/30d/img/web/logo.gif", r.id.image5);
// loadimage3("http://www.chinatelecom.com.cn/images/logo_new.gif", r.id.image1);
// loadimage3("http://www.baidu.com/img/baidu_logo.gif", r.id.image2);
// loadimage3("http://cache.soso.com/30d/img/web/logo.gif", r.id.image3);
// loadimage3("http://www.baidu.com/img/baidu_logo.gif", r.id.image4);
// loadimage3("http://cache.soso.com/30d/img/web/logo.gif", r.id.image5);
 
// loadimage4("http://www.chinatelecom.com.cn/images/logo_new.gif", r.id.image1);
// loadimage4("http://www.baidu.com/img/baidu_logo.gif", r.id.image2);
// loadimage4("http://cache.soso.com/30d/img/web/logo.gif", r.id.image3);
// loadimage4("http://www.baidu.com/img/baidu_logo.gif", r.id.image4);
// loadimage4("http://cache.soso.com/30d/img/web/logo.gif", r.id.image5);
 
// loadimage5("http://www.chinatelecom.com.cn/images/logo_new.gif", r.id.image1);
// //为了测试缓存而模拟的网络延时
// systemclock.sleep(2000);
// loadimage5("http://www.baidu.com/img/baidu_logo.gif", r.id.image2);
// systemclock.sleep(2000);
// loadimage5("http://cache.soso.com/30d/img/web/logo.gif", r.id.image3);
// systemclock.sleep(2000);
// loadimage5("http://www.baidu.com/img/baidu_logo.gif", r.id.image4);
// systemclock.sleep(2000);
// loadimage5("http://cache.soso.com/30d/img/web/logo.gif", r.id.image5);
// systemclock.sleep(2000);
// loadimage5("http://www.baidu.com/img/baidu_logo.gif", r.id.image4);
}
 
@override
protected void ondestroy() {
executorservice.shutdown();
super.ondestroy();
}
//线程加载图像基本原理
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.png");
} catch (ioexception e) {
}
((imageview) lazyloadimageactivity.this.findviewbyid(id)).setimagedrawable(drawable);
}
});
}
//采用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) {
}
 
message message= handler2.obtainmessage() ;
message.arg1 = id;
message.obj = drawable;
handler2.sendmessage(message);
}
};
thread.start();
thread = null;
}
// 引入线程池来管理多线程
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");
handler.post(new runnable() {
 
public void run() {
((imageview) lazyloadimageactivity.this.findviewbyid(id)).setimagedrawable(drawable);
}
});
} catch (exception e) {
throw new runtimeexception(e);
}
}
});
}
//引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
private void loadimage4(final string url, final int id) {
//如果缓存过就会从缓存中取出图像,imagecallback接口中方法也不会被执行
drawable cacheimage = asyncimageloader.loaddrawable(url,new asyncimageloader.imagecallback() {
//请参见实现:如果第一次加载url时下面方法会执行
public void imageloaded(drawable imagedrawable) {
((imageview) findviewbyid(id)).setimagedrawable(imagedrawable);
}
});
if(cacheimage!=null){
((imageview) findviewbyid(id)).setimagedrawable(cacheimage);
}
}
 
//采用handler+thread+封装外部接口
private void loadimage5(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);
}
}
 
 
}

xml文件大致如下:
复制代码 代码如下:

<span style="font-size: 18px"><strong>< ?xml version="1.0" encoding="utf-8"?>
 
< linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="fill_parent" >
<imageview android:id="@+id/image1" android:layout_height="wrap_content" android:layout_width="fill_parent"></imageview>
<imageview android:id="@+id/image2" android:layout_height="wrap_content" android:layout_width="fill_parent"></imageview>
<imageview android:id="@+id/image3" android:layout_height="wrap_content" android:layout_width="fill_parent"></imageview>
<imageview android:id="@+id/image5" android:layout_height="wrap_content" android:layout_width="fill_parent"></imageview>
<imageview android:id="@+id/image4" android:layout_height="wrap_content" android:layout_width="fill_parent"></imageview>
< /linearlayout></strong></span>