一、Bitmap 基础与内存
1. BitmapFactory 加载 Bitmap 的常用方法有哪些?各自适用场景?
- decodeFile(String pathName):从本地文件路径加载,适用于本地相册、下载的图片文件。
- decodeResource(Resources res, int id):从应用资源文件(res/drawable等)加载,适用于应用内置图片。
- decodeStream(InputStream is):从输入流加载,适用于网络图片、资产文件(assets)、内容提供者(ContentProvider)获取的图片。
- decodeByteArray(byte[] data, int offset, int length):从字节数组加载,适用于图片数据已读取到内存、需快速解析的场景。
- 核心注意:加载大图片时,需配合 BitmapFactory.Options 进行采样压缩,避免 OOM。
2. Bitmap 的复用机制(inBitmap)是什么?如何实现?
- 概念:
通过复用已分配的 Bitmap 内存空间,避免重复申请内存,减少内存碎片和 GC 频率,提升加载效率。 - 实现规则:
Android 版本限制:
4.4(API 19)及以上:新 Bitmap 的内存大小 ≤ 旧 Bitmap 内存大小,即可复用;
4.4 以下:新 Bitmap 与旧 Bitmap 的宽高、像素格式必须完全一致,才能复用。
配置要求:设置 BitmapFactory.Options 的 inBitmap 属性,赋值为已存在的可复用 Bitmap。
注意事项:复用前需确认旧 Bitmap 未被引用、已释放;复用后旧 Bitmap 不可再使用,避免内存错乱。
3. 安卓中 Bitmap 内存占用如何计算?大图加载 OOM 如何避免,给出完整实战方案。
- 内存计算公式:内存大小 = 宽 × 高 × 每个像素占用字节数
- OOM 完整实战方案:
- 降采样(inSampleSize):按屏幕尺寸计算合适比例,避免加载原图
- 格式优化:无透明场景使用 RGB_565,内存直接减半
- Bitmap 复用(inBitmap):复用旧 Bitmap 内存,避免频繁 GC
- LruCache 内存缓存 + 弱引用池:控制缓存上限
- 大图分片加载:BitmapRegionDecoder 只加载可视区域
- 及时回收:不用的 Bitmap 调用 recycle ()(8.0 以下必须)
4. Glide/Picasso 加载 Bitmap 的内存缓存、磁盘缓存原理,及 Bitmap 复用(inBitmap)的使用场景与限制
- 缓存原理:
内存缓存:LruCache(强引用)+ 弱引用池(用于复用)
磁盘缓存:缓存原始图、裁剪图、转换图 - inBitmap 复用:
4.4+:新 Bitmap 内存 ≤ 旧 Bitmap 即可复用
4.4 以下:宽高、格式必须完全一致
5. 大图长图(如长海报、地图)如何实现高效加载与显示,避免卡顿与 OOM。
- 使用 BitmapRegionDecoder 分片加载
- 监听滑动,动态加载当前可视区域
- 配合 inBitmap 复用 + LruCache 缓存
- 避免一次性加载全图
6. Bitmap 在不同安卓版本(8.0 前后)的内存分配变化,及对应适配方案。
- 8.0 前:Bitmap 内存在 Native 堆 → 必须手动 recycle ()
- 8.0 后:Bitmap 内存在 Java 堆 → GC 自动管理
- 适配:8.0 以下必须 recycle;8.0 以上建议主动释放。
二、Bitmap 绘制与性能(实战应用)
1. Canvas 绘制 Bitmap 时,如何避免过度绘制、提升绘制效率?
- onDraw 中禁止创建 Bitmap/Canvas
- 使用 clipRect 限制绘制区域,减少过度绘制
- 简单绘制开启硬件加速;复杂绘制关闭
- 使用矩阵变换代替多次裁剪
2. 图片圆角、圆形、阴影的实现方式,哪种性能最优?实战中如何选择。
-
使用 ViewOutlineProvider(推荐首选)
适用版本:Android 5.0(API 21)及以上
优点:由系统支持,GPU 渲染,性能最优,适合实现圆形或统一圆角
缺点:不支持阴影,不能设置不同半径的圆角
使用方式:通过 setOutlineProvider() 设置圆形或圆角轮廓 -
使用图片加载库(如 Glide)的 Transformation
优点:集成方便,适合网络图片加载时统一处理
缺点:每次加载都要处理图片,可能增加内存开销
使用方式:Glide.with(context).load(url).transform(new RoundedCorners(16))、 -
使用 Canvas.drawRoundRect() + Paint.setShadowLayer()
优点:支持阴影,适合自定义绘制
缺点:性能一般,绘制复杂时可能影响帧率
使用场景:需要阴影或更复杂的 UI 效果时使用 -
使用 CardView 包裹 ImageView
优点:简单易用,支持圆角和阴影
缺点:性能一般,阴影渲染有一定开销
使用方式:设置 cardCornerRadius 和 elevation -
使用第三方库(如 RoundedImageView、GPUImage)
优点:功能强大,兼容性好
缺点:引入额外依赖,可能增加 APK 体积
面试版本:
- 系统轮廓裁剪 clipToOutline(API21+ 性能天花板)
原理是利用 View 的 Outline 定义绘制边界,系统在渲染阶段直接裁切,全程硬件加速,无离屏渲染、无额外内存分配。
优势:性能最优,代码简洁,适合统一样式的图标、列表头像;
局限:最低支持 Android 5.0,不支持局部差异化圆角。
目前字节、微信、阿里的新业务页面,在版本达标的前提下,都会大规模采用该方案。
- 图片库解码阶段预处理(大厂通用主力方案)
这是各大 App网络图片、头像场景的标准实现。无论是微信深度定制的 Glide、阿里自研 Phenix、字节基于 Fresco 改造的 BDFresco,核心思路完全一致:在 Bitmap 解码完成后、设置给 View 之前,完成圆角 / 圆形变换。
三、动画基础
1. 常见动画分类
-
视图动画(补间动画):对 View 的绘制内容做矩阵变换,不改变 View 的实际属性和位置
-
帧动画: 按顺序播放一系列预定义的图片,像电影胶片一样形成动画效果。本质是 Drawable 的切换。
-
属性动画:通过反射调用对象的 setter 方法,真实修改对象的属性值。动画结束后,对象的状态会永久保留

2. 属性动画的核心原理是什么?与视图动画的本质区别?
- 核心原理:
属性动画通过 ValueAnimator 监听动画进度,计算目标属性的中间值,通过 反射 调用对象的 setXXX 方法修改真实属性;ObjectAnimator 封装了 ValueAnimator,自动反射调用 setXXX,更简洁。 - 本质区别:
视图动画:仅修改 View 的绘制层,不改变 View 的布局参数(如 left、top、width),点击事件仍在原位置;
属性动画:直接修改 View 的真实布局属性和成员变量,View 的位置、大小等实际属性发生变化,点击事件同步更新。
3. 属性动画内存泄漏原因(如持有 View 引用),实战中如何避免。
- 泄漏原因:动画持有 View 强引用,View 销毁但动画未停
- 避免:
onDestroy/onDetached 取消动画
使用弱引用
避免无限循环动画
4. ValueAnmimator与objectAnimator的应用场景
- ValueAnimator 常见使用场景:
自定义 View 动画(如圆形进度条、路径动画)
颜色渐变动画(结合 ArgbEvaluator)
数值型动画(如计数器动画、进度更新)
动态 UI 更新(如动态绘制、Canvas 动画)
- ObjectAnimator 常见使用场景:
View 属性动画(如 translationX, alpha, rotation)
组合动画(配合 AnimatorSet 实现动画顺序控制)
点击反馈动画(如按钮点击缩放、Material 风格动画)
页面转场动画(Fragment/Activity 切换动画)
RecyclerView Item 动画(进入/点击动画)
ValueAnimator:纯数值渐变,自由度高,用于自定义复杂动画、数值变化。
ObjectAnimator:直接操作控件属性,用法简单,普通 UI 动画优先用它。
四. 综合实战题
1. 实现一个图片加载 + 渐显动画 + 滑动销毁的组件,需兼顾 Bitmap 内存、动画性能、用户体验。
- 图片加载:Glide 加载 + RGB_565 + inBitmap
- 渐显动画:ObjectAnimator 控制 alpha
- 滑动销毁:GestureDetector + 属性动画平移 + 边界回弹
- 内存:及时释放、复用、缓存控制
2. 列表(RecyclerView)中图片加载与动画(如入场、刷新)的性能优化,避免卡顿与闪烁。
- 图片:Glide 预加载、复用、降采样
- 动画:使用 ItemAnimator,避免频繁 invalidate
- 防闪烁:固定尺寸、关闭默认动画、使用 holder 缓存
3. 自定义 View 中 Bitmap 绘制 + 属性动画结合的实战,如何保证流畅度(60fps)。
- Bitmap 预创建、复用
- onDraw 无对象创建
- 属性动画使用硬件加速
- 减少绘制区域、避免过度绘制
- 控制 invalidate 频率
4. 结合 Bitmap 与属性动画,实现一个 “图片渐变放大显示” 的实战效果,需注意内存优化。
- Bitmap 加载优化:
使用 BitmapFactory.Options 计算采样率,按 ImageView 尺寸压缩图片;
优先使用 RGB_565 格式(无透明需求时),减少内存占用;
加载完成后,及时回收原始资源,避免内存泄漏。 - 动画实现:
初始状态:设置 ImageView 的 alpha 为 0,scaleX/scaleY 为 0.5;
属性动画组合:使用 AnimatorSet 同时执行 AlphaAnimation(alpha 从 0→1)和 ScaleAnimation(scaleX/scaleY 从 0.5→1),时长 500ms,插值器使用 AccelerateDecelerateInterpolator(加速减速);
动画监听:动画结束后,若不再使用 Bitmap,调用 bitmap.recycle () 释放内存,配合 System.gc () 触发 GC。 - 内存防护:
避免在 onDraw 中创建 Bitmap;
列表场景中,使用 Glide/Picasso 结合 Bitmap 复用池,复用 Bitmap 内存。
5. 如何避免 Bitmap 与动画结合使用时的卡顿与 OOM?
- 内存优化:
强制采样压缩:所有 Bitmap 加载前必须计算 inSampleSize,禁止加载原图;
合理选择格式:无透明图用 RGB_565,内存减半;
及时回收:Activity/Fragment 销毁时,调用 Bitmap.recycle (),并置为 null;
复用池:集成 LruCache 缓存常用 Bitmap,配合 inBitmap 复用内存。 - 动画性能优化:
避免在动画更新中执行耗时操作(如 Bitmap 解码、压缩)
复杂动画开启硬件加速
动画结束后,及时释放动画引用,避免内存泄漏
9086

被折叠的 条评论
为什么被折叠?



