做公司的android应用,功能很简单,ViewFlipper中用GestureDetector实现滑动切换图片。重写onFling() 方法,判断手指滑动方向,将Bitmap加载到一个imageView中,在调用viewFlipper的addView()方法,再将imageView加载到viewFlipper中。
最不好解决的就是Bitmap的Out of Memory问题,加载很多图之后会报内存溢出的错误。
关于这个问题的,网上搜了很多解决方案,没有很好的解决方法,恰巧碰到群里也有人遇到过这种问题,现将我找到的解决方法总结如下,后面我会给出自己的解决方案。
1. 设置BitmapFactory的options
代码如下:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, ops);设置的inSampleSize就是将图片的长宽压缩,设置为3意味着,生成的图片长宽各为原长度的1/3。
这个方法只是对性能的一种优化,并没有从根本上解决内存的问题,当加载的Bitmap过多时,依然会有内存溢出的问题。
2. 使用SoftReference实现图片的缓存
代码不贴了,这里有详细的实现方式,以及关于软引用的介绍:
http://www.cnblogs.com/dwinter/archive/2012/01/30/2331556.html
实现的原理就是建立一个softreference用来存放图片和索引,因为softreference容易被GC回收,从而释放占用的内存。
但是亲自实现下来并没有很好的解决OOM的问题。个人谨慎猜测是因为viewFlipper占用着包含Bitmap的imageView,所以引用一直存在,GC并不会自动回收。
3. 调用Bitmap的recycle( )方法回收Bitmap
判断图片id号为current-2和current+2的Bitmap的isRecycled( ),如果没有被回收的话,调用recycle( )强制回收。
其实这个方法效果很好,只是当想但会浏览图片时会出现“试图加载已经被回收的Bitmap“的错误。
我自认为程序写的没什么问题(估计还是有问题),最后就放弃了这个方法。
4. 建立LruCache使图片的加载更有效率
这个方法在谷歌的官方文档上是有的,链接如下:
http://developer.android.com/training/displaying-bitmaps/index.html
实现的原理就是建立一个LruCache,这样可以对图片有效率的显示和利用。
第二种方法提到过softReference,这个文章中是不推荐这样做的,具体原因可以详细的阅读以下。
LruCache是在API Level 11中加入的,那Android 2.2 神马的怎么办?没关系,手动写一个LruCache,或者找一个。我贴一个出来吧。
/**
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.guju.service;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/***
* An LRU cache which stores recently inserted entries and all entries ever
* inserted which still has a strong reference elsewhere.
*/
public class LruCache<K, V> {
private final HashMap<K, V> mLruMap;
private final HashMap<K, Entry<K, V>> mWeakMap =
new HashMap<K, Entry<K, V>>();
private ReferenceQueue<V> mQueue = new ReferenceQueue<V>();
@SuppressWarnings("serial")
public LruCache(final int capacity) {
mLruMap = new LinkedHashMap<K, V>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
};
}
private static class Entry<K, V> extends WeakReference<V> {
K mKey;
public Entry(K key, V value, ReferenceQueue<V> queue) {
super(value, queue);
mKey = key;
}
}
@SuppressWarnings("unchecked")
private void cleanUpWeakMap() {
Entry<K, V> entry = (Entry<K, V>) mQueue.poll();
while (entry != null) {
mWeakMap.remove(entry.mKey);
entry = (Entry<K, V>) mQueue.poll();
}
}
public synchronized V put(K key, V value) {
cleanUpWeakMap();
mLruMap.put(key, value);
Entry<K, V> entry = mWeakMap.put(
key, new Entry<K, V>(key, value, mQueue));
return entry == null ? null : entry.get();
}
public synchronized V get(K key) {
cleanUpWeakMap();
V value = mLruMap.get(key);
if (value != null) return value;
Entry<K, V> entry = mWeakMap.get(key);
return entry == null ? null : entry.get();
}
public synchronized void clear() {
mLruMap.clear();
mWeakMap.clear();
mQueue = new ReferenceQueue<V>();
}
}可以直接拿来用哦亲~不过记得留着人家的版权声明,这是最起码的。
其实我试过以上所有的方法,问题还没解决——即使是在建立一个LruCache之后。
在stackoverflow上提过问题之后,有个人的回答是要调用removeView( ) 释放Bitmap。
恍然大悟啊!好了,问题解决了。
imageView.setImageBitmap(bitmap); imageView.setScaleType(ImageView.ScaleType.CENTER); viewFlipper.removeAllViews(); viewFlipper.addView(imageView);
每次addView之前,remove掉之前的view就可以了。
当然,设置BitmapFactory的options以及建立LruCache也是很必须的,能够更好的提供图片浏览的体验。
不过产生一个新问题就是添加Animation的问题,如果直接调用removeView的话,那个view的动画效果就不容易设置。
希望对用到的人有些帮助。