以下是 30个Android面试高频问题 ,覆盖基础原理、组件、性能优化、架构、三方库五大方向,答案简洁准确,贴合面试答题逻辑:
一、 基础原理类(7题)
- 问题:Android的四大组件是什么?它们的作用和生命周期分别是?
-
答案 :四大组件是
Activity(页面载体,与用户交互)、Service(后台服务,无界面)、BroadcastReceiver(广播接收,跨组件通信)、ContentProvider(数据共享,跨应用访问)。-
Activity生命周期:onCreate()→onStart()→onResume()→onPause()→onStop()→onDestroy(); -
Service分两种:startService(生命周期onCreate()→onStartCommand()→onDestroy())、bindService(绑定生命周期onCreate()→onBind()→onUnbind()→onDestroy()); -
BroadcastReceiver:静态广播(Manifest注册,常驻)、动态广播(代码注册,随组件生命周期); -
ContentProvider:核心方法query()/insert()/update()/delete(),通过ContentResolver访问。
-
- 问题:Handler的核心原理是什么?为什么会出现内存泄漏?如何解决?
-
答案 :Handler是Android异步通信工具,基于Looper + MessageQueue + Message实现:
-
原理:①
Looper.prepare()为线程创建唯一MessageQueue;②Handler.sendMessage()将Message加入队列;③Looper.loop()循环取出Message,交给Handler.handleMessage()处理。 -
内存泄漏原因:非静态内部类/匿名内部类的Handler持有外部Activity引用 ,若
Message未处理完,Activity销毁时无法被GC回收。 -
解决方案:① 用静态内部类 + WeakReference 包装Activity;② 页面销毁时调用
handler.removeCallbacksAndMessages(null)清空消息。
-
- 问题:ANR的全称是什么?触发条件有哪些?如何解决和避免?
-
答案 :ANR全称
Application Not Responding(应用无响应),由主线程阻塞超过阈值触发:-
触发条件:① 输入事件(点击/触摸)5秒未响应;②
BroadcastReceiver执行时间超过10秒(前台)/60秒(后台);③Service执行时间超过20秒(前台)/200秒(后台)。 -
解决&避免:① 耗时操作(网络、数据库、文件IO)移到子线程(协程/线程池);② 避免主线程做同步锁竞争;③ 用
StrictMode检测主线程耗时操作;④ 优化布局和启动逻辑,减少主线程负担。
-
- 问题:Android中的Context有哪些类型?它们的区别是什么?
-
答案 :Context有3种核心类型 ,本质都是
ContextImpl的包装:-
Application:全局唯一,生命周期=应用生命周期,适合全局初始化(如三方库初始化); -
Activity:页面级Context,包含窗口信息,生命周期=Activity生命周期,适合加载布局、启动Activity; -
Service:服务级Context,无窗口信息,生命周期=Service生命周期,适合后台操作(如播放音乐)。 -
区别:
Activity可启动Activity、弹出Dialog;Application和Service不行(无窗口);Application不能直接绑定布局。
-
- 问题:Serializable和Parcelable的区别?分别适用于什么场景?
-
答案:两者都是Android序列化接口,用于对象传输(如Intent传值、Binder通信):
|-------|---------------|------------------------|
| 维度 | Serializable | Parcelable |
| 实现方式 | Java原生接口,反射实现 | Android专用接口,手动实现 |
| 性能 | 低(反射+IO操作) | 高(内存直接读写) |
| 适用场景 | 序列化到磁盘(如文件存储) | 内存间传输(如Intent传值) |
| 代码复杂度 | 低(仅需实现接口) | 高(需重写writeToParcel等方法) |- 建议:内存传输用
Parcelable(如Activity间传对象);持久化存储用Serializable。
- 建议:内存传输用
- 问题:说说你对Android中进程和线程的理解?进程优先级是怎样的?
-
答案:
-
进程:Android应用的最小运行单元,每个应用默认一个进程(主进程),可通过
AndroidManifest的android:process指定多进程;进程间相互独立,通过Binder通信。 -
线程:进程内的执行单元,默认主线程(UI线程),负责UI绘制和用户交互;子线程负责耗时操作。
-
进程优先级(从高到低):前台进程 (用户正在交互的Activity)→可见进程 (Activity可见但非前台,如弹窗)→服务进程 (运行startService的Service)→后台进程 (Activity已onStop)→空进程(无组件运行,缓存用)。系统内存不足时,会按优先级从低到高杀进程。
-
- 问题:什么是Binder?它的优势是什么?
-
答案 :Binder是Android的跨进程通信(IPC)机制,也是Service的底层通信方式。
- 优势:① 性能高 :对比Socket(基于TCP/IP,开销大)、AIDL(基于Binder封装),Binder数据拷贝仅1次(传统IPC需2次);② 安全性 :支持UID/PID校验,可识别调用方身份;③ 易用性:通过AIDL可快速实现跨进程通信。
二、 组件与UI类(6题)
- 问题:Activity的启动模式有哪些?分别适用于什么场景?
-
答案 :共4种启动模式,通过
AndroidManifest的android:launchMode设置:-
standard(标准模式):默认,每次启动新建实例,适合大多数页面; -
singleTop(栈顶复用):若实例在栈顶,复用并调用onNewIntent(),否则新建,适合通知栏跳转的页面; -
singleTask(栈内复用):整个Task栈中仅一个实例,复用并清空上方Activity,适合主页面(如首页); -
singleInstance(单实例模式):单独占一个Task栈,全局唯一,适合系统级页面(如电话拨号界面)。
-
- 问题:Fragment的生命周期和Activity的生命周期有什么关联?
-
答案:Fragment依赖于Activity,其生命周期会随Activity生命周期变化,核心关联如下:
-
Activity
onCreate()→ FragmentonAttach()→onCreate()→onCreateView()→onViewCreated(); -
Activity
onStart()→ FragmentonStart(); -
Activity
onResume()→ FragmentonResume(); -
Activity
onPause()→ FragmentonPause(); -
Activity
onStop()→ FragmentonStop(); -
Activity
onDestroy()→ FragmentonDestroyView()→onDestroy()→onDetach()。 -
额外生命周期:
onHiddenChanged()(Fragment隐藏/显示时调用)、onActivityResult()(接收Activity返回结果)。
-
- 问题:Android中实现页面跳转的方式有哪些?
- 答案 :① 显式跳转:指定目标Activity的class,如
Intent(this, TargetActivity::class.java).startActivity();② 隐式跳转:通过Action、Category匹配(如打开系统相册);③ 组件化跳转:通过ARouter(ARouter.getInstance().build("/path").navigation());④ 深链接跳转:通过Scheme(如app://example.com/home),支持从浏览器/其他应用跳转。
- 问题:ListView和RecyclerView的区别?RecyclerView的优势是什么?
-
答案:两者都是列表控件,RecyclerView是ListView的升级版:
|-------|---------------|--------------------------------|
| 维度 | ListView | RecyclerView |
| 布局灵活性 | 仅支持垂直列表 | 支持线性、网格、瀑布流布局(通过LayoutManager) |
| 复用机制 | 复用convertView | 复用ViewHolder,更高效 |
| 动画支持 | 需自定义 | 内置ItemAnimator,支持增删动画 |
| 局部刷新 | 需手动实现 | 支持notifyItemChanged()局部刷新 |- RecyclerView优势:高灵活性、高性能、易扩展,是目前Android列表的首选。
- 问题:如何优化Android的布局?
- 答案 :① 减少布局层级:用
merge标签减少根布局嵌套,用ViewStub延迟加载(如懒加载弹窗布局);② 复用布局:用include标签复用公共布局(如标题栏);③ 避免过度绘制:减少背景重叠(如父布局和子布局都设背景),用tools:ignore="Overdraw"检测;④ 用ConstraintLayout替代线性/相对布局,ConstraintLayout是扁平化布局,性能更高。
- 问题:自定义View的步骤是什么?如何处理触摸事件?
-
答案:
-
自定义View步骤:① 继承
View或ViewGroup;② 重写构造方法(初始化属性);③ 重写onMeasure()(测量View大小);④ 重写onDraw()(绘制View内容);⑤ 重写onLayout()(ViewGroup需重写,确定子View位置)。 -
触摸事件处理:重写
onTouchEvent(MotionEvent event),根据event.getAction()判断触摸动作(ACTION_DOWN/ACTION_MOVE/ACTION_UP);若需处理事件传递,重写dispatchTouchEvent()和onInterceptTouchEvent()(ViewGroup专属)。
-
三、 性能优化类(5题)
- 问题:Android内存泄漏的常见原因有哪些?如何检测和解决?
-
答案:
-
常见原因:① 静态变量持有Activity引用;② 非静态内部类(如Handler、Thread)持有外部类引用;③ 未取消的监听器(如
View.setOnClickListener、广播接收器);④ 资源未关闭(如Cursor、IO流、Bitmap)。 -
检测工具:LeakCanary(自动检测并生成泄漏报告)、Android Studio Profiler(Memory Profiler查看内存快照)。
-
解决方案:① 静态变量用弱引用;② 内部类改为静态内部类;③ 页面销毁时取消监听器、关闭资源;④ 及时回收Bitmap(
bitmap.recycle())。
-
- 问题:如何优化Android应用的启动速度?
-
答案 :启动分为冷启动 (应用完全关闭后启动)、热启动(应用在后台,重新唤醒),优化重点在冷启动:
-
减少启动任务:① 延迟初始化三方库(如非必要的SDK,用
lazy或子线程初始化);② 避免在Application.onCreate()和Activity.onCreate()做耗时操作; -
布局优化:① 用
ViewStub延迟加载非首屏布局;② 减少布局层级,用ConstraintLayout; -
其他优化:① 启用
R8/ProGuard代码混淆和压缩;② 用SplashScreen替代传统启动页(Android 12+);③ 避免启动时做网络请求,可延后到首屏加载完成。
-
- 问题:Bitmap的内存优化方法有哪些?
- 答案 :Bitmap是内存占用大户,优化方法:① 采样压缩:通过
BitmapFactory.Options的inSampleSize缩小图片分辨率(如inSampleSize=2,宽高各缩小为1/2,内存缩小为1/4);② 格式优化:用RGB_565替代ARGB_8888(内存减少一半);③ 及时回收:页面销毁时调用bitmap.recycle(),并置为null;④ 用图片加载库:Glide/Picasso自动处理Bitmap缓存和回收,避免手动管理。
- 问题:什么是过度绘制?如何减少过度绘制?
-
答案 :过度绘制是指同一个像素被多次绘制(如父布局和子布局都设置背景),会增加GPU负担,降低性能。
-
检测:Android Studio → Profiler → GPU Profiler → 开启"Overdraw"检测,屏幕颜色越红,过度绘制越严重。
-
减少方法:① 移除不必要的背景(如Activity默认背景和布局背景重复);② 避免重叠的半透明控件;③ 用
Canvas.clipRect()裁剪不需要绘制的区域。
-
- 问题:如何解决Android的ANR问题?
- 答案 :① 定位ANR原因:查看
data/anr/traces.txt文件(记录ANR发生时的线程堆栈);② 优化主线程:耗时操作移到子线程(协程/线程池);③ 避免主线程同步锁:如主线程和子线程同时竞争一个锁,可能导致主线程阻塞;④ 优化广播和服务:避免在BroadcastReceiver和Service中做耗时操作;⑤ 监控ANR:接入第三方监控工具(如Bugly),及时发现线上ANR问题。
四、 架构与Jetpack类(5题)
- 问题:MVC、MVP、MVVM的区别?分别适用于什么场景?
-
答案 :三者都是Android的架构模式,核心是解耦:
-
MVC(Model-View-Controller):① Model(数据层)、View(UI层,Activity/Fragment)、Controller(控制层,Activity/Fragment);② 缺点:View和Controller耦合严重(Activity既是View又是Controller),适合小型项目。
-
MVP(Model-View-Presenter):① Presenter作为中间层,连接Model和View;② 优点:View和Model完全解耦,Presenter可测试;③ 缺点:Presenter持有View引用,需手动管理生命周期,适合中型项目。
-
MVVM (Model-View-ViewModel):① ViewModel作为中间层,通过LiveData和View双向绑定;② 优点:数据驱动UI,无需手动更新UI,ViewModel生命周期长于Activity,自动处理配置变更(如屏幕旋转);③ 缺点:学习成本高,适合大型项目。目前主流架构是MVVM + Jetpack。
-
- 问题:Jetpack的核心组件有哪些?分别有什么作用?
-
答案:Jetpack是Google推出的Android组件库,简化开发,核心组件:
-
ViewModel:存储页面数据,生命周期独立于Activity,解决屏幕旋转数据丢失问题; -
LiveData:可观察的数据持有类,感知生命周期,仅在页面活跃时更新UI,避免内存泄漏; -
Room:SQLite的ORM封装,编译期校验SQL语句,支持协程; -
DataBinding:实现View和ViewModel双向绑定,数据驱动UI; -
Lifecycle:感知组件生命周期,避免手动管理(如Handler、Timer)。
-
- 问题:ViewModel的生命周期是怎样的?它如何解决屏幕旋转数据丢失的问题?
-
答案:
-
ViewModel生命周期:从Activity
onCreate()创建,到Activity 完全销毁 (onDestroy()且非配置变更)时销毁;若屏幕旋转(配置变更),Activity重建,但ViewModel实例会保留。 -
解决数据丢失原理:屏幕旋转时,系统会销毁旧Activity并新建,但ViewModel会被系统缓存,新Activity可获取到旧ViewModel实例,从而保留数据。
-
- 问题:LiveData的优势是什么?如何实现数据倒灌?
-
答案:
-
LiveData优势:① 生命周期感知 :仅在页面活跃(
STARTED/RESUMED)时通知观察者;② 自动取消订阅 :页面销毁时自动移除观察者,避免内存泄漏;③ 数据粘性:新观察者会收到之前发送的数据。 -
数据倒灌:指新注册的观察者收到历史数据的现象。解决方法:① 自定义
SingleLiveEvent(仅发送一次数据);② 用Flow替代LiveData(Flow无粘性)。
-
- 问题:什么是依赖注入?Dagger2/Hilt的作用是什么?
-
答案:
-
依赖注入(DI):是一种设计模式,目的是解耦 ,将类的依赖对象创建交给外部容器,而非类内部
new创建。 -
Dagger2:基于注解的依赖注入框架,编译期生成依赖注入代码,无反射,性能高;
-
Hilt:Google基于Dagger2封装的框架,简化Dagger2的配置,与Jetpack组件深度集成(如
@ViewModelInject),是目前Android依赖注入的首选。
-
五、 三方库与实战类(7题)
- 问题:Retrofit的核心原理是什么?
-
答案 :Retrofit是类型安全的HTTP客户端 ,核心是动态代理 + OkHttp封装:
- ① 定义接口并添加注解(如
@GET/@POST);② Retrofit通过动态代理生成接口的实现类;③ 解析注解中的请求信息(URL、参数、请求方式),转换为OkHttp的Request对象;④ 由OkHttp执行网络请求;⑤ 通过ConverterFactory将响应数据转换为Java/Kotlin对象(如Gson解析)。
- ① 定义接口并添加注解(如
- 问题:Glide的缓存策略是怎样的?
-
答案 :Glide有两级缓存:内存缓存 + 磁盘缓存,加载图片时优先查内存缓存,再查磁盘缓存,最后从网络加载:
-
内存缓存 :基于
LruCache实现,缓存近期使用的图片,页面销毁时自动释放; -
磁盘缓存 :默认缓存适配后的图片 (
RESULT模式),也可配置缓存原始图片(SOURCE模式);缓存Key由URL、尺寸、参数等生成,保证唯一性。
-
- 问题:为什么推荐用MMKV替代SharedPreferences?
-
答案:
-
SharedPreferences缺点:① 基于XML存储,读写全量加载(改一个字段需读取整个XML);② 不支持多进程;③ 异步提交(
apply())可能导致ANR; -
MMKV优势:① 基于
mmap内存映射,读写性能提升10倍以上;② 支持多进程;③ 增量更新(仅修改变化字段);④ 无需手动提交,自动持久化。
-
- 问题:RxJava的核心思想是什么?它的线程切换是如何实现的?
-
答案:
-
核心思想:响应式编程 ,基于观察者模式,将异步操作转换为可观察的数据流(
Observable),通过操作符(map/flatMap/filter)处理数据,最后由观察者(Observer)订阅并接收数据。 -
线程切换:通过
subscribeOn()和observeOn()实现:①subscribeOn()指定数据流发射的线程 (仅第一次调用有效);②observeOn()指定观察者接收数据的线程(可多次调用);底层通过线程池实现线程切换。
-
- 问题:Kotlin协程和RxJava的区别?如何选择?
-
答案:
|--------|---------------------------|-------------------------------------|
| 维度 | RxJava | Kotlin协程 |
| 语法复杂度 | 高(需创建Observable/Observer) | 低(同步代码写法实现异步) |
| 性能 | 较低(对象创建多) | 高(轻量级线程,无额外开销) |
| 生命周期管理 | 需手动管理(如RxLifecycle) | 自动管理(viewModelScope/lifecycleScope) |
| 学习成本 | 高(操作符多) | 低(接近原生语法) |- 选择原则:纯Kotlin项目优先用协程(官方推荐);混合Java/Kotlin项目,若已有RxJava体系,可继续使用,新功能建议用协程。
- 问题:ARouter的核心作用是什么?如何实现组件间通信?
-
答案:
-
核心作用:解决组件化开发中的页面跳转、接口调用、参数传递问题,实现组件解耦。
-
组件间通信方式:① 页面跳转:通过
@Route注解标记Activity,调用ARouter.getInstance().build("/path").navigation();② 接口调用:定义公共接口,实现类用@Route注解,调用方通过ARouter.getInstance().navigation(接口类::class.java)获取实例。
-
- 问题:项目中如何处理网络请求的异常(如token过期、网络错误)?
-
答案:
-
① 统一异常拦截:通过OkHttp的
Interceptor拦截响应,若返回401(token过期),则同步刷新token,重新构建请求并执行; -
② 异常分类处理:将异常分为网络错误(无网络、超时)、业务错误(token过期、参数错误)、解析错误,分别给出提示;
-
③ 全局异常捕获:用
try-catch捕获协程/Retrofit的异常,通过Toast或弹窗提示用户; -
④ 重试机制:对网络超时等异常,通过
retryWhen()操作符(RxJava)或retry函数(协程)实现重试。
-
总结
以上30个问题覆盖了Android面试的核心考点,回答时需先讲原理,再讲场景,最后讲解决方案,突出实战经验。