Android开发之图片详解(3)

在Android中,Bitmap普遍是造成OOM的罪魁祸首,而且普遍都认为bitmap所占用的内存远大于Drawable,而且网上也提供了很多内存回收的方法以及建议,比如在activity销毁后主动去调用bitmap.recyle()方法等,那么到底是不是真的呢?我专门针对bitmap和drawble做了以下测试:

为了方便测出OOM,我专门将一张比较大的图片1270*1920放在了mdpi的文件夹下,而我的测试机是320dpi的,那么根据我上篇文章所讲到的,如果通过资源去加载bitmap,那么所占的内存会放大2倍,大概一张图在30M左右。

package com.example.imagetext;

import java.util.ArrayList;
import java.util.List;

import com.example.imagetext.R.drawable;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageView;

public class MainActivity extends Activity {
    private List<Drawable> listd = new ArrayList<Drawable>();
    private List<Bitmap> listb = new ArrayList<Bitmap>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_1));
        listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_2));
        listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_3));
        listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_4));

        Log.e("tttext","Bitmap占用的内存:"+getBitmapsize(listb.get(0))*4/1024/1024+"M");
        Log.e("tttext",  "当前APP分配的内存:"+Runtime.getRuntime().totalMemory()/1024/1024+"M"+"当前APP空闲内存:"+Runtime.getRuntime().freeMemory()/1024/1024+"M当前APP能够分配的最大内存:"+Runtime.getRuntime().maxMemory()/1024/1024+"M");        

    }

    @SuppressLint("NewApi")
    public long getBitmapsize(Bitmap bitmap) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
            return bitmap.getByteCount();
        }
        // Pre HC-MR1
        return bitmap.getRowBytes() * bitmap.getHeight();

    }

    /**
     * 获取屏幕密度系数
     *
     * 例:0.75 / 1.0 / 1.5 / 2 / 3
     *
     */
    public float getScreenDensity(Context context) {
        DisplayMetrics metric = new DisplayMetrics();
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        manager.getDefaultDisplay().getMetrics(metric);
        return metric.density;
    }

    /**
     * 获取屏幕密度
     *
     * 例:120 / 160 / 240 / 320(280-400) / 480(400-560)
     *
     */
    public int getScreenDensityDpi(Context context) {
        DisplayMetrics metric = new DisplayMetrics();
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        manager.getDefaultDisplay().getMetrics(metric);
        return metric.densityDpi;
    }

}

这里我说下,bg_1,bg_2……都是同一张大图的不同命名,为什么不直接用一张图加载5次呢,那是因为Android系统从资源文件夹加载图片的时候会有缓存机制,这个等会再说。

打印结果如下:()

这里顺便说下,关于一个Android应用到底占多少内存的问题,其实这跟不同的手机厂商有关,不过一般都是16的倍数,早前的是16M,32M,后来则是64M,96M,甚至100多M居多,大家可以看到我这个手机一个APP能够分配的最大内存是196M。至于第一个参数,是当前APP申请到的内存,也就是说,这个196M是系统允许非配给你的上限,并不是一下子就全部分配给你,而是内存不足的时候去向JVM申请一部分,不足再申请一部分,如果超过196M则OOM。

当我再增加一张同样大小的图片的时候,其实我们可以自己算出来,174-15+149/4>192M,这个时候则报OOM了

那么是不是用Drawble代替bitmap是不是就可以避免OOM呢,

下面我们来测试一下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listd.add(getResources().getDrawable(R.drawable.bg_1));
        listd.add(getResources().getDrawable(R.drawable.bg_2));
        listd.add(getResources().getDrawable(R.drawable.bg_3));
        listd.add(getResources().getDrawable(R.drawable.bg_4));
        Log.e("tttext",
                "当前APP分配的内存:" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M" + "当前APP空闲内存:"
                        + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M当前APP能够分配的最大内存:"
                        + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");

    }

只是稍作修改:

然后打印结果如下:

而再加一张,同样出现OOM:

这说明在占用内存上,网上流传的bitmap远大于Drawable是不对的。

其实如果当bitmap和Drawable表示的都是一个位图(也就是我们熟知的图片)的时候,Drawable所占用的内存反而是要比bitmap高那么一点点的。

我们完全可以这样写:


        Drawable drawable = getResources().getDrawable(R.drawable.bg_1);
        Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();

Drawable只是一个抽象类,可以看做一个“图画”,当Drawable指向的是一个位图,也就是普通的那些png,jpg这类的图片获取到的时候,它其实是一个bitmapDrawble,当然如果指向的是一个shape,那么就是一个shapeDrawble(图形),由上面的两句代码可以知道,这个时候bitmapDrawble是持有bitmap的一个对象的,所以这个时候的Drawble所占用的内存只可能比bitmap大而不是小。

那么为什么会给我们造成一种Bitmap占用的内存远小大于Drawble的假象呢?

以及Imageview.setImageBitmap(),

setImageDrawable,

setImageResource

这些方法有什么区别呢?

还有到底什么时候该用Bitmap,什么时候该用Drawable,它们各有什么优点和缺点,该怎么避免OOM呢?

下一篇再详细记录下。

时间: 2024-08-29 03:23:48

Android开发之图片详解(3)的相关文章

Android开发之图片详解(4)

接上一篇. 主要研究下bitmap和drawable的使用,以及两者的区别. 先看测试代码: package com.example.imagetext; import java.util.ArrayList; import java.util.List; import com.example.imagetext.R.drawable; import android.annotation.SuppressLint; import android.app.Activity; import andr

Android开发之异步详解(一)Thread+Handler

请尊重他人的劳动成果,转载请注明出处:  Android开发之异步详解(一)Thread+Handler http://blog.csdn.net/fengyuzhengfan/article/details/40211589 在Android实际开发工程中经常会进行一些诸如:文件读写.访问网络等耗时的操作,这些耗时的操作是不建议放到UI线程里的.所以我们会新开一个线程,在子线程中进行这些耗时的操作,耗时操作过程中,UI经常需要更新,但Android是不允许在子线程中修改UI的.所以就出现了Th

Android 开发 之 Fragment 详解

作者 : 韩曙亮 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38064191 1. Fragement 概述 Fragement 与 Activity 生命周期关系 : Fragement 嵌入到 Activity 组件中才可以使用, 其生命周期与 Activity 生命周期相关. -- stop 与 destroy 状态 : Activity 暂停 或者 销毁的时候, 其内部嵌入的所有的 Fragement 也会执行

Android开发之异步详解(二)之AsyncTask

请尊重他人的劳动成果,转载请注明出处:Android开发之异步详解(二)之AsyncTask http://blog.csdn.net/fengyuzhengfan/article/details/40212745 我曾在<Android开发之异步详解(一)之Thread+Handler>一文中介绍过通过Thread+Handler实现异步操作.感兴趣的朋友可以看一下. 虽然Thread+Handler可以实现更新主线程的UI并实现异步,但Thread+Handler模式需要为每一个任务创建一

搭建Android开发环境附图详解+模拟器安装(JDK+Eclipse+SDK+ADT)

——搭建android开发环境的方式有多种,比如:JDK+Eclipse+SDK+ADT或者JDK+Eclipse+捆绑好的AndroidSDK或者Android Studio. Google 决定将重点建设 Android Studio编译工具.Google 在去年年底终止支持其 Eclipse包括插件ADT.(Android Studio会是一个趋势). 这篇将仅作为一个笔记吧,因为之前一直使用Word,记事本记录一些笔记之类.(大神勿喷,如有错误不当的地方,还望指出.谢谢!) 搭建开发环境

【Android开发】InstanceState详解

首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Android开发过程中很少用到,但在有时候掌握其用法会帮我们起到比较好的效果.尤其是在应用程序在不知道的情况下退出后,如何实现其数据保存的功能.先来让我们看下这两个方法的有什么样的作用. 1. 基本作用:  Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 o

Android 开发之动画详解

一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 Java Code代码中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸伸缩动画效果 TranslateAnimation 画面转换位置移动动画效果 RotateAnim

Android开发 DialogFragment对话框详解

前言 在聊DialogFragment之前,我们看看以往我们在Android里实现一个对话框一般有这几种方式: Dialog 继承重写Dialog实现一个自定义的Dialog AlertDialog Android原生提供的对话框(底层是继承Dialog实现) PopupWindow 用弹出悬浮框,实现对话框.这种对话框可以用在指定位置显示,一般用于一些非常小的按键弹窗.怎么实现可以参考我的博客:https://www.cnblogs.com/guanxinjing/p/10156153.htm

Android:ViewPager扩展详解——带有导航的ViewPagerIndicator(附带图片缓存,异步加载图片)

大家都用过viewpager了, github上有对viewpager进行扩展,导航风格更加丰富,这个开源项目是ViewPagerIndicator,很好用,但是例子比较简单,实际用起来要进行很多扩展,比如在fragment里进行图片缓存和图片异步加载. 下面是ViewPagerIndicator源码运行后的效果,大家也都看过了,我多此一举截几张图: 下载源码请点击这里 ===========================================华丽的分割线==============