当前位置: 首页 > ds >正文

Android面试指南(五)

目录

一、Android异常

1.1、ANR

1.1.1、什么是ANR

1.1.2、ANR的主要原因

1.1.3、主线程的操作

1.1.4、ANR的解决方案

1.2、OOM

1.2.1、什么是OOM

1.2.2、易混淆概念

1.2.3、如何解决OOM

二、性能优化

2.1、Bitmap

2.2、UI卡顿

2.2.1、UI卡顿原理

2.2.2、UI卡顿原因分析

2.2.3、卡顿优化总结

2.3、内存泄漏

2.3.1、Java内存泄漏

2.3.2、Android内存泄漏

2.4、内存管理

2.4.1、内存管理机制概述

2.4.2、安卓内存管理机制

2.4.3、内存管理机制的特点

2.4.4、内存优化

2.4.5、内存溢出与内存泄漏

2.5、冷启动优化

2.5.1、什么是冷启动

2.5.2、冷启动流程

2.5.3、冷启动时间优化

2.6、其它优化

一、Android异常

1.1、ANR

1.1.1、什么是ANR

ANR(Application Not Responding)是安卓系统在应用程序未及时响应时触发的无响应对话框。默认情况下,Activity的最长响应时间为5秒,BroadcastReceiver的最长响应时间为10秒。若超时未完成操作,系统将强制弹出ANR对话框,影响用户体验。ANR的本质是主线程被耗时操作阻塞。以下条件都可以造成ANR发生:

  • InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
  • BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
  • Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
  • ContentProvider Timeout :ContentProvider的publish在10s内没进行完。

1.1.2、ANR的主要原因

ANR产生的核心原因可归纳为以下两点:

  • 主线程执行耗时I/O操作:包括网络请求、文件读写等,Android - 0后禁止主线程网络操作。
  • 主线程存在其他阻塞操作:如复杂计算或线程等待(Thread.sleep())。

根本原因均为主线程无法及时处理UI事件。解决方案需依赖Handler机制将耗时任务转移至子线程,主线程仅处理UI更新。

1.1.3、主线程的操作

  • Activity的所有生命周期回调方法
  • Service的默认执行线程(需使用IntentService处理耗时任务)
  • BroadcastReceiver的onReceive()方法
  • 未关联子线程Looper的Handler回调(HandlerMessage/postRunnable)
  • AsyncTask的回调方法(除doInBackground外)

1.1.4、ANR的解决方案

ANR的解决方案包括:

  • 使用AsyncTask处理I/O或网络请求
  • 通过Thread或HandlerThread提升子线程优先级(避免与主线程同级)
  • 利用Handler实现工作线程与主线程通信
  • 避免在Activity的onCreate/onResume中执行耗时操作

1.2、OOM

1.2.1、什么是OOM

OOM(Out of Memory)异常指当前占用的内存加上申请的内存资源超过了Dalvik虚拟机设定的最大内存限制。安卓系统为每个APP分配独立的工作区间(Dalvik虚拟机空间),若内存占用超过限制,系统会抛出OOM错误。常见场景为加载大尺寸Bitmap时触发。

1.2.2、易混淆概念

①、内存溢出

内存溢出即OOM,表现为内存占用超过虚拟机最大限制,导致程序崩溃。

②、内存抖动

内存抖动由短时间内大量对象创建并快速释放引发,频繁触发GC(垃圾回收),导致堆内存压力增大。

③、内存泄漏

内存泄漏指无用对象仍被GC Roots引用,导致无法回收。长期积累可能引发OOM。三者中OOM最严重,直接影响程序稳定性。

1.2.3、如何解决OOM

OOM解决主要分为两类:Bitmap优化和非Bitmap优化

①、Bitmap

图片显示

  • 加载图片时需匹配显示需求,例如列表滑动时暂停加载大图,仅显示缩略图。

及时释放内存

  • Bitmap占用JAVA和C两部分内存,需主动调用recycle()释放C层内存,避免累积占用。

图片压缩

  • 大图加载前需计算缩放比例(inSampleSize),减少内存占用。

inBitmap属性

  • inBitmap属性复用已分配内存区域,减少重复申请,提升解码效率。

捕获异常

  • 捕获OOM需捕获Error而非Exception,因OOM属于错误类型。

②、其它

以ListView为例:ListView需使用convertView复用机制,结合LRU缓存Bitmap。避免在getView()中频繁创建对象,防止内存抖动。多进程技术可扩展内存范围,但需谨慎使用以避免逻辑复杂性。

还有注意避免由内存泄漏引发的问题,避免内存泄漏的产生,比如:单例对象的Context问题、非静态内部类持有外部类引用的问题、广播未反注册问题等,平时开发中可以集成LeakCanary自动捕获。

二、性能优化

 推荐阅读:Android性能优化专栏

2.1、Bitmap

①、recycle方法

recycle方法用于释放Bitmap占用的内存,包括Java堆内存和Native内存。在Android3.0之前,Bitmap像素数据与对象一起存放在堆中,回收时仅需清理堆内存;而3.0之后,Bitmap直接存储在Native内存,需调用recycle()方法主动释放。

源码分析:recycle()会释放Native内存并清理数据对象引用,但并非立即执行,而是向垃圾回收器发送指令,待无其他引用时触发回收。调用后,Bitmap标记为dead状态,再调用其方法(如getPixels()或setPixels())会引发异常。

注意事项:

  • 操作不可逆,需确保Bitmap后续不再使用。
  • 官方建议优先依赖垃圾回收器自动管理,仅在特定场景主动调用。

②、LRU

LRU(最近最少使用)算法通过LinkedHashMap实现缓存管理,核心逻辑如下:

  • 数据结构:使用final LinkedHashMap存储强引用缓存对象。
  • 核心方法:
    • trimToSize():当缓存满时,移除最久未使用的对象并添加新对象。内部通过循环判断当前尺寸是否超过最大值,调用remove()方法逐项清理。
    • put():添加缓存对象时同步计算尺寸,若超限则触发trimToSize()。
    • remove():删除指定缓存对象并更新尺寸,支持自定义逻辑的空方法entryRemoved()。
  • 面试要点:LRU通过LinkedHashMap的访问顺序特性实现缓存淘汰策略,核心是维护访问时序与尺寸控制。

③、计算inSampleSize

inSampleSize用于计算Bitmap加载时的缩放比例,避免直接加载大图导致OOM。实现步骤:

  • 获取原始宽高,默认比例设为1。
  • 比较目标尺寸与原始尺寸,取宽高缩放比例的最小值作为最终inSampleSize。
  • 关键参数:通过BitmapFactory.Options.inJustDecodeBounds设为true可仅读取图片尺寸而不加载像素数据,优化计算效率。

④、缩略图

缩略图生成依赖inSampleSize的缩放比例,具体流程:

  • 设置BitmapFactory.Options.inJustDecodeBounds=true,仅解码图片边界信息。
  • 根据计算出的inSampleSize调整目标尺寸。
  • 将inJustDecodeBounds设为false,按比例加载缩略图至内存。

⑤、三级缓存

三级缓存结构(网络→本地→内存)优化图片加载效率:

  • 首次请求:从网络获取图片,并保存至本地(SD卡)和内存缓存。
  • 重复请求:优先检查内存缓存,未命中则查询本地缓存,最后回退至网络。
  • 核心价值:减少重复网络请求,提升加载速度并节省用户流量。

2.2、UI卡顿

2.2.1、UI卡顿原理

UI卡顿的核心原理与60 FPS和16毫秒的渲染机制相关。

①、FPS

  • 60 FPS是安卓系统规定的流畅帧率标准,即每秒60帧,换算为每帧16毫秒的渲染时间窗口。
  • 若程序操作未在16毫秒内完成,会导致丢帧现象,引发卡顿。
  • 卡顿常见原因包括:复杂布局嵌套、过多背景叠加、动画执行次数过多等造成的CPU/GPU负载过重。
  • GC操作会暂停所有线程,若在16毫秒渲染窗口内发生大量GC,会导致渲染超时,进而引发卡顿。

②、overdraw

  • 过度绘制指同一像素在同一帧时间内被多次绘制,常见于多层UI结构或设置invisible属性的视图。
  • 开发者可通过手机GPU调试工具观察过渡绘制情况,优化目标是减少深红色区域,优先呈现蓝色区域。
  • overdraw主因是UI布局中存在非必要重叠背景,例如Activity、Layout及子View均设置独立背景时,移除冗余背景可显著降低过渡绘制。

总结:UI卡顿本质是渲染性能不足,具体表现为:

  • 布局复杂度超标导致16毫秒内无法完成渲染。
  • 资源重叠或冗余引发过渡绘制。
  • 动画或GC操作占用过多系统资源。

2.2.2、UI卡顿原因分析

①、UI线程轻微耗时操作:UI线程执行轻微耗时操作(如轻量级计算)会阻塞渲染,需通过工作线程异步处理避免卡顿。

②、布局过于复杂:嵌套过深或结构复杂的布局会延长测量(measure)与摆放(layout)时间,需在需求评审阶段与UI设计师协同简化。

③、动画执行次数过多:高频动画会加剧CPU/GPU负载,需权衡视觉效果与性能消耗。

④、过度绘制:冗余背景或视图重叠导致像素多次绘制,需通过代码优化减少无效渲染。

⑤、频繁触发measure、layout:频繁调用measure()/layout()会触发视图树重新渲染,可通过自定义View替代复杂嵌套布局以降低开销。

⑥、内存频繁GC:内存抖动引发GC会中断渲染线程,需优化对象分配策略。

⑦、冗余资源及逻辑等导致加载和执行缓慢:低效代码逻辑或冗余资源加载会延迟渲染,需通过代码重构提升执行效率。

⑧、ANR产生:主线程耗时操作直接触发ANR,需严格遵循异步任务规范。

2.2.3、卡顿优化总结

①、布局优化

  • 使用include/merge/ViewStub标签减少嵌套。
  • 优先用GONE替代INVISIBLE以避免无效绘制。
  • 采用相对位置替代固定宽高以简化计算。
  • 复杂布局建议改用自定义View。

②、列表及Adapter优化

  • 复用getView()中的实例,避免重复创建。
  • 列表滑动时暂停数据加载,仅显示缩略图或默认值。

③、背景和图片等内存分配优化

  • 移除冗余背景,压缩图片资源。
  • 控制GC触发时机,避免渲染窗口内回收内存。

④、避免ANR

  • 耗时操作必须通过子线程处理,推荐使用HandlerThread、IntentService等异步框架。

2.3、内存泄漏

2.3.1、Java内存泄漏

JAVA内存泄漏指未被释放的对象因被实例持有而无法被垃圾回收。

①、Java内存分配策略

JAVA内存分配策略分为三类:

  • 静态存储区:存放静态数据和全局变量,内存分配在编译时完成,变量生命周期与程序一致。
  • 栈区:存储方法体内的局部变量,方法结束后自动释放,效率高但容量有限。
  • 堆区:存储通过new创建的对象,由垃圾回收器管理内存释放。

栈区与堆区的区别:

  • 栈区存储基本类型变量和对象引用变量,作用域结束后内存自动释放。
  • 堆区存储对象实例和数组,内存由垃圾回收器回收。

②、Java是如何管理内存的

JAVA内存管理的核心是对象分配与释放:

  • 对象通过new关键字在堆中分配内存。
  • 内存释放由垃圾回收器(GC)自动完成。

GC工作原理:

  • 监控对象引用关系,将对象视为有向图中的顶点。
  • 根顶点(如main方法)可达的对象为有效对象,不可达对象可被回收。

③、Java中的内存泄漏

JAVA内存泄漏定义为无用对象持续占用内存且无法释放,导致内存浪费。满足以下条件即视为内存泄漏:

  • 对象在引用图中可达。
  • 对象已无实际用途。
  • GC无法回收该对象。

后果:内存泄漏积累可能导致内存溢出(OOM)。

2.3.2、Android内存泄漏

①、单例

单例模式因生命周期与应用一致,若持有Activity的引用会导致Activity无法回收。正确写法应使用Application Context而非Activity Context。

②、匿名内部类

非静态内部类默认持有外部类引用,若创建静态实例会导致外部类无法回收。

解决方案:将内部类声明为static。

③、Handler

Handler因持有Activity引用且消息队列未处理完,会导致Activity无法回收。解决方案:

  • 将Handler改为静态内部类。
  • 使用弱引用持有外部类。

④、避免使用static变量

静态变量生命周期与应用一致,可能导致内存占用过高。优化建议:

  • 避免静态变量初始化时加载。
  • 对必需静态变量进行生命周期管理。

⑤、资源未关闭造成的内存泄漏

未关闭资源(如广播、游标、流等)会导致内存泄漏。解决方法:在Activity销毁时主动关闭或注销资源。

⑥、AsyncTask造成的内存泄漏

AsyncTask因非静态内部类持有外部类引用,需在onDestroy中调用cancel终止任务。

补充:

  • Bitmap需调用recycle释放C层内存。
  • 集合类需及时清理无用引用。

2.4、内存管理

2.4.1、内存管理机制概述

  • 内存是操作系统调度的数据存储区域,现代多进程操作系统中内存管理至关重要。
  • 分配机制:操作系统为每个进程分配合理内存大小,确保进程正常运行。
  • 回收机制:系统内存不足时,通过杀死占用内存的进程回收资源,并将副作用降到最低。
  • 安卓系统内存管理与PC端不同,移动设备内存资源更少,需更谨慎管理。

2.4.2、安卓内存管理机制

安卓内存管理机制分为分配机制和回收机制两部分。

①、分配机制

  • 弹性分配方式:系统初始为APP分配小额内存,根据设备物理尺寸动态调整。
  • 分配上限:额外内存分配有限制,系统最大限度让更多进程存活于内存中,减少应用启动时间。

②、回收机制

  • 进程优先级分类:
    • 前台进程:屏幕显示的进程。
    • 可见进程:用户仍可见的进程。
    • 服务进程:运行服务的进程(如定位、推送)。
    • 后台进程:后台计算的进程。
    • 空进程:无运行内容的进程,可随时回收。
  • 回收原则:优先级越低,被杀死概率越高;前台、可见及服务进程通常不会被杀死。
  • 回收效率:系统倾向于杀死能回收更多内存的进程,减少对用户体验的影响。

2.4.3、内存管理机制的特点

  • 更小的项目内存占用:提升用户体验。
  • 合理释放系统资源:避免频繁释放导致内存抖动。
  • 内存紧张时释放非重要资源:为系统提供可用内存。
  • 保存重要数据:便于APP重启时直接复用。

2.4.4、内存优化

①、Service

  • 推荐方案:使用IntentService替代普通Service
  • 优势:自动创建子线程处理耗时任务,执行完毕自动退出

②、UI资源释放

通过onTrimMemory()回调在UI不可见时释放资源

③、回收非重要资源

根据内存紧张等级释放不同级别资源。

④、Bitmap优化

  • 压缩Bitmap:根据设备分辨率调整。
  • 及时回收Bitmap:调用recycle方法或使用软引用缓存。

⑤、数据结构优化

  • 推荐SparseArray替代HashMap:减少内存占用。
  • 避免使用枚举:内存消耗较高,内存消耗是常量两倍多。

⑥、框架选择

慎用ButterKnife等框架,依赖注入框架会有额外开销,扫描注解消耗系统资源。

⑦、APK优化

使用zipalign工具压缩资源,减少运行时内存占用

⑧、多进程策略

  • 分离高内存模块:如定位、推送或WebView进程。
  • 注意数据传输与安全问题:多进程需谨慎使用。

2.4.5、内存溢出与内存泄漏

  • 内存溢出(OOM):常见于未压缩Bitmap,解决方法包括压缩图片、使用inBitmap属性等。
  • 内存泄露:对象未被GC回收,通常因错误引用(如非静态内部类持有外部类引用)。
  • 检测工具:LeakCanary或MAT分析内存映像文件。

2.5、冷启动优化

2.5.1、什么是冷启动

①、冷启动定义

冷启动指应用启动前系统中不存在该应用的任何进程信息(包括Activity、Service等)。典型场景包括:

  • 设备开机后首次启动应用
  • 应用进程被杀死后重新启动
  • 此类场景下应用启动时间最长,需完成全部初始化工作。

②、冷启动和热启动的区别

对比维度

冷启动

热启动

定义

系统需创建新进程

直接复用后台现有进程

初始化流程

需创建Application类及MainActivity

仅需创建MainActivity

性能消耗

高(需完整初始化)

低(跳过Application初始化)

关键差异:冷启动会触发Application类的构造方法及onCreate(),而热启动直接进入Activity生命周期。

举例说明:在MyApplication的onCreate()中设置3秒延时(Thread.sleep(3000))

  • 冷启动:首次启动出现3秒白屏(执行Application初始化)
  • 热启动:点击Home键返回后重新进入,无白屏(跳过Application初始化)

③、冷启动时间的计算

  • 计算范围:从应用启动(创建进程)开始,到完成视图第一次绘制(Activity内容对用户可见)为止
  • 测量方法:Android 4.4+通过logcat自动打印启动时间

2.5.2、冷启动流程

冷启动流程分为四个阶段:

  • 进程创建:系统从Zygote进程fork新应用进程
  • 组件初始化:依次创建Application类及MainActivity
  • 布局加载:执行inflate布局操作
  • 界面渲染:通过measure、layout、draw三阶段显示ContentView

总结:完整冷启动调用链如下:

  • Application构造方法 → attachBaseContext() → onCreate()
  • Activity生命周期:onCreate() → onStart() → onResume()
  • 最终通过视图测量、布局、绘制完成界面渲染

2.5.3、冷启动时间优化

冷启动优化核心策略包括:

  • 减少初始化工作:将第三方SDK改为懒加载(需要时再初始化),权衡懒加载可能带来的运行时延迟
  • 避免业务操作:Application只初始化全局必需数据,不放置业务模块相关数据初始化
  • 避免耗时操作:禁止在Application中进行IO操作(如读取文件/SD卡)
  • 避免静态变量:不在Application中使用静态变量保存数据(防止内存泄漏)
  • 优化布局:减少布局复杂性和层级深度,使用ViewStub实现按需加载
  • 异步初始化:将资源初始化延迟或放入子线程执行

2.6、其它优化

①、不使用静态变量存储数据

  • 静态变量存储数据存在风险:安卓应用进程不稳定,可能被系统回收,导致静态变量数据丢失。
  • 进程回收机制:进程被终止后,系统会重新创建Application对象并恢复用户离开时的Activity,造成应用未被终止的假象,但静态变量数据已重置。
  • 替代方案:推荐使用文件、SharedPreferences或ContentProvider传递数据,若通过Intent传递参数,需对变量进行非空检查以避免空指针异常。

②、SharedPreferences安全问题

问题类型

具体表现

影响

跨进程同步问题

多进程读写时,各进程维护独立副本,无法实时同步数据。

数据不一致,需应用结束后才持久化修改。

文件过大问题

存储大量键值对时,主线程可能阻塞,引发UI卡顿;解析时产生临时对象,导致频繁GC。

内存抖动、内存泄漏,甚至OOM;高频访问的Key与低频Key混存影响性能。

使用建议

避免跨进程读写;控制文件大小;分离高频与低频Key。

确保数据安全性与性能优化。

③、内存对象的序列化

  • 序列化定义:将对象状态转换为可存储或传输的形式。
  • 安卓序列化方式:
    • Serializable:Java标准方式,但生成临时变量易引发频繁GC,影响性能。
    • Parcelable:安卓特有方式,内存操作性能更优,但不支持磁盘存储且底层变更可能导致数据不可读。
  • 选择建议:进程间通信优先用Parcelable;需持久化数据时改用Serializable。

④、避免在UI线程中做繁重的操作

  • UI线程限制:耗时操作(如网络请求、数据库读写)会导致动画延迟和界面卡顿。
  • 优化方法:使用异步框架(如AsyncTask、HandlerThread)或工具(StrictMode)追踪卡顿点。
  • 设备性能瓶颈:移动端并发IO处理能力有限,即使应用空闲,其他应用的IO操作仍可能引发ANR或网络延迟。
  • 核心原则:UI线程仅处理核心UI逻辑,耗时任务必须异步执行。

OK,关于更多其它的优化还是推荐阅读:Android性能优化专栏的内容,那今天的内容就这么多了,下期再会!

http://www.xdnf.cn/news/18793.html

相关文章:

  • 青少年机器人技术(二级)等级考试试卷-实操题(2024年9月)
  • C语言文件操作精讲:从格式化读写到随机访问
  • GOLANG 接口
  • Axure:如何打开自定义操作界面
  • loj数列分块入门2-3
  • c++string
  • crypto.randomUUID is not a function
  • 拓扑排序|hash
  • frp+go-mmproxy 实现透明代理的内网穿透
  • Qt5 高级功能
  • 关于说明锂电池充电芯片实际应用
  • 曲面方程的三维可视化:从数学解析到Python实现
  • 从罗永浩访谈李想中学习现代家庭教育智慧
  • 定时器互补PWM输出和死区
  • 54.Redis持久化-AOF
  • JEI(Journal of Electronic lmaging)SCI四区期刊
  • 控制建模matlab练习16:线性状态反馈控制器-⑤轨迹追踪
  • Linux内核进程管理子系统有什么第三十三回 —— 进程主结构详解(29)
  • 【KO】前端面试四
  • Java八股文-java基础面试题
  • 9.Shell脚本修炼手册---数值计算实践
  • 使用tensorRT10部署yolov5目标检测模型(2)
  • UE5.3 中键盘按键和操作绑定
  • 青少年机器人技术(六级)等级考试试卷-实操题(2021年12月)
  • 深入理解3x3矩阵
  • 11.Shell脚本修炼手册---IF 条件语句的知识与实践
  • 【数据结构】布隆过滤器的概率模型详解及其 C 代码实现
  • mysql没有mvcc之前遇到了什么问题
  • 2025年AI Agent规模化落地:企业级市场年增超60%,重构商业作业流程新路径
  • Hive中的join优化