1,及时回收bitmap,在activity的onstop()和onDestory()里面调用如下代码进行bitmap的回收:

// 先判断是否已经回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();
bitmap = null;
}
System.gc();

 

2,对oom异常的捕获:出现异常不能让程序就那么崩掉吧,所以对程序中中设计bitmap的操作都要检测oom异常进而进行处理:

Bitmap bitmap = null;
try {
// 实例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
//此处主要处理bitmap加载到内存出现oom的问题,可以在此处加载默认的图片去处理oom的问题。
}
if (bitmap == null) {
// 如果实例化失败 返回默认的Bitmap对象
return defaultBitmapMap;
}

3,通过option相关参数的设置,对图片尺寸进行相应缩小,对图片质量进行降低来减少图片加载到内存中对内存的占用大小:

比如从res中获取图片:

/**
* 获取压缩之后的图片
*
* @param context
* @param resId res下资源图片的id
* @param reqWidth 期望的宽度
* @param reqHeight 期望的高度
* @return
*/
public static Bitmap getImageCompress(Context context, int resId,
int reqWidth, int reqHeight) {
Bitmap bitmap = null;
try {
bitmap =
ImageUtils.decodeSampledBitmapFromResource(context.getResources(),
resId,
reqWidth,
reqHeight);

} catch (OutOfMemoryError e) {
//此处监听oom的问题,出现oom问题就返回默认的图片
bitmap =

BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_launcher);
e.printStackTrace();

}
return bitmap;
}

再看decodeSampledBitmapFromResource方法:

public static Bitmap decodeSampledBitmapFromResource(Resources res,
int resId, int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize =
calculateInSampleSize(options, reqWidth, reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
     //降低了图片的质量,减少内存消耗
options.inPreferredConfig
= Bitmap.Config.RGB_565;
return BitmapFactory.decodeResource(res, resId, options);
}

inSampleSize参数通过下面的calculateInSampleSize()方法获取:

/**
* 获取一个合适的压缩比例:InSampleSize 根据需要(传入的宽高设置),与原图片的宽高进行比例的调节,获取一个合适的InSampleSize也就是一个合适的压缩比例!
*
* @param options BitmapFactory的设置选项
* @param reqWidth 期望压缩后的宽度
* @param reqHeight 期望压缩后的高度
* @return 压缩后的图片
*/
private static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio =
Math.round((float)height / (float)reqHeight);
final int widthRatio = Math.round((float)width / (float)reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}

还有可以参考使用可回收的Imageview,可以根据图片显示状态进行bitmap的回收,进而避免oom的问题

代码来自google官方的bitmapfun的代码:

/**
* Sub-class of ImageView which automatically notifies the drawable when it is
* being displayed.
*/
public class RecyclingImageView extends ImageView { public RecyclingImageView(Context context) {
super(context);
} public RecyclingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
} /**
* @see android.widget.ImageView#onDetachedFromWindow()
*/
@Override
protected void onDetachedFromWindow() {
// This has been detached from Window, so clear the drawable
setImageDrawable(null); super.onDetachedFromWindow();
} /**
* @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)
*/
@Override
public void setImageDrawable(Drawable drawable) {
// Keep hold of previous Drawable
final Drawable previousDrawable = getDrawable(); // Call super to set new Drawable
super.setImageDrawable(drawable); // Notify new Drawable that it is being displayed
notifyDrawable(drawable, true); // Notify old Drawable so it is no longer being displayed
notifyDrawable(previousDrawable, false);
} /**
* Notifies the drawable that it's displayed state has changed.
*
* @param drawable
* @param isDisplayed
*/
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
if (drawable instanceof RecyclingBitmapDrawable) {
// The drawable is a CountingBitmapDrawable, so notify it
((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
} else if (drawable instanceof LayerDrawable) {
// The drawable is a LayerDrawable, so recurse on each layer
LayerDrawable layerDrawable = (LayerDrawable) drawable;
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
}
}
} }

关联另外一个类:

/**
* A BitmapDrawable that keeps track of whether it is being displayed or cached.
* When the drawable is no longer being displayed or cached,
* {@link android.graphics.Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
*/
public class RecyclingBitmapDrawable extends BitmapDrawable { static final String LOG_TAG = "CountingBitmapDrawable";
//缓存引用计数器
private int mCacheRefCount = 0;
//显示引用计数器
private int mDisplayRefCount = 0;
//判断是否已经显示过了
private boolean mHasBeenDisplayed; public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
} /**
* Notify the drawable that the displayed state has changed. Internally a
* count is kept so that the drawable knows when it is no longer being
* displayed.
* 在RecyclingImageView setImageDrawable的时候,这个方法调用,相关计数发 * 生变化,checkState通过计数值的判断决定是否回收bitmap
* @param isDisplayed - Whether the drawable is being displayed or not
*/
public void setIsDisplayed(boolean isDisplayed) {
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
} // Check to see if recycle() can be called
checkState();
} /**
* Notify the drawable that the cache state has changed. Internally a count
* is kept so that the drawable knows when it is no longer being cached.
*
* @param isCached - Whether the drawable is being cached or not
*/
public void setIsCached(boolean isCached) {
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
} // Check to see if recycle() can be called
checkState();
} private synchronized void checkState() {
// If the drawable cache and display ref counts = 0, and this drawable
// has been displayed, then recycle
//如果图片缓存和显示引用计数为0,并且已经显示过了,回收掉
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
&& hasValidBitmap()) {
getBitmap().recycle();
}
} private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
} }

可以下载bitmap自己去参考写适合当前业务的RecyclingImageView,下面给出下载地址:http://download.csdn.net/detail/syy81k816/6811575

关于LRU算法来对内存大小进行限制来避免OOM的问题产生:

内存缓存之LRU算法:
LRU算法其实内部是LinkedHashMap
LinkedHashMap 先进先出 链表结构,能够记住存储顺序,存储的时候会判断缓存是否超出构造方法传进去的缓存大小,如果超出了,会删除最早插入的内容;这个算法可以配合在Volley的图片加载中使用,避免oom的问题出现在volley图片加载中也可以使用,可参考前面写过的博客:http://www.cnblogs.com/androidsuperman/p/8a157b18ede85caa61ca5bc04bba43d0.html 下面下看下代码再说:
1,lruche的构造方法里面初始化创建linkedHashMap对象,指定缓存大小maxSize
2,lruche的put()方法:首先算出占用多大字节,safeSizeOf(key,value);内容在内存中占用的字节
3 , lruche的trimToSize()判断图片的大小是否超出指定的缓存大小,如果超出会调用entryRemove()的方法
其实LRU算法存在于support_v4包下面,具体查看源码:
/*
* Copyright (C) 2011 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.support.v4.util;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Static library version of {@link android.util.LruCache}. Used to write apps
* that run on API levels prior to 12. When running on API level 12 or above,
* this implementation is still used; it does not try to switch to the
* framework's implementation. See the framework SDK documentation for a class
* overview.
*/
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
/** Size of this cache in units. Not necessarily the number of elements. */
private int size;
private int maxSize;
private int putCount;
private int createCount;
private int evictionCount;
private int hitCount;
private int missCount;
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*构造方法里面初始化创建linkedHashMap对象,指定缓存大小maxSize
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
/**
* Sets the size of the cache.
*
* @param maxSize The new maximum size.
*/
public void resize(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
synchronized (this) {
this.maxSize = maxSize;
}
trimToSize(maxSize);
}
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*lruche的put方法:首先算出占用多大字节,safeSizeOf(key,value);内容在内存中占用的字节
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
/**
* Remove the eldest entries until the total of remaining entries is at or
* below the requested size.
*判断图片的大小是否超出指定的缓存大小,如果超出会调用entryRemove()的方法
* @param maxSize the maximum size of the cache before returning. May be -1
* to evict even 0-sized elements.
*/
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
/**
* Removes the entry for {@code key} if it exists.
*
* @return the previous value mapped by {@code key}.
*/
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
/**
* Called for entries that have been evicted or removed. This method is
* invoked when a value is evicted to make space, removed by a call to
* {@link #remove}, or replaced by a call to {@link #put}. The default
* implementation does nothing.
*
* <p>The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* @param evicted true if the entry is being removed to make space, false
* if the removal was caused by a {@link #put} or {@link #remove}.
* @param newValue the new value for {@code key}, if it exists. If non-null,
* this removal was caused by a {@link #put}. Otherwise it was caused by
* an eviction or a {@link #remove}.
*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
/**
* Called after a cache miss to compute a value for the corresponding key.
* Returns the computed value or null if no value can be computed. The
* default implementation returns null.
*
* <p>The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* <p>If a value for {@code key} exists in the cache when this method
* returns, the created value will be released with {@link #entryRemoved}
* and discarded. This can occur when multiple threads request the same key
* at the same time (causing multiple values to be created), or when one
* thread calls {@link #put} while another is creating a value for the same
* key.
*/
protected V create(K key) {
return null;
}
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}
/**
* Returns the size of the entry for {@code key} and {@code value} in
* user-defined units. The default implementation returns 1 so that size
* is the number of entries and max size is the maximum number of entries.
*
* <p>An entry's size must not change while it is in the cache.
*/
protected int sizeOf(K key, V value) {
return 1;
}
/**
* Clear the cache, calling {@link #entryRemoved} on each removed entry.
*/
public final void evictAll() {
trimToSize(-1); // -1 will evict 0-sized elements
}
/**
* For caches that do not override {@link #sizeOf}, this returns the number
* of entries in the cache. For all other caches, this returns the sum of
* the sizes of the entries in this cache.
*/
public synchronized final int size() {
return size;
}
/**
* For caches that do not override {@link #sizeOf}, this returns the maximum
* number of entries in the cache. For all other caches, this returns the
* maximum sum of the sizes of the entries in this cache.
*/
public synchronized final int maxSize() {
return maxSize;
}
/**
* Returns the number of times {@link #get} returned a value that was
* already present in the cache.
*/
public synchronized final int hitCount() {
return hitCount;
}
/**
* Returns the number of times {@link #get} returned null or required a new
* value to be created.
*/
public synchronized final int missCount() {
return missCount;
}
/**
* Returns the number of times {@link #create(Object)} returned a value.
*/
public synchronized final int createCount() {
return createCount;
}
/**
* Returns the number of times {@link #put} was called.
*/
public synchronized final int putCount() {
return putCount;
}
/**
* Returns the number of values that have been evicted.
*/
public synchronized final int evictionCount() {
return evictionCount;
}
/**
* Returns a copy of the current contents of the cache, ordered from least
* recently accessed to most recently accessed.
*/
public synchronized final Map<K, V> snapshot() {
return new LinkedHashMap<K, V>(map);
}
@Override public synchronized final String toString() {
int accesses = hitCount + missCount;
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
maxSize, hitCount, missCount, hitPercent);
}
}

最后一点:最后就是通过图片加载的框架,灵活的处理缓存,避免多个地方引用同一图片时的内存中多余的加载和避免不必要的流量消耗和电量消耗,从而避免个人应用影响设备性能等。

android 图片加载优化,避免oom问题产生的更多相关文章

  1. 关于android 图片加载优化

    android应用对图片处理算是比较频繁的了,尤其是在程序加载大量图片和高分辨率图片时,最容易产生oom异常,下面是个人平时一些省内存加载方法 方法一: public Bitmap decodeFil ...

  2. Android图片加载库的理解

    前言     这是“基础自测”系列的第三篇文章,以Android开发需要熟悉的20个技术点为切入点,本篇重点讲讲Android中的ImageLoader这个库的一些理解,在Android上最让人头疼是 ...

  3. 一起写一个Android图片加载框架

    本文会从内部原理到具体实现来详细介绍如何开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做 ...

  4. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本篇将是我们这个Glide系列的最后一篇文章. 其实在写这个系列第一篇文章的时候,Glide就推出4.0.0的RC版了.那个时候因为我一直研究的都是Glide 3.7.0版本,再加上RC版本还不太稳定 ...

  5. Android图片加载框架最全解析(一),Glide的基本用法

    现在Android上的图片加载框架非常成熟,从最早的老牌图片加载框架UniversalImageLoader,到后来Google推出的Volley,再到后来的新兴军Glide和Picasso,当然还有 ...

  6. fackbook的Fresco (FaceBook推出的Android图片加载库-Fresco)

    [Android开发经验]FaceBook推出的Android图片加载库-Fresco   欢迎关注ndroid-tech-frontier开源项目,定期翻译国外Android优质的技术.开源库.软件 ...

  7. Android 图片加载库Glide 实战(二),占位符,缓存,转换自签名高级实战

    http://blog.csdn.net/sk719887916/article/details/40073747 请尊重原创 : skay <Android 图片加载库Glide 实战(一), ...

  8. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

    我们的Glide系列文章终于要进入收尾篇了.从我开始写这个系列的第一篇文章时,我就知道这会是一个很长的系列,只是没有想到竟然会写这么久. 在前面的六篇文章中,我们对Glide的方方面面都进行了学习,包 ...

  9. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    在本系列的上一篇文章中,我带着大家一起阅读了一遍Glide的源码,初步了解了这个强大的图片加载框架的基本执行流程. 不过,上一篇文章只能说是比较粗略地阅读了Glide整个执行流程方面的源码,搞明白了G ...

随机推荐

  1. 结构-行为-样式-requireJs实现图片轮播插件

    最近工作需要,就自己写了一个图片轮播插件,不过想到要集成到框架中,于是又用RequireJs改了一遍. 主要文件: style.css jquery-1.11.1.min.js require.js ...

  2. Android开发学习——自定义View

    转载自: http://blog.csdn.net/xmxkf/article/details/51454685 本文出自:[openXu的博客]

  3. 2017 年不可错过的开发工具 Top 50

    想知道 2017 年有哪些值得关注的开发工具吗?StackShare 年度开发工具排行榜来啦! StackShare.io 是一个开发者工具及服务分享平台,致力于发现并分享开发者使用的开发工具.服务与 ...

  4. GMap学习笔记

    GMap学习笔记 1.GMap体系详解 What is the map control (GMapControl)? This is the control which renders the map ...

  5. DBCP连接池的使用

    1.新建工程 2.导入commons-dbcp commons-logging commons-pool这三个包 3.创建dbcpconfig.properties的文件 实例如下 #连接设置 dri ...

  6. C#中的重写和覆盖的区别

    #中重写(override)和覆盖(new)的区别 重写用关键字 virtual 修饰的方法,叫虚方法.可以在子类中用override 声明同名的方法,这叫“重写”.相应的没有用virtual修饰的方 ...

  7. System.Data.DbType和数据库映射关系

    有如下类型的映射对照: System.Data.SqlClient.SqlDbType  System.Data.OleDb.OleDbType System.Data.Odbc.OdbcType S ...

  8. [学习笔记]agileone安装登陆报错,localhost重定向次数过多

    1.找到.htaccess文件 2.修改RewriteEngine  参数为off即可

  9. AndroidStudio0.5.2 BUG 导致 menu 菜单键崩溃

    郁闷了半天,今天发现一点击手机 menu 键应用就崩溃了,记得之前都是好好的,调试了半天代码还是搞不定,于是网上google了一番,发现仅国外有一两篇文章有提到类似问题,据说是 0.5.2 版本的 B ...

  10. Hibernate5-课程笔记4

    单表查询:   Hibernate是DAO层技术,对数据的使用,查询是最为重要的.Hibernate的查询技术非常强大,支持原始SQL语句查询,支持QBC查询及Hibernate特有的HQL查询. H ...