Android
1.1 背景相关与系统架构分析
1.2 开发环境搭建
1.2.1 使用Eclipse + ADT + SDK开发Android APP
1.2.2 使用Android Studio开发Android APP
1.3 SDK更新不了问题解决
1.4 Genymotion模拟器安装
1.5.1 Git使用教程之本地仓库的基本操作
1.5.2 Git之使用GitHub搭建远程仓库
1.6 .9(九妹)图片怎么玩
1.7 界面原型设计
1.8 工程相关解析(各种文件,资源访问)
1.9 Android程序签名打包
1.11 反编译APK获取代码&资源
2.1 View与ViewGroup的概念
2.2.1 LinearLayout(线性布局)
2.2.2 RelativeLayout(相对布局)
2.2.3 TableLayout(表格布局)
2.2.4 FrameLayout(帧布局)
2.2.5 GridLayout(网格布局)
2.2.6 AbsoluteLayout(绝对布局)
2.3.1 TextView(文本框)详解
2.3.2 EditText(输入框)详解
2.3.3 Button(按钮)与ImageButton(图像按钮)
2.3.4 ImageView(图像视图)
2.3.5.RadioButton(单选按钮)&Checkbox(复选框)
2.3.6 开关按钮ToggleButton和开关Switch
2.3.7 ProgressBar(进度条)
2.3.8 SeekBar(拖动条)
2.3.9 RatingBar(星级评分条)
2.4.1 ScrollView(滚动条)
2.4.2 Date & Time组件(上)
2.4.3 Date & Time组件(下)
2.4.4 Adapter基础讲解
2.4.5 ListView简单实用
2.4.6 BaseAdapter优化
2.4.7ListView的焦点问题
2.4.8 ListView之checkbox错位问题解决
2.4.9 ListView的数据更新问题
2.5.0 构建一个可复用的自定义BaseAdapter
2.5.1 ListView Item多布局的实现
2.5.2 GridView(网格视图)的基本使用
2.5.3 Spinner(列表选项框)的基本使用
2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用
2.5.5 ExpandableListView(可折叠列表)的基本使用
2.5.6 ViewFlipper(翻转视图)的基本使用
2.5.7 Toast(吐司)的基本使用
2.5.8 Notification(状态栏通知)详解
2.5.9 AlertDialog(对话框)详解
2.6.0 其他几种常用对话框基本使用
2.6.1 PopupWindow(悬浮框)的基本使用
2.6.2 菜单(Menu)
2.6.3 ViewPager的简单使用
2.6.4 DrawerLayout(官方侧滑菜单)的简单使用
3.1.1 基于监听的事件处理机制
3.2 基于回调的事件处理机制
3.3 Handler消息传递机制浅析
3.4 TouchListener PK OnTouchEvent + 多点触碰
3.5 监听EditText的内容变化
3.6 响应系统设置的事件(Configuration类)
3.7 AnsyncTask异步任务
3.8 Gestures(手势)
4.1.1 Activity初学乍练
4.1.2 Activity初窥门径
4.1.3 Activity登堂入室
4.2.1 Service初涉
4.2.2 Service进阶
4.2.3 Service精通
4.3.1 BroadcastReceiver牛刀小试
4.3.2 BroadcastReceiver庖丁解牛
4.4.1 ContentProvider初探
4.4.2 ContentProvider再探——Document Provider
4.5.1 Intent的基本使用
4.5.2 Intent之复杂数据的传递
5.1 Fragment基本概述
5.2.1 Fragment实例精讲——底部导航栏的实现(方法1)
5.2.2 Fragment实例精讲——底部导航栏的实现(方法2)
5.2.3 Fragment实例精讲——底部导航栏的实现(方法3)
5.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面
5.2.5 Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现
6.1 数据存储与访问之——文件存储读写
6.2 数据存储与访问之——SharedPreferences保存用户偏好参数
6.3.1 数据存储与访问之——初见SQLite数据库
6.3.2 数据存储与访问之——又见SQLite数据库
7.1.1 Android网络编程要学的东西与Http协议学习
7.1.2 Android Http请求头与响应头的学习
7.1.3 Android HTTP请求方式:HttpURLConnection
7.1.4 Android HTTP请求方式:HttpClient
7.2.1 Android XML数据解析
7.2.2 Android JSON数据解析
7.3.1 Android 文件上传
7.3.2 Android 文件下载(1)
7.3.3 Android 文件下载(2)
7.5.1 WebView(网页视图)基本用法
7.5.2 WebView和JavaScrip交互基础
7.5.3 Android 4.4后WebView的一些注意事项
7.5.4 WebView文件下载
7.5.5 WebView缓存问题
7.5.6 WebView处理网页返回的错误码信息
7.6.1 Socket学习网络基础准备
7.6.2 基于TCP协议的Socket通信(1)
7.6.3 基于TCP协议的Socket通信(2)
7.6.4 基于UDP协议的Socket通信
8.1.1 Android中的13种Drawable小结 Part 1
8.1.2 Android中的13种Drawable小结 Part 2
8.1.3 Android中的13种Drawable小结 Part 3
8.2.1 Bitmap(位图)全解析 Part 1
8.3.1 三个绘图工具类详解
8.3.2 绘图类实战示例
8.3.3 Paint API之—— MaskFilter(面具)
8.3.4 Paint API之—— Xfermode与PorterDuff详解(一)
8.3.5 Paint API之—— Xfermode与PorterDuff详解(二)
8.3.6 Paint API之—— Xfermode与PorterDuff详解(三)
8.3.7 Paint API之—— Xfermode与PorterDuff详解(四)
8.3.8 Paint API之—— Xfermode与PorterDuff详解(五)
8.3.9 Paint API之—— ColorFilter(颜色过滤器)(1/3)
8.3.10 Paint API之—— ColorFilter(颜色过滤器)(2-3)
8.3.11 Paint API之—— ColorFilter(颜色过滤器)(3-3)
8.3.12 Paint API之—— PathEffect(路径效果)
8.3.13 Paint API之—— Shader(图像渲染)
8.3.14 Paint几个枚举/常量值以及ShadowLayer阴影效果
8.3.15 Paint API之——Typeface(字型)
8.3.16 Canvas API详解(Part 1)
8.3.17 Canvas API详解(Part 2)剪切方法合集
8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash
8.4.1 Android动画合集之帧动画
8.4.2 Android动画合集之补间动画
8.4.3 Android动画合集之属性动画-初见
8.4.4 Android动画合集之属性动画-又见
9.1 使用SoundPool播放音效(Duang~)
9.2 MediaPlayer播放音频与视频
10.1 TelephonyManager(电话管理器)
10.2 SmsManager(短信管理器)
10.3 AudioManager(音频管理器)
10.4 Vibrator(振动器)
10.5 AlarmManager(闹钟服务)
10.6 PowerManager(电源服务)
10.7 WindowManager(窗口管理服务)
10.8 LayoutInflater(布局服务)
10.9 WallpaperManager(壁纸管理器)
10.10 传感器专题(1)——相关介绍
10.11 传感器专题(2)——方向传感器
10.12 传感器专题(3)——加速度/陀螺仪传感器
 

8.2.2 Bitmap引起的OOM问题

阅读:358      收藏:0      [点我收藏+]

本节引言:

上节,我们已经学习了Bitmap的基本用法,而本节我们要来探讨的Bitmap的OOM问题, 大家在实际开发中可能遇到过,或者没遇到过因为Bitmap引起的OOM问题,本节我们 就来围绕这个话题来进行学习~了解什么是OOM,为什么会引起OOM,改善因Bitmap引起的 OOM问题~


1.什么是OOM?为什么会引起OOM?

答:Out Of Memory(内存溢出),我们都知道Android系统会为每个APP分配一个独立的工作空间, 或者说分配一个单独的Dalvik虚拟机,这样每个APP都可以独立运行而不相互影响!而Android对于每个 Dalvik虚拟机都会有一个最大内存限制,如果当前占用的内存加上我们申请的内存资源超过了这个限制 ,系统就会抛出OOM错误!另外,这里别和RAM混淆了,即时当前RAM中剩余的内存有1G多,但是OOM还是会发生!别把RAM(物理内存)和OOM扯到一起!另外RAM不足的话,就是杀应用了,而不是仅仅是OOM了! 而这个Dalvik中的最大内存标准,不同的机型是不一样的,可以调用:

ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
Log.e("HEHE","最大内存:" + activityManager.getMemoryClass());

获得正常的最大内存标准,又或者直接在命令行键入:

adb shell getprop | grep dalvik.vm.heapgrowthlimit

你也可以打开系统源码/system/build.prop文件,看下文件中这一部分的信息得出:

dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=2m
dalvik.vm.heapmaxfree=8m

我们关注的地方有三个:heapstartsize堆内存的初始大小,heapgrowthlimit标准的应用的最大堆 内存大小,heapsize则是设置了使用android:largeHeap的应用的最大堆内存大小!

我这里试了下手头几个机型的正常最大内存分配标准:

你也可以试试自己手头的机子~

好啦,不扯了,关于OOM问题的产生,就扯到这里,再扯就到内存管理那一块了,可是个大块头, 现在还啃不动...下面我们来看下避免Bitmap OOM的一些技巧吧!


2.避免Bitmap引起的OOM技巧小结


1)采用低内存占用量的编码方式

上一节说了BitmapFactory.Options这个类,我们可以设置下其中的inPreferredConfig属性, 默认是Bitmap.Config.ARGB_8888,我们可以修改成Bitmap.Config.ARGB_4444
Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位
Bitmap.Config ARGB_8888:每个像素占八位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位
默认使用ARGB_8888,即一个像素占4个字节!


2)图片压缩

同样是BitmapFactory.Options,我们通过inSampleSize设置缩放倍数,比如写2,即长宽变为原来的1/2,图片就是原来的1/4,如果不进行缩放的话设置为1即可!但是不能一味的压缩,毕竟这个值太小 的话,图片会很模糊,而且要避免图片的拉伸变形,所以需要我们在程序中动态的计算,这个 inSampleSize的合适值,而Options中又有这样一个方法:inJustDecodeBounds,将该参数设置为 true后,decodeFiel并不会分配内存空间,但是可以计算出原始图片的长宽,调用 options.outWidth/outHeight获取出图片的宽高,然后通过一定的算法,即可得到适合的 inSampleSize,这里感谢街神提供的代码——摘自鸿洋blog!

public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
        int widthRadio = Math.round(width * 1.0f / reqWidth);
        int heightRadio = Math.round(height * 1.0f / reqHeight);
        inSampleSize = Math.max(widthRadio, heightRadio);
    }
    return inSampleSize;
}

然后使用下上述的方法即可:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 设置了此属性一定要记得将值设置为false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
options.inSampleSize = computeSampleSize(options,128,128);
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
/* 下面两个字段需要组合使用 */  
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
    bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
        Log.e(TAG, "OutOfMemoryError");
}

3.及时回收图像

如果引用了大量的Bitmap对象,而应用又不需要同时显示所有图片。可以将暂时不用到的Bitmap对象 及时回收掉。对于一些明确知道图片使用情况的场景可以主动recycle回收,比如引导页的图片,使用 完就recycle,帧动画,加载一张,画一张,释放一张!使用时加载,不显示时直接置null或recycle! 比如:imageView.setImageResource(0); 不过某些情况下会出现特定图片反复加载,释放,再加载等,低效率的事情...


4.其他方法

下面这些方法,我并没有用过,大家可以自行查阅相关资料:

1.简单通过SoftReference引用方式管理图片资源

建个SoftReference的hashmap 使用图片时先查询这个hashmap是否有softreference, softreference里的图片是否为空, 如果为空就加载图片到softreference并加入hashmap。 无需再代码里显式的处理图片的回收与释放,gc会自动处理资源的释放。 这种方式处理起来简单实用,能一定程度上避免前一种方法反复加载释放的低效率。但还不够优化。

示例代码:

private Map<String, SoftReference<Bitmap>> imageMap 
                                           = new HashMap<String, SoftReference<Bitmap>>();

public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
    SoftReference<Bitmap> reference = imageMap.get(imageUrl);
    if(reference != null) {
        if(reference.get() != null) {
            return reference.get();
        }
    }
    final Handler handler = new Handler() {
        public void handleMessage(final android.os.Message msg) {
            //加入到缓存中
            Bitmap bitmap = (Bitmap)msg.obj;
            imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
            if(imageCallBack != null) {
                imageCallBack.getBitmap(bitmap);
            }
        }
    };
    new Thread(){
        public void run() {
            Message message = handler.obtainMessage();
            message.obj = downloadBitmap(imageUrl);
            handler.sendMessage(message);
        }
    }.start();
    return null ;
}

// 从网上下载图片
private Bitmap downloadBitmap (String imageUrl) {
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
        return bitmap ;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } 
}
public interface ImageCallBack{
    void getBitmap(Bitmap bitmap);
}

2.LruCache + sd的缓存方式

Android 3.1版本起,官方还提供了LruCache来进行cache处理,当存储Image的大小大于LruCache 设定的值,那么近期使用次数最少的图片就会被回收掉,系统会自动释放内存!

使用示例

步骤:

1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/8, 手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);

2)LruCache里面的键值对分别是URL和对应的图片

3)重写了一个叫做sizeOf的方法,返回的是图片数量。

private LruCache<String, Bitmap> mMemoryCache;
private LruCacheUtils() {
    if (mMemoryCache == null)
        mMemoryCache = new LruCache<String, Bitmap>(
                MAXMEMONRY / 8) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }

            @Override
            protected void entryRemoved(boolean evicted, String key,
                    Bitmap oldValue, Bitmap newValue) {
                Log.v("tag", "hard cache is full , push to soft cache");
               
            }
        };
}

4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。

移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。

public void clearCache() {
    if (mMemoryCache != null) {
        if (mMemoryCache.size() > 0) {
            Log.d("CacheUtils",
                    "mMemoryCache.size() " + mMemoryCache.size());
            mMemoryCache.evictAll();
            Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
        }
        mMemoryCache = null;
    }
}

public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (mMemoryCache.get(key) == null) {
        if (key != null && bitmap != null)
            mMemoryCache.put(key, bitmap);
    } else
        Log.w(TAG, "the res is aready exits");
}

public synchronized Bitmap getBitmapFromMemCache(String key) {
    Bitmap bm = mMemoryCache.get(key);
    if (key != null) {
        return bm;
    }
    return null;
}

/**
 * 移除缓存
 * 
 * @param key
 */
public synchronized void removeImageCache(String key) {
    if (key != null) {
        if (mMemoryCache != null) {
            Bitmap bm = mMemoryCache.remove(key);
            if (bm != null)
                bm.recycle();
        }
    }
}

上述内容摘自——图片缓存之内存缓存技术LruCache,软引用


本节小结:

本节给大家讲解了OOM问题的发生缘由,也总结了一下网上给出的一些避免因Bitmap而引起OOM 的一些方案,因为公司做的APP都是地图类的,很少涉及到图片,所以笔者并没有遇到过OOM的问题, 所以对此并不怎么熟悉~后续在进阶课程的内存管理,我们再慢慢纠结这个OOM的问题,好的, 本节就到这里,谢谢~

© 2014 bubuko.com 版权所有 鲁ICP备09046678号-4
打开技术之扣,分享程序人生!
             

鲁公网安备 37021202000002号