Android异步加载图像

来源:互联网 发布:json rpc 编辑:程序博客网 时间:2024/06/05 18:20

在学习"Android异步加载图像小结"这篇文章时, 发现有些地方没写清楚,我就根据我的理解,把这篇文章的代码重写整理了一遍,下面就是我的整理。

下面测试使用的layout文件:

简单来说就是 LinearLayout 布局,其下放了5个ImageView。

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><TextView android:text="图片区域开始" android:id="@+id/textView2"android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView><ImageView android:id="@+id/imageView1"android:layout_height="wrap_content" android:src="@drawable/icon"android:layout_width="wrap_content"></ImageView><ImageView android:id="@+id/imageView2"android:layout_height="wrap_content" android:src="@drawable/icon"android:layout_width="wrap_content"></ImageView><ImageView android:id="@+id/imageView3"android:layout_height="wrap_content" android:src="@drawable/icon"android:layout_width="wrap_content"></ImageView><ImageView android:id="@+id/imageView4"android:layout_height="wrap_content" android:src="@drawable/icon"android:layout_width="wrap_content"></ImageView><ImageView android:id="@+id/imageView5"android:layout_height="wrap_content" android:src="@drawable/icon"android:layout_width="wrap_content"></ImageView><TextView android:text="图片区域结束" android:id="@+id/textView1"android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView></LinearLayout>

我们将演示的逻辑是异步从服务器上下载5张不同图片,依次放入这5个ImageView。上下2个TextView 是为了方便我们看是否阻塞了UI的显示。

当然 AndroidManifest.xml 文件中要配置好网络访问权限。

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

Handler+Runnable模式

我们先看一个并不是异步线程加载的例子,使用 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 {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);loadImage(http://www.chinatelecom.com.cn/images/logo_new.gif",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 {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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 {@Overridepublic 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 *            回调接口 * @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();}}// 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中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 {@Overridepublic 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);}}}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 物流公司压司机工资怎么办 立元增压泵不出水怎么办 高浊度pac不沉怎么办 集水池中沉积大量污泥怎么办 电脑qq截图不好使怎么办 捡了个手机需要指纹怎么办 手机锁屏锁住了怎么办 三星屏锁忘记了怎么办 文字下面有蓝色直线怎么办 苹果x不能截屏了怎么办 u盘在电脑上打不开怎么办 电脑桌面上文档剪切了怎么办 苹果官网查不到保修日期怎么办 吃了发霉的花生怎么办 鸡吃大蒜多了怎么办 玖瑰花叶子黄怎么办 羊偷吃腥油和花生饼吃多了怎么办 肉牛眼睛有点变黄少吃东西怎么办 黑坑草鱼不开口怎么办 花生和瓜子受潮皮了怎么办? 菜叶上长了腻虫怎么办 磨辊耐磨层脱落怎么办 磨辊耐磨层小块脱落怎么办 密封胶皮圈松了怎么办 汽筒里胶皮垫密封不严怎么办 磁耦气缸脱磁了怎么办 无杆气缸行程大怎么办 c4d中模型变成线怎么办 内径槽异性需要车一刀怎么办 轴承太紧影响转速怎么办 电动车前轮蝶刹抱死怎么办 摩托三轮车油刹抱死怎么办 手动档汽车离合抱死怎么办 别克gl8后轮吃胎怎么办 扭力梁后轮吃胎怎么办 非独立悬挂吃胎怎么办 货车半轴法兰盘裂纹怎么办 小天才平板裂屏了怎么办 新车撞了个坑怎么办 新车碰了个坑怎么办 汽车顶被砸了个坑怎么办