【Android面试】动画 & Bitmap

文章目录

一、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 解码、压缩)
    复杂动画开启硬件加速
    动画结束后,及时释放动画引用,避免内存泄漏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值