Android - app - 面试题, 架构 (mvvm 常见架构区别,livedata/databinding)四大组件 acitivity(两种启动方式区别应用场景例子,四种启动模式区别应用场景 ,java版flag模拟实现四种启动模式例子,常用10种 FLAG 区别与应用场景例子,taskAffinity应用场景及例子,acitivity传参(Intent/BundleParcelable/Serializable 区别应用场景,传大数据单例共享缓存 /ViewModel/LiveData/EventBus/ 文件 / 数据 / 共享内存等方式区别应用场景例子),Frament(概述,打开,跳转等例子,场景问题),acitivity 和 Fragment (生命周期),其他组件 (ViewPager,ScrollView,RecyclerView,surfaceview 等组件常见问题及解决方法),布局 (相对布局,约束布局,动态布局等区别应用场景常见问题),view 刷新 (常见问题),自定义 view (流程,常见问题),事件 (常见问题),图片 (例子,常见问题,为啥要手动回收),弹窗 (toast,dialog,dialogFragment, 悬浮窗等区别应用场景例子,常见问题及解决方法),弹窗类型(windowType概述例子,常见问题),handler (原理,主线程和子线程实现例子,复用例子,常见问题及解决方法),倒计时器(例子),多线程 (多线程,线程安全,线程休眠等各种实现方式区别应用场景例子常见问题),数据持久化 (sp,sqllite各种实现方式区别应用场景例子常见问题),aidl (原理,和socket区别,客户端监听服务端die和服务端监听客户端die例子,常见问题及解决方法),网络 (常见问题),gc (内存架构,gc原理,gc日志,常见问题),adb 常见调试命令(看cpu,mem,io,window,audio,wifi等),性能优化大全(ui,后台,内存,cpu ,io,binder,启动耗时,丢帧,卡顿 等分析解决方法), 稳定性分析优化 大全(anr/java和native的crash/内存泄漏等分析解决方法),单例 (静态内部类优点),设计模式 (区别应用场景),还有其他问题
Android中高级APP面试题库(完整版‑Java,含考点+代码+坑点)
一、架构 MVC‑MVP‑MVVM、LiveData、DataBinding
1、三种架构对比
MVC
- M‑Model(数据层)、V‑View(布局)、C‑Controller,一般由Activity/Fragment充当控制器
- 缺点:Activity业务逻辑与UI代码高度耦合、代码臃肿,单元测试困难
- 适用场景:简单老旧项目,新项目不推荐
MVP
- M‑Model;V‑View接口,Activity实现;P‑Presenter中间人
- 缺点:Presenter双向持有View引用,页面销毁极易内存泄漏,接口数量爆炸
- 适用场景:遗留老项目维护,逐步迁移MVVM
MVVM(Jetpack主流)
- M‑数据源;V‑Activity/Fragment;VM‑ViewModel
- ViewModel不持有View强引用,依靠LiveData实现数据驱动UI、单向数据流
- 优点:UI与业务解耦、屏幕旋转数据自动保留、生命周期安全、便于单元测试
- 适用场景:绝大多数新项目
2、LiveData
- 生命周期感知:宿主处于
STARTED/RESUMED活跃状态才下发数据;页面销毁自动解绑,天然防泄漏 - 黏性数据:新观察者可收到历史缓存数据;事件场景会重复回调;解决方案:
SingleLiveEvent、一次性Event包装类 - 默认主线程回调;不能跨进程通信
3、DataBinding
编译期自动生成绑定类,实现xml布局绑定Java对象
- 单向绑定:
@{data} - 双向绑定:
@={data}>
LiveData = 可观察数据容器;DataBinding = 视图绑定工具;二者经常搭配使用,互不替代
坑点:双向绑定循环调用、空指针、layout标签编译报错
二、Activity
1、两种启动方式
- 显式启动:直接指定目标Activity类
Intent intent=new Intent(this,TestActivity.class); startActivity(intent);场景:APP内部页面跳转,优先推荐
2.隐式启动:通过action、category、data匹配目标页面,不写类名
Intent intent=new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("https://www.baidu.com")); startActivity(intent);场景:跳转系统页面、调用第三方APP;需要配置清单intent‑filter
2、四种启动模式
| 启动模式 | 行为 | 典型业务场景 |
|---|---|---|
| standard | 每次打开都新建实例,任务栈跟随启动方 | 商品详情、资讯页面 |
| singleTop | 栈顶已有目标Activity就复用,回调onNewIntent(),不新建 | 首页,防止双击重复打开页面 |
| singleTask | 栈内存在实例,则清除它上层所有Activity,目标页面回到栈顶,回调onNewIntent() | 登录页、主页;登录成功清栈回到首页 |
| singleInstance | 全局唯一实例,独占一个独立任务栈 | 来电界面、闹钟弹窗 |
⚠️ FLAG无法完全模拟singleInstance,只能在AndroidManifest清单配置。
3、FLAG模拟启动模式代码示例
//1、模拟 singleTop Intent intent=new Intent(this,TestActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(intent); //2、模拟 singleTask intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); //3、模拟 singleInstance(无法100%复现,仅新开任务栈) intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent);singleInstance 只能清单配置
android:launchMode="singleInstance"
4、10个高频FLAG详解
- FLAG_ACTIVITY_NEW_TASK:开启新任务栈;子线程启动Activity必须添加该标记
- FLAG_ACTIVITY_CLEAR_TOP:清除目标页面上方所有Activity实例
- FLAG_ACTIVITY_SINGLE_TOP:栈顶复用
- FLAG_ACTIVITY_NO_HISTORY:页面不入任务栈,返回键直接跳过该页面
- FLAG_ACTIVITY_CLEAR_TASK:清空整个任务栈;必须配合NEW_TASK一起使用
- FLAG_ACTIVITY_REORDER_TO_FRONT:栈内已有页面挪至栈顶,不清上层页面
- FLAG_ACTIVITY_FORWARD_RESULT:传递Activity返回结果给下一页
- FLAG_ACTIVITY_PREVIOUS_IS_TOP:标记上一个Activity作为栈顶
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:页面不在最近任务列表显示
- FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY:从最近任务列表唤起页面
5、taskAffinity
android:taskAffinity="com.xxx.task"- 默认值等于应用包名;生效条件:
singleTask+FLAG_ACTIVITY_NEW_TASK - 使用例子:推送跳转首页,返回键不回到APP首页;独立业务进程页面
- 坑点:会开启全新任务栈,返回栈逻辑混乱
6、Parcelable vs Serializable
- Serializable:Java原生序列化;性能较差;适合持久化存储少量数据
- Parcelable:Android内存序列化;速度快;仅用于Intent内存传递,不能存磁盘>
Binder缓冲区上限1MB,大数据抛出
TransactionTooLargeException
7、大数据传递方案对比
| 方案 | 优点 | 缺点 | 适用场景例子 |
|---|---|---|---|
| 单例静态缓存 | 速度快,代码简单 | 易内存泄漏,进程重启丢失 | 临时传递大图对象 |
| ViewModel+LiveData | 生命周期安全无泄漏 | 仅限同一个Activity内部 | Activity‑Fragment通信 |
| EventBus | 跨组件解耦 | 无生命周期感知,易泄漏 | 全局小事件通知 |
| 本地临时文件 | 无大小上限 | IO开销,用完需要手动清理 | 视频、大图文件传递 |
| 共享内存‑NDK | 零拷贝高性能 | 开发复杂、需要JNI | 跨进程音视频数据流 |
三、Fragment
1、概述
Fragment依附于Activity,拥有独立生命周期;模块化UI组件,一个Activity可以承载多个Fragment。
2、打开添加例子
xml静态添加
<fragment android:id="@+id/fragment" android:name="com.demo.HomeFragment"/>代码动态添加(推荐)
HomeFragment fragment=new HomeFragment(); getSupportFragmentManager().beginTransaction() .add(R.id.container,fragment,"home") .commit();3、跳转切换例子
replace:销毁重建Fragment;适合低频页面切换
getSupportFragmentManager().beginTransaction() .replace(R.id.container,new MineFragment()).commit();add‑hide‑show:实例常驻内存;底部导航首选,保存页面状态
transaction.hide(homeFragment).show(mineFragment).commit();addToBackStack(null):加入回退栈;返回键回到上一个Fragment
4、高频场景坑点
commit()在onSaveInstanceState后调用崩溃 →commitAllowingStateLoss()(允许丢失状态)- 屏幕旋转Fragment重叠:判断
savedInstanceState==null才新建Fragment getActivity()空指针:异步回调Fragment已经解绑宿主;使用getContext()判空setUserVisibleHint()已废弃;新版懒加载方案使用setMaxLifecycle
四、Activity & Fragment生命周期
Activity完整生命周期
onCreate→onStart→onResume(前台交互) →onPause→onStop→onDestroy
后台切回前台重启路径:onRestart→onStart→onResume
默认配置屏幕旋转,Activity销毁重建
Fragment完整生命周期
onAttach→onCreate→onCreateView→onViewCreated→onStart→onResume
→onPause→onStop→onDestroyView→onDestroy→onDetach
重点考点:
hide/show不会触发onPause/onResume
五、UI组件:ViewPager、ScrollView、RecyclerView、SurfaceView
RecyclerView
1.卡顿:onBindViewHolder耗时、大图加载;解决:异步加载图片、DiffUtil局部刷新
2.ScrollView嵌套RecyclerView高度wrap‑content失效、滑动冲突
3.notifyDataSetChanged()全局刷新造成item闪烁 → DiffUtil局部刷新
4.嵌套RecyclerView卡顿:recyclerView.setNestedScrollingEnabled(false)
ScrollView
只能包含一个直接子View;嵌套列表极易出现滑动冲突
ViewPager / ViewPager2
‑ViewPager:默认预加载左右两侧Fragment;懒加载难以控制
‑ViewPager2:底层RecyclerView实现,真正懒加载,支持横竖双向滑动;新项目优先选用
SurfaceView
-原理:拥有独立绘图缓冲区;子线程绘制画面,不会阻塞UI主线程
-场景例子:视频播放器、相机预览、游戏画面、车载视频画面
SurfaceView sv=findViewById(R.id.sv); sv.getHolder().addCallback(new SurfaceHolder.Callback(){ @Override public void surfaceCreated(SurfaceHolder holder) { //开启绘制子线程 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {} @Override public void surfaceDestroyed(SurfaceHolder holder) { //停止绘制线程,释放资源 } });坑点:Surface销毁绘制线程未停止造成死循环崩溃;锁屏切后台Surface被销毁;实现透明效果需要setZOrderOnTop(true)
六、布局:相对布局、约束布局、动态布局
- RelativeLayout相对布局:两次测量,性能较差;新项目逐步淘汰;适合简单控件依赖布局
- ConstraintLayout约束布局:扁平化单层布局,减少布局嵌套;复杂页面首选,性能优秀
- 动态布局addView:
LayoutParams类型必须和父布局一致,否则布局失效
七、View刷新机制
invalidate():主线程调用;仅执行onDraw,不走onMeasure、onLayoutrequestLayout():触发完整测量‑布局‑绘制onMeasure‑onLayout‑onDraw;宽高、位置变化调用postInvalidate():子线程刷新View
坑点:频繁调用invalidate引发过度绘制;View还未添加到Window刷新无效
八、自定义View
绘制流程
- 构造函数:xml布局加载走第二个构造
onMeasure:测量控件宽高onLayout:摆放子View(仅ViewGroup)onDraw:Canvas绘制图形onTouchEvent:接收触摸事件
###常见坑
- wrap_content不生效,需要手动测量宽高
- padding失效:绘制内容留出padding边距
- 动画未cancel,页面销毁内存泄漏
- 部分Canvas绘图API不支持硬件加速
九、事件分发
传递顺序:Activity.dispatchTouchEvent→ViewGroup.dispatchTouchEvent→onInterceptTouchEvent(拦截)→子View.dispatchTouchEvent→onTouchEvent
‑return true:消费事件,终止事件向下传递
‑return false:事件向上冒泡
滑动冲突两种解决方案:
1.外部拦截:父ViewGroup重写onInterceptTouchEvent
2.内部拦截:子View调用requestDisallowInterceptTouchEvent(true)禁止父布局拦截事件
十、图片‑Bitmap
-Android 8.0以前像素数据放在Native堆;8.0+像素数据放到Java堆
‑recycle()作用:主动释放Native内存;Glide等图片框架自动管理Bitmap,不要手动recycle;自己new创建的Bitmap建议手动回收,防止Native内存泄漏
‑OOM解决方案例子:采样压缩、inBitmap内存复用、三级缓存
十一、弹窗:Toast、Dialog、DialogFragment、悬浮窗
1.Toast:系统弹窗;Android11后后台不能弹出Toast;连续弹出排队延时
2.Dialog:依附Activity;Activity销毁未dismiss弹窗引发崩溃;生命周期不安全
3.DialogFragment:弹窗首选方案;屏幕旋转弹窗不会消失,生命周期安全
4.悬浮窗WindowManager.addView:需要悬浮窗权限;Android8.0后窗口类型强制改为TYPE_APPLICATION_OVERLAY
WindowType窗口类型
‑TYPE_APPLICATION:普通APP窗口,依附Activity
‑TYPE_APPLICATION_DIALOG:对话框窗口
‑TYPE_APPLICATION_OVERLAY:新版悬浮窗
坑点:Activity销毁弹窗Token失效,直接崩溃
十二、Handler
###原理四大组件
Handler、Message、MessageQueue、Looper
‑Looper:线程唯一;loop()死循环阻塞读取消息
‑MessageQueue:消息队列,存放Message
‑Handler:发送消息、处理回调消息
###主线程示例
Handler handler=new Handler(Looper.getMainLooper()); handler.post(()->{ //更新UI操作 });###子线程创建Looper完整例子
new Thread(){ @Override public void run() { Looper.prepare(); Handler handler=new Handler(Looper.myLooper()); Looper.loop(); } }.start(); Thread mThread = new Thread(); mThread.start(); Handler mHandler=new Handler(mThread.myLooper());###防内存泄漏写法:静态Handler+弱引用
static class MyHandler extends Handler{ WeakReference<MainActivity> ref; MyHandler(MainActivity activity){ ref=new WeakReference<>(activity); } @Override public void handleMessage(@NonNull Message msg) { MainActivity activity=ref.get(); if(activity!=null){ //业务代码 } } }坑点:非静态Handler持有Activity,延时消息引发内存泄漏;子线程默认没有Looper,不能直接new Handler
十三、CountDownTimer倒计时例子
new CountDownTimer(5000,1000){ @Override public void onTick(long millisUntilFinished) { Log.d("cd","剩余:"+millisUntilFinished/1000); } @Override public void onFinish() { Log.d("cd","倒计时结束"); } }.start();坑点:后台计时不准;页面销毁调用cancel()防止内存泄漏
十四、多线程、线程安全、线程休眠
###多线程实现方式对比
1.Thread/Runnable:简单一次性任务;频繁创建销毁开销大
2.ThreadPoolExecutor线程池:线程复用;并发任务首选方案
3.AsyncTask:已经废弃,极易产生生命周期泄漏
4.Handler+postDelayed:主线程延时任务
5.RxJava:链式异步任务
6.Kotlin协程:轻量级异步方案,新项目推荐
###线程休眠代码例子
try{ Thread.sleep(2000); //休眠2秒;**不会释放锁** }catch (InterruptedException e){ e.printStackTrace(); }sleep()‑Thread静态方法;wait()‑Object方法,调用wait释放锁
###线程安全方案对比
1.synchronized:悲观锁;保证原子性、可见性、有序性;阻塞,性能较差
2.volatile:仅保证可见性,不保证原子性;适合布尔标记位场景
3.ReentrantLock显式锁;支持公平/非公平锁;手动加锁解锁
4.AtomicXXX原子类:CAS无锁;适合简单计数;高并发场景存在ABA问题
###常见坑
嵌套锁顺序不一致引发死锁;子线程更新UI直接崩溃;线程未关闭造成内存泄漏
十五、数据持久化‑SP、SQLite、Room
1.SharedPreferences:轻量xml键‑值存储;apply()异步提交;commit()同步阻塞主线程;禁止存储大数据,多次apply偶发丢失数据
2.SQLite:本地关系数据库;适合大量结构化本地数据;原生手写SQL繁琐
3.Room:Jetpack封装SQLite的ORM框架;编译期校验SQL语法;官方推荐
十六、AIDL跨进程通信
###原理
基于Binder实现Android跨进程通信
###Binder vs Socket
‑Binder:一次内存拷贝;仅限本机进程通信;高性能,Android专属IPC方案
‑Socket:TCP/IP通信,两次拷贝;支持跨设备网络通信;开销较大
###客户端监听服务端死亡代码示例
IBinder.DeathRecipient recipient=new IBinder.DeathRecipient(){ @Override public void binderDied() { //服务端死亡回调,释放资源、开启重连逻辑 } }; mBinder.linkToDeath(recipient,0);Android没有原生API监听客户端死亡;解决方案例子:客户端定时心跳上报,服务端超时判定客户端断开连接
###AIDL坑点
远程调用放在主线程超时引发ANR;oneway关键字实现异步调用;跨进程传递对象必须实现Parcelable序列化
十七、网络高频问题
1.主线程禁止发起网络请求
2.Android9.0默认禁止明文HTTP;清单配置cleartextTrafficPermitted开启
3.OkHttp连接池复用、拦截器、超时重试、熔断策略
4.HTTPS证书校验、证书锁定、双向认证
5.离线缓存策略
十八、 ART内存架构&GC
-内存架构:线程私有区,线程共享区,native堆
‑GC算法:标记‑清除、标记‑复制、标记‑整理
‑GC日志查看命令:adb logcat | grep GC
‑Concurrent GC:后台并发回收;Full GC:STW暂停所有线程,极易造成卡顿
‑内存泄漏:对象无用但仍然存在有效引用,GC无法回收;内存持续上涨最终OOM
十九、ADB调试命令
#查看CPU adb shell top adb shell dumpsys cpuinfo #查看内存mem adb shell dumpsys meminfo 包名 #查看IO adb shell iotop #窗口window信息 adb shell dumpsys window displays #音频audio adb shell dumpsys audio #wifi adb shell dumpsys wifi #导出ANR日志 adb pull /data/anr/traces.txt ./二十、性能优化大全
###UI卡顿、丢帧优化
1.布局优化:ConstraintLayout减少嵌套;include、merge、ViewStub懒加载
2.绘制优化:降低过度绘制,移除无用背景
3.监控工具:Systrace、Choreographer线上丢帧监控
###内存优化
减少临时对象创建;图片三级缓存;资源及时释放;LeakCanary检测泄漏
###CPU优化
主线程移除耗时任务;减少无效循环;降低后台唤醒频率
###IO优化
批量读写文件;异步IO;增加内存缓存
###Binder跨进程优化
减少跨进程调用次数;批量传输参数
###启动耗时优化
冷启动Application不要执行耗时任务;懒加载;App‑Startup;区分冷/温/热启动
二十一、稳定性优化 ANR、Crash、内存泄漏
###ANR触发条件
输入事件5s无响应;广播10s;Service20s;排查traces.txt日志,常见原因:主线程IO、死锁、Binder阻塞
###Crash
‑Java Crash:全局异常捕获UncaughtExceptionHandler
‑Native Crash:so库崩溃,addr2line解析堆栈日志
###高频内存泄漏场景
Handler延时消息未移除;静态变量持有Activity;单例持有Context;匿名内部类;定时器、广播未注销
二十二、静态内部类单例
public class Singleton{ private Singleton(){} private static class Holder{ private static final Singleton INSTANCE=new Singleton(); } public static Singleton getInstance(){ return Holder.INSTANCE; } }优点:懒加载、JVM类加载天然线程安全、无锁高性能;缺点:反射、反序列化可以破坏单例
二十三、高频设计模式+应用场景例子
1.单例模式:全局管理器、图片加载框架
2.工厂模式:对象创建、网络请求工厂
3.Builder建造者:OkHttpClient、AlertDialog
4.观察者模式:LiveData、EventBus
5.适配器模式:RecyclerView.Adapter
6.策略模式:动态切换业务算法
7.装饰模式:Context
8.中介者模式:FragmentManager
二十四、拓展高频面试题
1.WebView全套坑:内存泄漏、JS交互、缓存、HTTPS适配
2.多进程优缺点,Application多次创建,静态变量不共享
3.Binder完整原理,一次拷贝
4.WorkManager、Navigation原理
5.线上卡顿监控方案
6.字节码插桩 AOP
7.Android权限适配
8.车载Android AVM、Audio、Framework专项
如果你需要,我可以再输出一份纯考点精简背诵版(无代码),用来快速复盘。