Android 性能调优相关

1. 谈谈代码混淆的步骤?

  1. 开启混淆 :在模块的 build.gradle 中设置 minifyEnabled true,并指定混淆文件:

    groovy 复制代码
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  2. 编写混淆规则proguard-rules.pro):

    • 保留四大组件、自定义 View、实体类(用于 JSON 反序列化需保留无参构造器和字段名)。
    • 保留通过反射、注解、JNI、序列化访问的类或方法。
    • 引入第三方库专属混淆规则(通常库文档会提供 -keep 指令)。
  3. 测试:对混淆后的包进行充分测试,避免 ClassNotFoundExceptionNoSuchMethodError

  4. 打包 :混淆器自动进行代码压缩、类名/方法名短化、配合 shrinkResources true 压缩资源、移除无效代码。

  5. 保存映射文件 :每次发布保存 build/outputs/mapping/ 下的 mapping.txt,记录了混淆前的类名、方法名、字段和行号的对应关系,用于线上堆栈还原(崩溃日志)。

  6. 作用:减小 APK 体积、加固防破解、删除无用代码。


2. 谈谈怎么给 apk 瘦身?

  • 代码混淆:开启 ProGuard / R8,移除无用代码。
  • 资源压缩shrinkResources true,移除未引用资源。
  • 图片优化:使用 WebP、矢量图(VectorDrawable)、降低 PNG 质量。
  • 避免重复库:使用 gradleexclude 去重。
  • 动态下载:部分模块插件化或 AAB(Android App Bundle)按需分发。
  • 移除不必要的语言资源:resConfigs "zh"

3. 谈谈如何对网络请求进行优化?

  • 减少请求次数:批量接口、合并请求。
  • 数据压缩:Gzip 压缩请求体 / 响应体。
  • 使用缓存 :HTTP 缓存(Cache-Control)、本地磁盘缓存。
  • 连接复用 :使用 HTTP/2、OkHttp 连接池。
  • 弱网优化:设置合理超时、重试策略、断点续传。
  • DNS 优化:HttpDNS 防劫持、减少 DNS 解析时间。

4. 谈谈 App 的电量优化?

  • 减少网络请求:合并请求、使用 GCM/FCM 推送。
  • 后台任务 :避免长时间后台运行,使用 JobScheduler 进行批量处理。
  • 减少唤醒次数:使用 AlarmManager 时选非精确(setInexactRepeating),使用 JobScheduler / WorkManager
  • 定位优化:按需请求位置,使用被动定位(requestLocationUpdates 合理设置间隔)。
  • Sensor 使用:及时注销传感器监听。
  • WakeLock:及时释放,使用 acquire / release 配对。

5. 谈谈你是如何优化 App 启动过程的?

  • 异步初始化Application 中的第三方库延迟或异步初始化,使用 IdleHandler
  • 减少主线程任务:将非立即需要的初始化移到子线程。
  • 视觉优化 :设置主题页(windowBackground)避免白屏。
  • 优化布局:首屏布局简单,避免嵌套过深。
  • 预加载WebView 预创建、数据预取。
  • 减小 Dex 体积:开启混淆、MultiDex 优化。

6. 谈谈如何对 WebView 进行优化?

  • 预初始化:提前创建 WebView 并复用。
  • 加载优化 :开启硬件加速、合理设置缓存策略(setCacheMode 网页缓存、资源缓存、离线缓存)。
  • 资源拦截:本地替换 JS/CSS,使用 shouldInterceptRequest
  • 进程独立:将 WebView 置于单独进程,避免内存泄漏影响主进程。
  • 销毁时:onDestroyremoveAllViewsdestroy()
  • JS 注入优化:延迟注入或按需注入。

7. Android Native Crash 问题如何分析定位?

  • 获取日志logcat 抓取 crash 日志,关注 Fatal signal 11 (SIGSEGV) 等。
  • tombstone 文件/data/tombstones/ 下有崩溃堆栈。
  • addr2line :用 ndk-stackaddr2line 解析 so 文件中的地址。
  • Breakpad / Google Breakpad:捕获 Native 崩溃生成 minidump 文件分析。
  • 常见原因:空指针、内存越界、释放后使用、栈溢出、动态库加载失败。

8. 谈谈你对 Android 性能优化方面的了解?

  • 内存优化 :避免内存泄漏(静态引用、Handler、匿名内部类)、对象复用、使用 LruCache及时回收 Bitmap。
    • LruCache:内存缓存工具类,基于 LRU(Least Recently Used,最近最少使用)算法,优先淘汰最近就少使用的对象
  • 启动优化 :减少 Application 和首屏 onCreate 任务、异步加载、优化主题页。
  • 卡顿优化 :避免主线程耗时操作、使用 TraceView / Systrace 分析。
  • 网络优化:合并请求、数据压缩、使用缓存、HttpDNS。
  • 安装包瘦身:混淆、资源压缩、WebP、移除无用资源。
  • 绘制优化onDraw 中避免创建对象、频繁调用 invalidate
  • 布局优化 :减少层级(ConstraintLayout)、避免过度绘制、使用 <merge> / <ViewStub>

9 简要说说 LruCache 的原理?

  • LRU 全称:最近最少使用算法,优先淘汰长时间未使用的数据。
  • 底层: 基于 LinkedHashMap,开启访问有序模式。
  • 核心机制:
    • 每次 get/put 访问元素,会将当前元素移至队列头部;
    • 长期不访问的元素会逐步挤到队列尾部;
    • 缓存达到设定阈值时,自动移除尾部最少使用元素
  • 优点: 可控内存上限、自动淘汰、防止缓存无限膨胀、减少 OOM。
  • 适用: 图片内存缓存、高频本地缓存场景。

一句话记忆:LruCache 基于 LinkedHashMap 的访问顺序,自动淘汰最久未使用的条目。


10 为什么推荐用 SparseArray 代替 HashMap?

  • Key 类型SparseArray 的 Key 为 int 基本类型,无需自动装箱(intInteger),节省内存和 CPU。
  • 内存结构 :底层采用数组存储 ,内存占用更小;
    • HashMap 是数组 + 链表 / 红黑树,结构冗余、内存开销大。
  • 查找效率SparseArray 使用二分查找 (O(log n)),小数据量下速度更快。
    • HashMap 使用哈希表(O(1) 平均)。数据量较小时,二分查找开销可接受且更省内存。
  • 延迟删除机制: 删除时先标记,不立即移动数组,减少拷贝开销。
  • 适用场景数据量小(几百以内)Key 为 int(如映射 View ID 到 View)。

一句话记忆:SparseArray 省内存、免装箱,适合 Key 为 int 的小数据量场景。


11. 谈谈 Android 中内存优化的方式?

  • 避免内存泄漏 :静态内部类 + 弱引用(非静态内部类、匿名类持有外部引用)、及时注销监听、onDestroy 中清空 Handler。
  • Bitmap 优化inSampleSize 缩放、inBitmap 复用、RGB_565 格式。
  • 对象池 :复用对象(如 Message.obtain)。
  • 使用数据缓存LruCacheDiskLruCache
  • 使用 SparseArray / ArrayMap 替代 HashMap 节省内存。

12. 哪些情况下会导致 OOM 问题?

  • 加载过大图片:未缩放直接加载到内存。
  • 内存泄漏:Activity 未释放,大量对象无法回收。
  • 创建大量对象 :循环中频繁 new 对象。
  • 内存碎片:频繁分配释放导致大块连续内存不足。
  • 长生命周期集合:静态 List / Map 不断添加数据。
  • 超大数组:一次性加载大量数据到内存。
  • Android 版本差异:低版本单个进程内存上限更小。

13. 自定义 Handler 时如何有效地避免内存泄漏问题?

  • 原因:非静态内部类 Handler 隐式持有外部 Activity 引用,延迟消息导致 Activity 无法回收。

  • 解决方案

    1. 使用静态内部类 + 弱引用持有 Activity。
    2. onStop / onDestroy 中调用 handler.removeCallbacksAndMessages(null) 移除所有消息。
  • 示例

    java 复制代码
    static class MyHandler extends Handler {
        WeakReference<Activity> ref;
        MyHandler(Activity activity) { ref = new WeakReference<>(activity); }
        @Override void handleMessage(Message msg) {
            Activity act = ref.get();
            if (act != null) { /* 处理 */ }
        }
    }

14. Bitmap 的内存优化方法有哪些/如何避免 Bitmap 的 OOM 问题?

  • 按需加载: 图片压缩(inSampleSize 降低宽高)、缩略图、局部加载、大图分块加载(只解码显示区域);
  • 合理格式: 选用高效格式 WebP、HEIF,减少内存占用;
  • 解码配置inPreferredConfigRGB_565(16位)代替 ARGB_8888(32位)降低内存。
  • 使用缓存:三级缓存(内存缓存、磁盘缓存、网络);控制缓存上限;(Glide/Coil )
  • 复用内存inBitmap 复用已分配的内存,避免重新分配;
  • 及时回收recycle() 释放 native 内存(Android 8.0 后内部自动管理,但建议仍调用);

15. 什么是 ANR?触发条件?如何避免?

  • ANR:Application Not Responding,应用无响应,主线程阻塞超时(Activity 5 秒、BroadcastReceiver 10 秒、Service 20 秒)。
  • 触发条件
    • 输入事件(按键/触摸)5 秒内未响应
    • 广播接收者(BroadcastReceiver)10 秒内未执行完
    • 服务(Service)20 秒内未启动完成
    • ContentProvider 发布超时
  • 常见场景
    • 主线程执行网络请求、IO 读写、大文件操作
    • 主线程执行复杂计算(循环、加密)
    • 主线程执行数据库大量操作
    • 线程死锁导致主线程等待
  • 避免方法
    • 不在主线程做耗时操作(网络、IO、复杂计算),使用子线程处理耗时任务(Thread、协程);
    • 避免在主线程中使用锁;
    • 主线程只做 UI 刷新,轻量逻辑;
    • 优化布局,避免过度绘制、频繁的 requestLayout
    • 使用 HandlerThreadIntentService 处理后台任务。
    • 使用 StrictMode 检测主线程中的潜在阻塞操作。

16. 如何计算一张图片所占的内存空间大小?

图片内存大小 = 总像素个数 × 单个像素所占字节

  • 总像素 = 图片宽 × 图片高

  • 像素位深举例:

    • ARGB_8888:每个像素 4 字节
    • RGB_565:每个像素 2 字节
    • ARGB_4444:每个像素 2 字节

补充要点:

  1. 计算的是加载进内存的像素内存,不是磁盘文件大小;
  2. inSampleSize 采样压缩后,宽高同时缩小倍数,内存成倍降低;
  3. Android 图片内存为 Native 内存,容易引发 OOM。

17. WebP 和 SVG 特点 & Android 使用方案

WebP 特点:

  1. 高效压缩,体积远小于 JPG/PNG,无损 / 有损都支持;
  2. 支持透明、动图,画质相近体积更小;
  3. 安卓高版本原生完美兼容,减少图片包体积。

使用场景: 图标、商品图、背景图、常规位图,替代 PNG/JPG

SVG 特点:

  1. 矢量图,放大缩小不失真
  2. 体积极小,纯路径绘制,无像素概念;
  3. 不适合复杂大图、色彩丰富图片。

使用场景: 纯色图标、按钮 ICON、简单矢量标识。

开发使用:

  1. WebP:直接放置资源目录,系统原生加载、Glide 完美支持;
  2. SVG:AndroidStudio 导入矢量图,使用 VectorDrawable 渲染。

18. 对于 GIF 图片加载有什么思路和建议?

  1. 使用成熟框架: 优先 Glide、Coil 原生支持 GIF 解析播放,不手写解码。
  2. 格式替代: 简单动图优先用 Lottie 矢量动画,替代笨重 GIF,性能更好。
  3. 内存限制: 限制 GIF 分辨率、帧率,避免逐帧大图占用大量内存。
  4. 资源回收: 页面销毁及时释放 GIF 解码器、停止动画、清空引用。
  5. 按需播放: 页面不可见、切后台时暂停 GIF、减少耗电与内存占用。
  6. 避免频繁循环: 非必要场景限制播放次数,降低 CPU 消耗。

19. MVP 中如何处理 Presenter 防止内存泄漏?

  1. View 层弱引用: Presenter 持有 View 时,使用 WeakReference 包裹,避免 Activity/Fragment 被强引用。
  2. 生命周期解绑: 页面销毁 onDestroy/ onDestroyView 中,主动解绑 View,置空引用。
  3. 终止异步任务: Presenter 中网络请求、协程、回调,页面销毁时统一取消、终止。
  4. 杜绝静态持有: Presenter 不要使用静态变量持有页面、View 实例。
  5. 接口隔离: 通过 View 接口通信,减少强耦合,方便统一回收销毁。

总结:Presenter 弱引用持有 View,页面生命周期及时解绑、清空异步任务,杜绝长引用绑定。

相关推荐
头发够用的程序员13 小时前
从滑动窗口到矩阵运算:img2col算法基本原理
人工智能·算法·yolo·性能优化·矩阵·边缘计算·jetson
刘~浪地球19 小时前
系统性能瓶颈分析与优化
性能优化
电商API_180079052471 天前
获取淘宝商品原价、券后价的区别在哪里?难度以及解决办法
数据库·性能优化·数据挖掘·数据分析·网络爬虫
comerzhang6552 天前
别再只看 Long Task 了:页面卡顿到底是 React、Layout,还是 V8 GC?
性能优化·next.js
腹黑天蝎座2 天前
前端性能优化实战指南:从原理到落地的全方位解决方案
前端·性能优化·监控
Ulyanov2 天前
《PySide6 GUI开发指南:QML核心与实践》 第八篇:性能优化大师——QML应用性能调优实战
python·qt·ui·性能优化·qml·系统仿真
悟空瞎说2 天前
前端性能优化进阶指南:从底层原理到工程化闭环
面试·性能优化
南村群童欺我老无力.2 天前
鸿蒙动画系统的常见陷阱与性能优化
华为·性能优化·harmonyos
Mr_pyx2 天前
【告别for循环】Java Stream 流式编程精通:从入门到源码级的性能优化
java·开发语言·性能优化