001 | package cn.jd3g.utils; |
002 | |
003 | import java.lang.ref.SoftReference; |
004 | import java.util.HashMap; |
005 | import java.util.LinkedHashMap; |
006 | import java.util.Map.Entry; |
007 | |
008 | import android.graphics.Bitmap; |
009 | import android.os.Handler; |
010 | import android.util.Log; |
011 | import android.widget.ImageView; |
012 | |
013 | /** |
014 | * 利用多线程异步加载图片并更新视图 |
015 | * |
016 | * @author xfzhang |
017 | * |
018 | */ |
019 | public final class AsynImageLoader { |
020 | |
021 | private LoaderThread thread; // 加载图片并发消息通知更新界面的线程 |
022 | private HashMap<String, SoftReference<Bitmap>> imageCache; // 图片对象缓存,key:图片的url |
023 | private Handler handler; // 界面Activity的Handler对象 |
024 | |
025 | public AsynImageLoader(Handler handler) { |
026 | imageCache = new HashMap<String, SoftReference<Bitmap>>(); |
027 | this .handler = handler; |
028 | } |
029 | |
030 | /** |
031 | * 加载图片前显示到指定的ImageView中,图片的url保存在视图对象的Tag中 |
032 | * |
033 | * @param imageView |
034 | * 要显示图片的视图 |
035 | * @param defaultBitmap |
036 | * 加载需要显示的提示正在加载的默认图片对象 |
037 | */ |
038 | public void loadBitmap(ImageView imageView, Bitmap defaultBitmap) { |
039 | // 图片所对应的url,这个值在加载图片过程中很可能会被改变 |
040 | String url = (String) imageView.getTag(); |
041 | if (imageCache.containsKey(url)) { // 判断缓存中是否有 |
042 | SoftReference<Bitmap> softReference = imageCache.get(url); |
043 | Bitmap bitmap = softReference.get(); |
044 | if (bitmap != null ) { // 如果图片对象不为空,则可挂接更新视图,并返回 |
045 | imageView.setImageBitmap(bitmap); |
046 | return ; |
047 | } else { // 如果为空,需要将其从缓存中删除(其bitmap对象已被回收释放,需要重新加载) |
048 | Log.e( "TAG" , "cache bitmap is null" ); |
049 | imageCache.remove(url); |
050 | } |
051 | } |
052 | imageView.setImageBitmap(defaultBitmap); // 先显示一个提示正在加载的图片 |
053 | if (thread == null ) { // 加载线程不存在,线程还未启动,需要新建线程并启动 |
054 | thread = new LoaderThread(imageView, url); |
055 | thread.start(); |
056 | } else { // 如果存在,就调用线程对象去加载 |
057 | thread.load(imageView, url); |
058 | } |
059 | |
060 | } |
061 | |
062 | /** |
063 | * 释放缓存中所有的Bitmap对象,并将缓存清空 |
064 | */ |
065 | public void releaseBitmapCache() { |
066 | if (imageCache != null ) { |
067 | for (Entry<String, SoftReference<Bitmap>> entry : imageCache.entrySet()) { |
068 | Bitmap bitmap = entry.getValue().get(); |
069 | if (bitmap != null ) { |
070 | bitmap.recycle(); // 释放bitmap对象 |
071 | } |
072 | } |
073 | imageCache.clear(); |
074 | } |
075 | } |
076 | |
077 | /** |
078 | * 加载图片并显示的线程 |
079 | */ |
080 | private class LoaderThread extends Thread { |
081 | |
082 | LinkedHashMap<String, ImageView> mTaskMap; // 需要加载图片并显示的图片视图对象任务链 |
083 | private boolean mIsWait; // 标识是线程是否处于等待状态 |
084 | |
085 | public LoaderThread(ImageView imageView, String url) { |
086 | mTaskMap = new LinkedHashMap<String, ImageView>(); |
087 | mTaskMap.put(url, imageView); |
088 | } |
089 | |
090 | /** |
091 | * 处理某个视图的更新显示 |
092 | * |
093 | * @param imageView |
094 | */ |
095 | public void load(ImageView imageView, String url) { |
096 | mTaskMap.remove(imageView); // 任务链中可能有,得先删除 |
097 | mTaskMap.put(url, imageView); // 将其添加到任务中 |
098 | if (mIsWait) { // 如果线程此时处于等待得唤醒线程去处理任务队列中待处理的任务 |
099 | synchronized ( this ) { // 调用对象的notify()时必须同步 |
100 | this .notify(); |
101 | } |
102 | } |
103 | } |
104 | |
105 | @Override |
106 | public void run() { |
107 | while (mTaskMap.size() > 0 ) { // 当队列中有数据时线程就要一直运行,一旦进入就要保证其不会跳出循环 |
108 | mIsWait = false ; |
109 | final String url = mTaskMap.keySet().iterator().next(); |
110 | final ImageView imageView = mTaskMap.remove(url); |
111 | if (imageView.getTag() == url) { // 判断视图有没有复用(一旦ImageView被复用,其tag值就会修改变) |
112 | final Bitmap bitmap = MyConnection.getBitmapByUrl(url); // 此方法应该是从网络或sd卡中加载 |
113 | try { |
114 | Thread.sleep( 1000 ); // 模拟网络加载数据时间 |
115 | } catch (InterruptedException e1) { |
116 | e1.printStackTrace(); |
117 | } |
118 | // 将加载的图片放入缓存map中 |
119 | imageCache.put(url, new SoftReference<Bitmap>(bitmap)); |
120 | if (url == imageView.getTag()) { // 再次判断视图有没有复用 |
121 | handler.post( new Runnable() { // 通过消息机制在主线程中更新UI |
122 | @Override |
123 | public void run() { |
124 | imageView.setImageBitmap(bitmap); |
125 | } |
126 | }); |
127 | } |
128 | } |
129 | if (mTaskMap.isEmpty()) { // 当任务队列中没有待处理的任务时,线程进入等待状态 |
130 | try { |
131 | mIsWait = true ; // 标识线程的状态,必须在wait()方法之前 |
132 | synchronized ( this ) { |
133 | this .wait(); // 保用线程进入等待状态,直到有新的任务被加入时通知唤醒 |
134 | } |
135 | } catch (InterruptedException e) { |
136 | e.printStackTrace(); |
137 | } |
138 | } |
139 | } |
140 | } |
141 | } |
142 | } |
01 | private class ProductListAdapter extends BaseAdapter { |
02 | |
03 | private AsynImageLoader mImageAsynLoader; |
04 | |
05 | public ProductListAdapter() { |
06 | mImageAsynLoader = new AsynImageLoader(mHandler); |
07 | } |
08 | |
09 | @Override |
10 | public int getCount() { |
11 | int size = Math.min(mLastItemViewIndex + 1 , mDataList.size()); |
12 | mLastItemViewIndex = size - 1 ; |
13 | return size; |
14 | } |
15 | |
16 | @Override |
17 | public Object getItem( int position) { |
18 | return mDataList.get(position); |
19 | } |
20 | |
21 | @Override |
22 | public long getItemId( int position) { |
23 | return position; |
24 | } |
25 | |
26 | @Override |
27 | public View getView( int position, View convertView, ViewGroup parent) { |
28 | if (convertView == null ) { |
29 | convertView = getLayoutInflater().inflate(R.layout.product_list_item, |
30 | null ); |
31 | } |
32 | ImageView imageView = (ImageView) convertView |
33 | .findViewById(R.id.iv_item_product_image); |
34 | Map<String, String> map = mDataList.get(position); |
35 | //存放图片所对应的url |
36 | imageView.setTag(map.get( "product_pic_address" )); |
37 | mImageAsynLoader.loadBitmap(imageView, mDefautBitmap); |
38 | return convertView; |
39 | } |
40 | } |
联系客服