Android中加载网络资源时的优化可使用(线程+缓存)解决

颜战时代

颜战时代

2016-02-19 09:32

清醒时做事,糊涂时读书,大怒时睡觉,无聊时关注图老师为大家准备的精彩内容。下面为大家推荐Android中加载网络资源时的优化可使用(线程+缓存)解决,无聊中的都看过来。
网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化:
1、采用线程池
2、内存缓存+文件缓存
3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4
4、对下载的图片进行按比例缩放,以减少内存的消耗

具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:
代码如下:

SPAN style="FONT-SIZE: 18px"STRONGpublic class MemoryCache {
private static final String TAG = "MemoryCache";
// 放入缓存时是个同步操作
// LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU
// 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率
private MapString, Bitmap cache = Collections
.synchronizedMap(new LinkedHashMapString, Bitmap(10, 1.5f, true));
// 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存
private long size = 0;// current allocated size
// 缓存只能占用的最大堆内存
private long limit = 1000000;// max memory in bytes
public MemoryCache() {
// use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory() / 4);
}
public void setLimit(long new_limit) {
limit = new_limit;
Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");
}
public Bitmap get(String id) {
try {
if (!cache.containsKey(id))
return null;
return cache.get(id);
} catch (NullPointerException ex) {
return null;
}
}
public void put(String id, Bitmap bitmap) {
try {
if (cache.containsKey(id))
size -= getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size += getSizeInBytes(bitmap);
checkSize();
} catch (Throwable th) {
th.printStackTrace();
}
}
/**
* 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存
*
*/
private void checkSize() {
Log.i(TAG, "cache size=" + size + " length=" + cache.size());
if (size limit) {
// 先遍历最近最少使用的元素
IteratorEntryString, Bitmap iter = cache.entrySet().iterator();
while (iter.hasNext()) {
EntryString, Bitmap entry = iter.next();
size -= getSizeInBytes(entry.getValue());
iter.remove();
if (size = limit)
break;
}
Log.i(TAG, "Clean cache. New size " + cache.size());
}
}
public void clear() {
cache.clear();
}
/**
* 图片占用的内存
*
* @param bitmap
* @return
*/
long getSizeInBytes(Bitmap bitmap) {
if (bitmap == null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}/STRONG/SPAN

也可以使用SoftReference,代码会简单很多,但是我推荐上面的方法。
代码如下:

public class MemoryCache {

private MapString, SoftReferenceBitmap cache = Collections
.synchronizedMap(new HashMapString, SoftReferenceBitmap());
public Bitmap get(String id) {
if (!cache.containsKey(id))
return null;
SoftReferenceBitmap ref = cache.get(id);
return ref.get();
}
public void put(String id, Bitmap bitmap) {
cache.put(id, new SoftReferenceBitmap(bitmap));
}
public void clear() {
cache.clear();
}
}

下面是文件缓存类的代码FileCache.java:
代码如下:

public class FileCache {
private File cacheDir;
public FileCache(Context context) {
// 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片
// 没有SD卡就放在系统的缓存目录中
if (android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED))
cacheDir = new File(
android.os.Environment.getExternalStorageDirectory(),
"LazyList");
else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(String url) {
// 将url的hashCode作为缓存的文件名
String filename = String.valueOf(url.hashCode());
// Another possible solution
// String filename = URLEncoder.encode(url);
File f = new File(cacheDir, filename);
return f;
}
public void clear() {
File[] files = cacheDir.listFiles();
if (files == null)
return;
for (File f : files)
f.delete();
}
}

最后最重要的加载图片的类,ImageLoader.java:
代码如下:

public class ImageLoader {
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
private MapImageView, String imageViews = Collections
.synchronizedMap(new WeakHashMapImageView, String());
// 线程池
ExecutorService executorService;
public ImageLoader(Context context) {
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5);
}
// 当进入listview时默认的图片,可换成你自己的默认图片
final int stub_id = R.drawable.stub;
// 最主要的方法
public void DisplayImage(String url, ImageView imageView) {
imageViews.put(imageView, url);
// 先从内存缓存中查找
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else {
// 若没有的话则开启新线程加载图片
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView) {
PhotoToLoad p = new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
// 先从文件缓存中查找是否有
Bitmap b = decodeFile(f);
if (b != null)
return b;
// 最后从指定的url中下载图片
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
// decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
private Bitmap decodeFile(File f) {
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 REQUIRED_SIZE
|| height_tmp / 2 REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
}
return null;
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i) {
url = u;
imageView = i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
@Override
public void run() {
if (imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
// 更新的操作放在UI线程中
Activity a = (Activity) photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
/**
* 防止图片错位
*
* @param photoToLoad
* @return
*/
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
// 用于在UI线程中更新界面
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
if (bitmap != null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
public static void CopyStream(InputStream is, OutputStream os) {
final int buffer_size = 1024;
try {
byte[] bytes = new byte[buffer_size];
for (;;) {
int count = is.read(bytes, 0, buffer_size);
if (count == -1)
break;
os.write(bytes, 0, count);
}
} catch (Exception ex) {
}
}
}

主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。
代码如下:

a.runOnUiThread(...);

在你的程序中的基本用法:
代码如下:

SPAN style="FONT-SIZE: 18px"STRONGImageLoader imageLoader=new ImageLoader(context);
...
imageLoader.DisplayImage(url, imageView);/STRONG/SPAN

比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。
展开更多 50%)
分享

猜你喜欢

Android中加载网络资源时的优化可使用(线程+缓存)解决

编程语言 网络编程
Android中加载网络资源时的优化可使用(线程+缓存)解决

网络资源

PHP
网络资源

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

从资源中加载皮肤

C语言教程 C语言函数
从资源中加载皮肤

Win10没有权限使用网络资源的解决方法

windows10
Win10没有权限使用网络资源的解决方法

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

Win10提示没有权限使用网络资源的解决方法

windows10
Win10提示没有权限使用网络资源的解决方法

Win10提示没有权限使用网络资源怎么办?

电脑入门
Win10提示没有权限使用网络资源怎么办?

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

android CursorLoader用法介绍

android CursorLoader用法介绍

SQL Server2005打开数据表中的XML内容时报错的解决办法

SQL Server2005打开数据表中的XML内容时报错的解决办法
下拉加载更多内容 ↓