服务器之家

服务器之家 > 正文

在Android的应用中实现网络图片异步加载的方法

时间:2019-12-31 14:50     来源/作者:zinss26914

前言
其实很幸运,入职一周之后就能跟着两个师兄做android开发,师兄都是大神,身为小白的我只能多多学习,多多努力。最近一段时间都忙的没机会总结,今天刚完成了android客户端图片异步加载的类,这里记录一下(ps:其实我这里都是参考网上开源实现)


原理
在ListView或者GridView中加载图片的原理基本都是一样的:

    先从内存缓存中获取,取到则返回,取不到进行下一步
    从文件缓存中获取,取到则返回并更新到内存缓存,取不到则进行进行下一步
    从网络上下载图片,并更新内存缓存和文件缓存


流程图如下:

在Android的应用中实现网络图片异步加载的方法

同时,要注意线程的数量。一般在listview中加载图片,大家都是开启新的线程去加载,但是当快速滑动时,很容易造成OOM,因此需要控制线程数量。我们可以通过线程池控制线程的数量,具体线程池的大小还需要根据处理器的情况和业务情况自行判断

建立线程池的方法如下:

   

?
1
ExecutorService executorService = Executors.newFixedThreadPool(5); // 5是可变的

文件缓存类

   

?
1
2
3
4
5
6
7
8
9
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
import java.io.File;
  
 import android.content.Context;
  
 public class FileCache {
   private static final String DIR_NAME = "your_dir";
   private File cacheDir;
  
   public FileCache(Context context) {
     // Find the directory to save cached images
     if (android.os.Environment.getExternalStorageState().equals(
         android.os.Environment.MEDIA_MOUNTED)) {
       cacheDir = new File(
           android.os.Environment.getExternalStorageDirectory(),
           DIR_NAME);
     } else {
       cacheDir = context.getCacheDir();
     }
  
     if (!cacheDir.exists()) {
       cacheDir.mkdirs();
     }
   }
  
   public File getFile(String url) {
     // Identify images by url's hash code
     String filename = String.valueOf(url.hashCode());
  
     File f = new File(cacheDir, filename);
  
     return f;
   }
  
   public void clear() {
     File[] files = cacheDir.listFiles();
     if (files == null) {
       return;
     } else {
       for (File f : files) {
         f.delete();
       }
     }
   }
 }

内存缓存类
这里使用了软引用,Map<String, SoftReference<Bitmap>> cache,可以google一下软引用的机制,简单的说:实现了map,同时当内存紧张时可以被回收,不会造成内存泄露

   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.lang.ref.SoftReference;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
  
 import android.graphics.Bitmap;
  
 public class MemoryCache {
   private Map<String, SoftReference<Bitmap>> cache = Collections
       .synchronizedMap(new LinkedHashMap<String, SoftReference<Bitmap>>(
           10, 1.5f, true));
  
   public Bitmap get(String id) {
     if (!cache.containsKey(id)) {
       return null;
     }
  
     SoftReference<Bitmap> ref = cache.get(id);
  
     return ref.get();
   }
  
   public void put(String id, Bitmap bitmap) {
     cache.put(id, new SoftReference<Bitmap>(bitmap));
   }
  
   public void clear() {
     cache.clear();
   }
 }

图片加载类

?
1
2
3
4
5
6
7
8
9
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.ImageView;
 
public class ImageLoader {
  /**
   * Network time out
   */
  private static final int TIME_OUT = 30000;
  /**
   * Default picture resource
   */
  private static final int DEFAULT_BG = R.drawable.plate_list_head_bg;
 
  /**
   * Thread pool number
   */
  private static final int THREAD_NUM = 5;
 
  /**
   * Memory image cache
   */
  MemoryCache memoryCache = new MemoryCache();
 
  /**
   * File image cache
   */
  FileCache fileCache;
 
  /**
   * Judge image view if it is reuse
   */
  private Map<ImageView, String> imageViews = Collections
      .synchronizedMap(new WeakHashMap<ImageView, String>());
 
  /**
   * Thread pool
   */
  ExecutorService executorService;
 
  /**
   * Handler to display images in UI thread
   */
  Handler handler = new Handler();
 
  public ImageLoader(Context context) {
    fileCache = new FileCache(context);
    executorService = Executors.newFixedThreadPool(THREAD_NUM);
  }
 
  public void disPlayImage(String url, ImageView imageView) {
    imageViews.put(imageView, url);
    Bitmap bitmap = memoryCache.get(url);
    if (bitmap != null) {
      // Display image from Memory cache
      imageView.setImageBitmap(bitmap);
    } else {
      // Display image from File cache or Network
      queuePhoto(url, imageView);
    }
  }
 
  private void queuePhoto(String url, ImageView imageView) {
    PhotoToLoad photoToLoad = new PhotoToLoad(url, imageView);
    executorService.submit(new PhotosLoader(photoToLoad));
  }
 
  private Bitmap getBitmap(String url) {
    File f = fileCache.getFile(url);
 
    // From File cache
    Bitmap bmp = decodeFile(f);
    if (bmp != null) {
      return bmp;
    }
 
    // From Network
    try {
      Bitmap bitmap = null;
      URL imageUrl = new URL(url);
      HttpURLConnection conn = (HttpURLConnection) imageUrl
          .openConnection();
      conn.setConnectTimeout(TIME_OUT);
      conn.setReadTimeout(TIME_OUT);
      conn.setInstanceFollowRedirects(true);
      InputStream is = conn.getInputStream();
      OutputStream os = new FileOutputStream(f);
      copyStream(is, os);
      os.close();
      conn.disconnect();
      bitmap = decodeFile(f);
      return bitmap;
    } catch (Throwable ex) {
      if (ex instanceof OutOfMemoryError) {
        clearCache();
      }
      return null;
    }
 
  }
 
  private void copyStream(InputStream is, OutputStream os) {
    int buffer_size = 1024;
 
    try {
      byte[] bytes = new byte[buffer_size];
      while (true) {
        int count = is.read(bytes, 0, buffer_size);
        if (count == -1) {
          break;
        }
        os.write(bytes, 0, count);
      }
 
    } catch (Exception e) {
 
    }
  }
 
  private Bitmap decodeFile(File f) {
    try {
      // TODO:Compress image size
      FileInputStream fileInputStream = new FileInputStream(f);
      Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream);
      return bitmap;
 
    } catch (FileNotFoundException e) {
      return null;
    }
  }
 
  private void clearCache() {
    memoryCache.clear();
    fileCache.clear();
  }
 
  /**
   * Task for the queue
   *
   * @author zhengyi.wzy
   *
   */
  private class PhotoToLoad {
    public String url;
    public ImageView imageView;
 
    public PhotoToLoad(String url, ImageView imageView) {
      this.url = url;
      this.imageView = imageView;
    }
  }
 
  /**
   * Asynchronous to load picture
   *
   * @author zhengyi.wzy
   *
   */
  class PhotosLoader implements Runnable {
    PhotoToLoad photoToLoad;
 
    public PhotosLoader(PhotoToLoad photoToLoad) {
      this.photoToLoad = photoToLoad;
    }
 
    private boolean imageViewReused(PhotoToLoad photoToLoad) {
      String tag = imageViews.get(photoToLoad.imageView);
      if (tag == null || !tag.equals(photoToLoad.url)) {
        return true;
      }
 
      return false;
    }
 
    @Override
    public void run() {
      // Abort current thread if Image View reused
      if (imageViewReused(photoToLoad)) {
        return;
      }
 
      Bitmap bitmap = getBitmap(photoToLoad.url);
 
      // Update Memory
      memoryCache.put(photoToLoad.url, bitmap);
 
      if (imageViewReused(photoToLoad)) {
        return;
      }
 
      // Don't change UI in children thread
      BitmapDisplayer bd = new BitmapDisplayer(bitmap, photoToLoad);
      handler.post(bd);
    }
 
    class BitmapDisplayer implements Runnable {
      Bitmap bitmap;
      PhotoToLoad photoToLoad;
 
      public BitmapDisplayer(Bitmap bitmap, PhotoToLoad photoToLoad) {
        this.bitmap = bitmap;
        this.photoToLoad = photoToLoad;
      }
 
      @Override
      public void run() {
        if (imageViewReused(photoToLoad)) {
          return;
        }
 
        if (bitmap != null) {
          photoToLoad.imageView.setImageBitmap(bitmap);
        } else {
          photoToLoad.imageView.setImageResource(DEFAULT_BG);
        }
      }
 
    }
  }
}

 

调用方法

?
1
2
ImageLoader imageLoader = new ImageLoader(context);
imageLoader.disPlayImage(imageUrl, imageView);

 

相关文章

热门资讯

玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情 2019-06-22
配置IIS网站web服务器的安全策略配置解决方案
配置IIS网站web服务器的安全策略配置解决方案 2019-05-23
Nginx服务器究竟是怎么执行PHP项目
Nginx服务器究竟是怎么执行PHP项目 2019-05-24
返回顶部