android 下的网络图片加载

Android图片的异步加载,主要原理:

加载图片时先查看缓存中时候存在该图片,如果存在则返回该图片,否则先加载载一个默认的占位图片,同时创建一个通过网络获取图片的任务并添加,任务完成后放松消息给主线程更新界面。

废话少说,先贴上代码:

1 package com.wangge.uumao.http;
  2
  3 import java.lang.ref.SoftReference;
  4 import java.util.ArrayList;
  5 import java.util.HashMap;
  6 import java.util.List;
  7 import java.util.Map;
  8
  9 import android.graphics.Bitmap;
 10 import android.os.Handler;
 11 import android.os.Message;
 12 import android.util.Log;
 13 import android.widget.ImageView;
 14
 15 import com.wangge.uumao.util.PicUtil;
 16
 17 public class AsynImageLoader {
 18     private static final String TAG = "AsynImageLoader";
 19     // 缓存下载过的图片的Map
 20     private Map<String, SoftReference<Bitmap>> caches;
 21     // 任务队列
 22     private List<Task> taskQueue;
 23     private boolean isRunning = false;
 24
 25     public AsynImageLoader(){
 26         // 初始化变量
 27         caches = new HashMap<String, SoftReference<Bitmap>>();
 28         taskQueue = new ArrayList<AsynImageLoader.Task>();
 29         // 启动图片下载线程
 30         isRunning = true;
 31         new Thread(runnable).start();
 32     }
 33
 34     /**
 35      *
 36      * @param imageView 需要延迟加载图片的对象
 37      * @param url 图片的URL地址
 38      * @param resId 图片加载过程中显示的图片资源
 39      */
 40     public void showImageAsyn(ImageView imageView, String url, int resId){
 41         imageView.setTag(url);
 42         Bitmap bitmap = loadImageAsyn(url, getImageCallback(imageView, resId));
 43
 44         if(bitmap == null){
 45             imageView.setImageResource(resId);
 46         }else{
 47             imageView.setImageBitmap(bitmap);
 48         }
 49     }
 50
 51     public Bitmap loadImageAsyn(String path, ImageCallback callback){
 52         // 判断缓存中是否已经存在该图片
 53         if(caches.containsKey(path)){
 54             // 取出软引用
 55             SoftReference<Bitmap> rf = caches.get(path);
 56             // 通过软引用,获取图片
 57             Bitmap bitmap = rf.get();
 58             // 如果该图片已经被释放,则将该path对应的键从Map中移除掉
 59             if(bitmap == null){
 60                 caches.remove(path);
 61             }else{
 62                 // 如果图片未被释放,直接返回该图片
 63                 Log.i(TAG, "return image in cache" + path);
 64                 return bitmap;
 65             }
 66         }else{
 67             // 如果缓存中不常在该图片,则创建图片下载任务
 68             Task task = new Task();
 69             task.path = path;
 70             task.callback = callback;
 71             Log.i(TAG, "new Task ," + path);
 72             if(!taskQueue.contains(task)){
 73                 taskQueue.add(task);
 74                 // 唤醒任务下载队列
 75                 synchronized (runnable) {
 76                     runnable.notify();
 77                 }
 78             }
 79         }
 80
 81         // 缓存中没有图片则返回null
 82         return null;
 83     }
 84
 85     /**
 86      *
 87      * @param imageView
 88      * @param resId 图片加载完成前显示的图片资源ID
 89      * @return
 90      */
 91     private ImageCallback getImageCallback(final ImageView imageView, final int resId){
 92         return new ImageCallback() {
 93
 94             @Override
 95             public void loadImage(String path, Bitmap bitmap) {
 96                 if(path.equals(imageView.getTag().toString())){
 97                     imageView.setImageBitmap(bitmap);
 98                 }else{
 99                     imageView.setImageResource(resId);
100                 }
101             }
102         };
103     }
104
105     private Handler handler = new Handler(){
106
107         @Override
108         public void handleMessage(Message msg) {
109             // 子线程中返回的下载完成的任务
110             Task task = (Task)msg.obj;
111             // 调用callback对象的loadImage方法,并将图片路径和图片回传给adapter
112             task.callback.loadImage(task.path, task.bitmap);
113         }
114
115     };
116
117     private Runnable runnable = new Runnable() {
118
119         @Override
120         public void run() {
121             while(isRunning){
122                 // 当队列中还有未处理的任务时,执行下载任务
123                 while(taskQueue.size() > 0){
124                     // 获取第一个任务,并将之从任务队列中删除
125                     Task task = taskQueue.remove(0);
126                     // 将下载的图片添加到缓存
127                     task.bitmap = PicUtil.getbitmap(task.path);
128                     caches.put(task.path, new SoftReference<Bitmap>(task.bitmap));
129                     if(handler != null){
130                         // 创建消息对象,并将完成的任务添加到消息对象中
131                         Message msg = handler.obtainMessage();
132                         msg.obj = task;
133                         // 发送消息回主线程
134                         handler.sendMessage(msg);
135                     }
136                 }
137
138                 //如果队列为空,则令线程等待
139                 synchronized (this) {
140                     try {
141                         this.wait();
142                     } catch (InterruptedException e) {
143                         e.printStackTrace();
144                     }
145                 }
146             }
147         }
148     };
149
150     //回调接口
151     public interface ImageCallback{
152         void loadImage(String path, Bitmap bitmap);
153     }
154
155     class Task{
156         // 下载任务的下载路径
157         String path;
158         // 下载的图片
159         Bitmap bitmap;
160         // 回调对象
161         ImageCallback callback;
162
163         @Override
164         public boolean equals(Object o) {
165             Task task = (Task)o;
166             return task.path.equals(path);
167         }
168     }
169 }  

源代码的17-24行是一些变量,数组的声明。

1    private static final String TAG = "AsynImageLoader";
2     // 缓存下载过的图片的Map
3     private Map<String, SoftReference<Bitmap>> caches;
4     // 任务队列
5     private List<Task> taskQueue;
6     private boolean isRunning = false;
7       

值得一提的是的这里的下载过的图片的mAp是软应用了,为什么了。我们查阅资料以后得知:
如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只 要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 软引用可以和一个引用队列(ReferenceQueue)联 合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

 由于图片是大对象了,确保没有足够的缓存以后,java虚拟机能够将其回收。

代码的25-33行做了一件事,来初始化变量。

1  public AsynImageLoader(){
2         // 初始化变量
3         caches = new HashMap<String, SoftReference<Bitmap>>();
4         taskQueue = new ArrayList<AsynImageLoader.Task>();
5         // 启动图片下载线程
6         isRunning = true;
7         new Thread(runnable).start();
8     }  

并且启动图片下载线程。
40行-50行主要是用来启动异步线程来展示图片。

public void showImageAsyn(ImageView imageView, String url, int resId){
        imageView.setTag(url);
        Bitmap bitmap = loadImageAsyn(url, getImageCallback(imageView, resId));  

        if(bitmap == null){
            imageView.setImageResource(resId);
        }else{
            imageView.setImageBitmap(bitmap);
        }
    }  

这个方法,主要调用了本段源代码,最核心的方法loadImageAsyn方法。

  public Bitmap loadImageAsyn(String path, ImageCallback callback){
        // 判断缓存中是否已经存在该图片
        if(caches.containsKey(path)){
            // 取出软引用
            SoftReference<Bitmap> rf = caches.get(path);
            // 通过软引用,获取图片
            Bitmap bitmap = rf.get();
            // 如果该图片已经被释放,则将该path对应的键从Map中移除掉
            if(bitmap == null){
                caches.remove(path);
            }else{
                // 如果图片未被释放,直接返回该图片
                Log.i(TAG, "return image in cache" + path);
                return bitmap;
            }
        }else{
            // 如果缓存中不常在该图片,则创建图片下载任务
            Task task = new Task();
            task.path = path;
            task.callback = callback;
            Log.i(TAG, "new Task ," + path);
            if(!taskQueue.contains(task)){
                taskQueue.add(task);
                // 唤醒任务下载队列
                synchronized (runnable) {
                    runnable.notify();
                }
            }
        }  

        // 缓存中没有图片则返回null
        return null;
    }  

 首先判断缓存池中是否包含了这个图片,如果有这个图片,就从缓存中取得这个图片,否则的话,就从网络段来请求。这就是本方法的作用。

而runnable是从网络直接下载图片的方法。

private Runnable runnable = new Runnable() {
118.
119.        @Override
120.        public void run() {
121.            while(isRunning){
122.                // 当队列中还有未处理的任务时,执行下载任务
123.                while(taskQueue.size() > 0){
124.                    // 获取第一个任务,并将之从任务队列中删除
125.                    Task task = taskQueue.remove(0);
126.                    // 将下载的图片添加到缓存
127.                    task.bitmap = PicUtil.getbitmap(task.path);
128.                    caches.put(task.path, new SoftReference<Bitmap>(task.bitmap));
129.                    if(handler != null){
130.                        // 创建消息对象,并将完成的任务添加到消息对象中
131.                        Message msg = handler.obtainMessage();
132.                        msg.obj = task;
133.                        // 发送消息回主线程
134.                        handler.sendMessage(msg);
135.                    }
136.                }
137.
138.                //如果队列为空,则令线程等待
139.                synchronized (this) {
140.                    try {
141.                        this.wait();
142.                    } catch (InterruptedException e) {
143.                        e.printStackTrace();
144.                    }
145.                }
146.            }
147.        }
148.    };  

上述的代码告诉我们这样一个意思,通过一个消息的队列来下载图片,如果没有此线程的话,就令线程等待。

这就实现了android异步加载图片的应用。

时间: 2024-09-14 09:38:41

android 下的网络图片加载的相关文章

Android:下拉刷新+加载更多+滑动删除实例讲解_Android

         小伙伴们在逛淘宝或者是各种app上,都可以看到这样的功能,下拉刷新和加载更多以及滑动删除,刷新,指刷洗之后使之变新,比喻突破旧的而创造出新的,比如在手机上浏览新闻的时候,使用下拉刷新的功能,我们可以第一时间掌握最新消息,加载更多是什么nie,简单来说就是在网页上逛淘宝的时候,我们可以点击下一页来满足我们更多的需求,但是在手机端就不一样了,没有上下页,怎么办nie,方法总比困难多,细心的小伙伴可能会发现,在手机端中,有加载更多来满足我们的要求,其实加载更多也是分页的一种体现.小伙

Android实现上拉加载更多以及下拉刷新功能(ListView)_Android

首先为大家介绍Andorid5.0原生下拉刷新简单实现. 先上效果图: 相对于上一个19.1.0版本中的横条效果好看了很多.使用起来也很简单. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" and

Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法_Android

RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看:     使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用Recycl

Android程序开发ListView+Json+异步网络图片加载+滚动翻页的例子(图片能缓存,图片不错乱)_Android

例子中用于解析Json的Gson请自己Google下载 主Activity: package COM.Example.Main; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import COM.Example.Main.R; import COM.Example.Main.stringG

Android实现Listview异步加载网络图片并动态更新的方法_Android

本文实例讲述了Android实现Listview异步加载网络图片并动态更新的方法.分享给大家供大家参考,具体如下: 应用实例:解析后台返回的数据,把每条都显示在ListView中,包括活动图片.店名.活动详情.地址.电话和距离等. 在布局文件中ListView的定义: <ListView android:id="@id/maplistview" android:background="@drawable/bg" android:layout_width=&qu

Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

Android实现上拉加载更多以及下拉刷新功能(ListView)

首先为大家介绍Andorid5.0原生下拉刷新简单实现. 先上效果图: 相对于上一个19.1.0版本中的横条效果好看了很多.使用起来也很简单. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" and

Android实现Listview异步加载网络图片并动态更新的方法

本文实例讲述了Android实现Listview异步加载网络图片并动态更新的方法.分享给大家供大家参考,具体如下: 应用实例:解析后台返回的数据,把每条都显示在ListView中,包括活动图片.店名.活动详情.地址.电话和距离等. 在布局文件中ListView的定义: <ListView android:id="@id/maplistview" android:background="@drawable/bg" android:layout_width=&qu

Android常用的图片加载库

前言:图片加载涉及到图片的缓存.图片的处理.图片的显示等.四种常用的图片加载框架,分别是Fresco.ImageLoader. Picasso. Glide. Universal Image Loader:ImageLoader是比较老的框架,一个强大的图片加载库,包含各种各样的配置,最老牌,使用也最广泛. ImageLoader开源库存哪些特征: 1.多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等 2.支持随意的配置ImageLoader,例如线