安卓Bitmap基础

在这里插入图片描述


参考博客:https://juejin.cn/post/6844903641032163336

1.Bitmap加载

1.Bitmap基础

在这里插入图片描述
说明

ARGB_8888:ARGB分别代表的是透明度,红色,绿色,蓝色,每个值分别用 8bit 来记录,也就是一个像素会占用4byte,共32bit.

ARGB_4444:ARGB的是每个值分别用4bit来记录,一个像素会占用2byte,共16bit.

RGB_565:R=5bit,G=6bit,B=5bit,不存在透明度,每个像素会占用2byte,共16bit.

ALPHA_8:该像素只保存透明度,会占用1byte,共8bit.

在实际应用中而言,建议使用ARGB_8888以及RGB_565。 如果你不需要透明度,选择RGB_565,可以减少一半的内存占用.

加载 Bitmap 的四个静态方法:

BitmapFactory类提供了以下四个静态方法用来以不同的“原料”生产一个Bitmap对象:

  • decodeByteArray(byte[] data, int offset, int length): 把一个byte数组从offset开始的length个字节解析为一个Bitmap对象
  • decodeFile(String pathName): 把pathName指定的文件解析成一个Bitmap对象
  • decodeFileDescriptor(FileDescriptor fd): 把描述符fd指定的文件解析为一个Bitmap对象
  • decodeResource(Resources res, int id, Bitmap.Options options): 根据id从给定的资源中解析出一个Bitmap对象,加载这个对象到内存中时应用options指定的选项
  • decodeStream(InputStream is): 从给定的流中解析出一个Bitmap对象
    Options参数
 public static class Options {
    public Options() {
        inDither = false;
        inScaled = true;
        inPremultiplied = true;
     }
     ...      
    public Bitmap inBitmap; //用于实现Bitmap的复用,下面会具体介绍
    public int inSampleSize;  //采样率 
    public boolean inPremultiplied;   
    public boolean inDither;  //是否开启抖动
    public int inDensity; //即上文我们提到的inDensity
    public int inTargetDensity;  //目标屏幕密度,同上文提到的含义相同            
    public boolean inScaled;    //是否支持缩放
    public int outWidth;   //图片的原始宽度
    public int outHeight;  //图片的原始高度
    ...
 }

2、Bitmap如何处理大图,如一张30M的大图,如何预防OOM?

参考回答

避免OOM的问题就需要对大图片的加载进行管理,主要通过缩放来减小图片的内存占用。

  • BitmapFactory提供的加载图片的四类方法(decodeFile、decodeResource、decodeStream、decodeByteArray)都支持BitmapFactory.Options参数,通过inSampleSize参数就可以很方便地对一个图片进行采样缩放
  • 比如一张10241024的高清图片来说。那么它占有的内存为102410244,即4MB,如果inSampleSize为2,那么采样后的图片占用内存只有512512*4,即1MB(注意:根据最新的官方文档指出,inSampleSize的取值应该总是为2的指数,即1、2、4、8等等,如果外界输入不足为2的指数,系统也会默认选择最接近2的指数代替,比如2
  • 综合考虑。通过采样率即可有效加载图片,流程如下
    • 将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片
    • 从BitmapFactory.Options中取出图片的原始宽高信息,它们对应outWidth和outHeight参数
    • 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize
    • 将BitmapFactory.Options的inJustDecodeBounds参数设为false,重新加载图片
      在这里插入图片描述

3、加载一张500*500的png高清图片,占用多少的内存?

参考回答:

  • 不考虑屏幕比的话:占用内存=500 * 500 * 4 = 1000000B ≈ 0.95MB
  • 考虑屏幕比的的话:占用内存= 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一个像素所占的内存字节大小
  • inDensity表示目标图片的dpi(放在哪个资源文件夹下),inTargetDensity表示目标屏幕的dpi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GChNXEoz-1635754600399)(../../pic/169957db5956a922)]
博客: https://juejin.cn/post/6844903575634575367
在这里插入图片描述

4、Bitmap使用需要注意哪些问题 ?

参考回答:

  • 要选择合适的图片规格(bitmap类型),通常我们优化Bitmap时,当需要做性能优化或者防止OOM,我们通常会使用RGB_565,因为ALPHA_8只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多。:

    • ALPHA_8 每个像素占用1byte内存
    • ARGB_4444 每个像素占用2byte内存
    • ARGB_8888 每个像素占用4byte内存(默认)
    • RGB_565 每个像素占用2byte内存
  • 降低采样率:BitmapFactory.Options 参数inSampleSize的使用,先把options.inJustDecodeBounds设为true,只是去读取图片的大小,在拿到图片的大小之后和要显示的大小做比较通过calculateInSampleSize()函数计算inSampleSize的具体值,得到值之后。options.inJustDecodeBounds设为false读图片资源。

  • 复用内存:即通过软引用(内存不够的时候才会回收掉),复用内存块,不需要再重新给这个bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。

  • 使用recycle()方法及时回收内存

  • 压缩图片

5、Bitmap.recycle()会立即回收么?什么时候会回收?如果没有地方使用这个Bitmap,为什么垃圾回收不会直接回收?

参考回答:

  • 通过源码可以了解到,加载Bitmap到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java部分的,一部分是C部分的。这个Bitmap对象是由Java部分分配的,不用的时候系统就会自动回收了
  • 但是那个对应的C可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用recycle()方法来释放C部分的内存
  • bitmap.recycle()方法用于回收该Bitmap所占用的内存,接着将bitmap置空,最后使用System.gc()调用一下系统的垃圾回收器进行回收,调用System.gc()并不能保证立即开始进行回收过程,而只是为了加快回收的到来。

6、一张Bitmap所占内存以及内存占用的计算

参考回答:

  • Bitamp 所占内存大小 = 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一个像素所占的内存字节大小
    • 注:这里inDensity表示目标图片的dpi(放在哪个资源文件夹下),inTargetDensity表示目标屏幕的dpi,所以你可以发现inDensity和inTargetDensity会对Bitmap的宽高进行拉伸,进而改变Bitmap占用内存的大小。
  • 在Bitmap里有两个获取内存占用大小的方法。
    • getByteCount():API12 加入,代表存储 Bitmap 的像素需要的最少内存。
    • getAllocationByteCount():API19 加入,代表在内存中为 Bitmap 分配的内存大小,代替了 getByteCount() 方法。
    • 不复用 Bitmap 时,getByteCount() 和 getAllocationByteCount 返回的结果是一样的。在通过复用 Bitmap 来解码图片时,那么 getByteCount() 表示新解码图片占用内存的大 小,getAllocationByteCount() 表示被复用 Bitmap 真实占用的内存大小

7、Android中缓存更新策略 ?

参考回答:

  • Android的缓存更新策略没有统一的标准,一般来说,缓存策略主要包含缓存的添加、获取和删除这三类操作,但不管是内存缓存还是存储设备缓存,它们的缓存容量是有限制的,因此删除一些旧缓存并添加新缓存,如何定义缓存的新旧这就是一种策略,不同的策略就对应着不同的缓存算法
  • 比如可以简单地根据文件的最后修改时间来定义缓存的新旧,当缓存满时就将最后修改时间较早的缓存移除,这就是一种缓存算法,但不算很完美

8、LRU的原理 ?

参考回答:

  • 为减少流量消耗,可采用缓存策略。常用的缓存算法是LRU(Least Recently Used):当缓存满时, 会优先淘汰那些近期最少使用的缓存对象。主要是两种方式:
  • LruCache(内存缓存):LruCache类是一个线程安全的泛型类:内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,并提供get和put方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
  • DiskLruCache(磁盘缓存): 通过将缓存对象写入文件系统从而实现缓存效果

2.内存相关

1、内存泄露和内存溢出的区别 ?AS有什么工具可以检测内存泄露

参考回答:

  • 内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
  • 内存泄露(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!
  • 查找内存泄漏可以使用Android Studio 自带的AndroidProfiler工具或MAT

2、性能优化,怎么保证应用启动不卡顿? 黑白屏怎么处理?

参考回答:

  • 应用启动速度,取决于你在application里面时候做了什么事情,比如你集成了很多sdk,并且sdk的init操作都需要在主线程里实现所以会有卡顿的感觉。在非必要的情况下可以把加载延后或则开启子线程处理

  • 另外,影响界面卡顿的两大因素,分别是 界面绘制和数据处理。

    • 布局优化(使用include,merge标签,复杂布局推荐使用ConstraintLayout等)
    • onCreate() 中不执行耗时操作 把页面显示的 View 细分一下,放在 AsyncTask 里逐步显示,用 Handler 更好。这样用户的看到的就是有层次有步骤的一个个的 View 的展示,不会是先看到一个黑屏,然后一下显示所有 View。最好做成动画,效果更自然。
    • 利用多线程的目的就是尽可能的减少 onCreate() 和 onReume() 的时间,使得用户能尽快看到页面,操作页面。
    • 减少主线程阻塞时间。
    • 提高 Adapter 和 AdapterView 的效率。
  • 黑白屏产生原因
    当我们在启动一个应用时,系统会去检查是否已经存在这样一个进程,如果不存在,系统的服务会先检查startActivity中的intent的信息,然后在去创建进程,最后启动Acitivy,即冷启动。而启动出现白黑屏的问题,就是在这段时间内产生的。系统在绘制页面加载布局之前,首先会初始化窗口(Window),而在进行这一步操作时,系统会根据我们设置的Theme来指定它的Theme 主题颜色,我们在Style中的设置就决定了显示的是白屏还是黑屏。

    • windowIsTranslucent和windowNoTitle,将这两个属性都设置成true (会有明显的卡顿体验,不推荐)
    • 如果启动页只是是一张图片,那么为启动页专一设置一个新的主题,设置主题的android:windowBackground属性为启动页背景图即可
    • 使用layer-list制作一张图片launcher_layer.xml,将其设置为启动页专一主题的背景,并将其设置为启动页布局的背景。
  • 推荐文章:[Android启动页解决攻略](

3、强引用置为null,会不会被回收?

参考回答:

  • 不会立即释放对象占用的内存。 如果对象的引用被置为null,只是断开了当前线程栈帧中对该对象的引用关系,而 垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点(safe point)或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 finalize方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值