一Handler+Runnable模式

我们先看一个并不是异步线程加载的例子,使用 Handler+Runnable模式。

这里为何不是新开线程的原因请参看这篇文章:Android Runnable 运行在那个线程 这里的代码其实是在UI 主线程中下载图片的,而不是新开线程。

我们运行下面代码时,会发现他其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。

(请发邮件到 freeget.one@gmail.com  获得最新翻强软件。)

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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_tv9sr\"" onclick= "\"zoom(this," this .src,= "" 0 ,= "" 0 )\ "=" " class=" \"zoom\" " file=" \"http: //www.chinatelecom.com.cn/images/logo_new.gif\"" onmouseover="\"img_onmouseoverfunc(this)\"" onload="\"thumbImg(this)\"" border="\"0\"" alt="\"\"">",
                                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模式

这种模式使用了线程,所以可以看到异步加载的效果。

核心代码:

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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 下面的演示例子是创建一个可重用固定线程数的线程池。

核心代码

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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+缓存模式

下面比起前一个做了几个改造:

  • 把整个代码封装在一个类中
  • 为了避免出现同时多次下载同一幅图的问题,使用了本地缓存

封装的类:

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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

前段调用:

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 异步加载图片总结相关推荐

  1. Android:异步加载图片

    我们知道Android为了不阻塞UI线程(main线程),不允许在非UI线程中进行UI操作以及网络请求等操作,为了不阻塞UI,我们往往就要进行异步加载. 我们以异步加载图片为例子,来学习一下异步加载 ...

  2. wemall app商城源码中基于JAVA的Android异步加载图片管理器代码

    wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...

  3. android异步加载图片并缓存到内存和sd卡上,Android批量图片加载经典系列——采用二级缓存、异步加载网络图片...

    http://www.cnblogs.com/jerehedu/p/4560119.html 2015-06-08 09:20 by 杰瑞教育, 232 阅读, 1 评论, 收藏, 编辑一.问题描述 ...

  4. 演化理解 Android 异步加载图片

    引用:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038738.html 下面测试使用的layout文件: 简单来说就是 LinearLayo ...

  5. Android 异步加载图片分析

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

  6. android图片异步加载图片,Android 异步加载图片分析总结

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

  7. android 异步加载图片缩略图

    建一个AsyncLoadedImage类继承AsyncTask异步加载类,调用publishProgress方法更新onProgressUpdate贮存缩略图信息到Adapter.监听Adapter ...

  8. Android实现ListView异步加载图片

    转: http://www.iteye.com/topic/685986 ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法 ...

  9. android开发小技巧:实现listview异步加载图片

    2019独角兽企业重金招聘Python工程师标准>>> 针对listview异步加载图片这个问题,麦子学院android开发老师讲了一种非常实用的方法,麦子学院android开发老师 ...

最新文章

  1. 计算机网络讨论4,计算机网络的创新研究讨论原稿(资料4)
  2. 招聘面试的套路和原则
  3. [转]分布式事务之TCC服务设计和实现注意事项
  4. [毕业生的商业软件开发之路]系列文章目录规划
  5. android 连接windows 蓝牙 串口工具_ESP32变身蓝牙串口调试工具 只需几行代码即可实现...
  6. 2021年中国传感器行业产业链分析:智能时代发展下,传感器市场需求旺盛[图]
  7. 怎样锁定计算机本地磁盘,win7系统本地磁盘加密的操作方法
  8. 天猫精灵python开发_天猫精灵X1智能音箱使用感想
  9. 笔记本双系统linux显示屏显示错误(笔记本屏幕显示错误 | 外接显示屏显示错误)
  10. 卡通可爱的门户网站登录表单页面
  11. 使用C#进行串口通信开发上位机软件
  12. c语言电子万年历大作业,C语言实现一个简单的电子万年历
  13. java爬虫抓取漫画,一二三部下载到本地
  14. Optimizing App Startup Time
  15. Selenium IDE教程
  16. 国家大地2000坐标 4547范围 坐标值相关
  17. 文本相似度的计算-向量空间模型
  18. 【1419. 数青蛙】
  19. DL20 Gen9 server 安装 Raid
  20. 跑马灯效果影响EditText的焦点

热门文章

  1. fiddler手机抓包问题总结
  2. 【为自己写的】如何提升自信
  3. 中国企业家充斥着弱者思维
  4. android k主题,K主题(萌图锁屏)
  5. jq加css制作图片切换,jQuery+css实现的切换图片功能代码
  6. eclipse安装插件:XXX cannot be resolved解决方案
  7. 安装 wine QQ 最新版本的方法
  8. hub设备_倍思小圆盒四合一HUB转换器功能使用
  9. 测试开发岗位-面试总结
  10. influxdb delete 删除数据