Android面试指南(一)
目录
一、Activity
1.1、Activity生命周期
1.1.1、Activity的4种状态
1.1.2、Activity生命周期
1.1.3、Android进程优先级
1.2、Android任务栈
1.3、Activity启动模式
1.4、scheme跳转协议
二、Fragment
2.1、Fragment的常见问题
2.1.1、Fragment为什么被称为第五大组件?
2.1.2、Fragment加载到Activity的两种方式
2.1.3、FragmentPagerAdapter与FragmentStatePagerAdapter区别
2.2、Fragment的生命周期
2.3、Fragment通信
2.4、Fragment的replace、add、remove方法
三、Service
3.1、Service基础
3.2、Service和Thread的区别
3.3、Service的两种启动方式
3.3.1、startService
3.3.2、bindService
四、Broadcast Receiver
4.1、广播
4.1.1、广播的定义
4.1.2、广播的使用场景
4.1.3、广播的种类
4.2、实现广播
4.3、广播的实现机制
4.4、LocalBroadcastManager
4.4.1、本地广播的特点
4.4.2、LocalBroadcastManager详解
五、WebView
5.1、WebView常见问题
5.2、WebView内存泄漏问题
六、Binder
6.1、Linux内核基础知识
6.2、Binder通信机制
6.3、AIDL使用
一、Activity
1.1、Activity生命周期
Q:什么是Activity?
A:安卓是与用户交互的接口,它提供了一个界面,让用户进行点击,各种滑动操作,这就是Activity,它也是我们接触安卓遇到的第一个四大组件,也是我们接触最多的组件。
1.1.1、Activity的4种状态
- running状态:Activity处于活动状态,位于Activity栈顶,用户可点击屏幕并得到响应。
- paused状态:Activity失去焦点时,可能被非全屏或透明Activity覆盖,此时Activity处于paused状态,该状态会保留所有状态信息和成员变量,但用户无法交互。如果处于内存紧张时,Activity会被回收。
- stopped状态:被另一个Activity完全覆盖时,被覆盖的那个Activity会处于stopped状态,此时它不可见但它的内存状态信息及成员变量等都有可能还在(除非内存紧张)。
- killed状态:表明Activity已经被系统回收,它所保存的信息和成员变量肯定也都不存在了。
1.1.2、Activity生命周期
- 启动流程:
- onCreate():在Activity第一次被创建的时候调用。你应该在这个方法中完成Activity的初始化操作,比如加载布局、绑定事件等。
- onStart():Activity正在启动,处于可见状态但未在前台显示,用户不可交互,见而不得。
- onResume():Activity进入前台用户可以与之交互,此时的Activity一 定位于返回栈的栈顶,并且处于运行状态。
- 退居后台(点击Home键回到主界面,Activity不可见):
- onPause():Activity失去焦点但仍可见(比如弹出对话框),这个方法在系统准备去启动或者恢复另一个Activity的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶Activity的使用。
- onStop():这个方法在Activity完全不可见的时候调用。它和onPause()方法的主要区 别在于,如果启动的新Activity是一个对话框式的Activity,那么onPause()方法会得到执 行,而onStop()方法并不会执行。
- 返回前台(点击应用图标再次回到原Activity时):
- onRestart():Activity正在重新启动,由不可见变为可见时触发
- 后续流程:onStart()→onResume()
- 销毁流程(退出当前Activity时:onPause()→onStop()→onDestroy()):
- onDestroy():当前Activity正在被销毁,是最后执行的方法,可以进行资源的回收和释放
1.1.3、Android进程优先级
- 前台进程:一般就是处于正在和用户交互的Activity或者前台Activity绑定的Service
- 可见进程:Activity可见但不可交互(如被非全屏Activity覆盖)
- 服务进程:运行后台Service(如音乐播放)
- 后台进程:按Home键后的Activity进程,根据内存情况可能被回收
- 空进程:没有活跃组件的缓存进程,优先级最低,系统可以随时杀掉它
1.2、Android任务栈
- 栈结构特性: 采用后进先出(LIFO)的数据结构存储activity组件,每次启动新activity会入栈,退出时从栈顶移除。
- 任务栈本质: 一个task本质上就是一个activity的集合,Android系统通过任务栈有序管理每个activity的生命周期状态。
- 安全销毁要求: 完全退出应用时必须清空任务栈,否则需合理安全的保存栈内activity状态和数据信息。
- 多任务栈情况: 单个APP可能包含多个任务栈,特定情况下一个activity可以独享一个任务栈(比如Activity的single instance模式)。
1.3、Activity启动模式
①、standard
- 标准模式: 每次启动都新建activity实例并压入任务栈,不考虑栈内是否已存在相同实例。
- 生命周期影响: 每次创建都会完整执行onCreate->onStart->onResume生命周期方法。
- 资源消耗: 频繁创建实例会显著增加内存和系统资源开销。
②、singleTop
- 栈顶复用原则: 当待启动的activity已处于栈顶时,直接复用该实例而不是新建。
- 非栈顶处理: 若目标activity不在栈顶(即使存在于栈中),仍会创建新实例。
- 典型场景: 适用于防止快速重复点击导致同一页面多次实例化的情况。
③、singleTask
- 栈内复用原则: 检查整个任务栈是否存在目标activity实例,若存在则将该实例移至栈顶,并销毁其上所有activity。
- 回调方法: 复用时会触发onNewIntent()方法接收新的意图。
- 与single top对比: single top仅检查栈顶,而single task检查全栈并清理上方实例。
④、singleInstance
- 单例特性: 整个系统内只存在唯一实例,且独占独立任务栈。
- 使用场景: 适用于需要全局唯一性的特殊场景(如来电接听界面),常规开发较少使用。
1.4、scheme跳转协议
实现方式:
- 注册机制: 客户端通过定义自定义URL scheme实现页面路由。
- 跨平台触发: 可通过浏览器URL直接启动指定activity。
应用场景
- 服务端控制: 服务端下发URL路径引导客户端跳转特定页面。
- H5联动APP: 网页链接直接跳转至APP内指定功能页。
- 跨APP导流: 通过热门APP分享链接跳转到关联APP的推广页面(常见于集团型产品矩阵)。
二、Fragment
2.1、Fragment的常见问题
2.1.1、Fragment为什么被称为第五大组件?
- 使用频率:在实际项目开发中,Fragment的使用频率和作用与四大组件(Activity/Service/Broadcast/ContentProvider)同样突出
- 生命周期特性:与View不同,Fragment拥有自己的生命周期,可以像Activity一样灵活处理UI逻辑
- 设计初衷:Android3.0引入,最初是为大屏幕设备提供更灵活的UI展现方案
- 典型应用场景:
- 平板设备:单Activity多Fragment布局
- 手机设备:多Activity各自包含Fragment布局
- 优势:
- 相比Activity更节省内存
- UI切换效果更流畅
- 支持动态加载到Activity中
2.1.2、Fragment加载到Activity的两种方式
- 静态加载:
- 直接将Fragment作为XML标签写入Activity布局文件,使用简单但灵活性较差
- 动态加载(项目中常用的方式):
- 实现步骤:
- 通过getFragmentManager()获取FragmentManager实例
- 调用beginTransaction()创建FragmentTransaction
- 使用add()方法添加Fragment(需指定容器ID和Fragment实例)
- 调用commit()提交事务
- 容器ID作用:
- 确定Fragment视图在Activity中的显示位置
- 作为Fragment在管理器队列中的唯一标识符
- 注意事项:容器ID是视图资源ID,不是layout文件ID
- 实现步骤:
2.1.3、FragmentPagerAdapter与FragmentStatePagerAdapter区别
注意:该问题产生的背景是ViewPager与Fragment结合使用时遇到的内存管理机制的问题
- FragmentStatePagerAdapter:
- 调用remove()真正释放Fragment内存
- 适合页面较多的情况(节省内存)
- FragmentPagerAdapter:
- 调用detach()仅分离UI不回收内存
- 适合页面较少的情况(保持状态)
2.2、Fragment的生命周期
- 生命周期阶段:从创建到销毁经历onAttach、onCreate、onCreateView、onActivityCreated、onStart、onResume、onPause、onStop、onDestroyView、onDestroy、onDetach等回调方法
- 关键交互点:Fragment必须依附于Activity才能使用,其生命周期与宿主Activity紧密关联
Fragment从启动到销毁生命周期的全过程如下:
①、Fragment创建阶段
- onAttach:Fragment与Activity建立关联时的首个回调,此时Activity还未完成创建
- onCreate:仅用于Fragment的初始化,不同于Activity的onCreate,此时Activity尚未完成创建
- onCreateView:首次绘制UI时调用,必须返回Fragment布局的根视图
②、Fragment用户界面绘制阶段
- onViewCreated:UI完全绘制完成后调用,适合初始化控件和资源
- onActivityCreated:必须在Activity的onCreate完成后才会调用,表示Activity已渲染完成
③、Activity与Fragment的交互阶段
- 执行顺序:Activity.onStart→Fragment.onStart→Activity.onResume→Fragment.onResume
- 关键区别:Fragment的生命周期方法总是在对应Activity生命周期方法之后调用
④、Fragment可见与用户可交互阶段
- onResume:表示Fragment完全进入活跃状态,用户可进行点击、滑动等交互操作
- 交互保障:此时宿主Activity也已处于onResume状态,确保完整的交互环境
⑤、Fragment回退与销毁阶段
- 回退流程:onPause(失去交互)→Activity.onPause→onStop→Activity.onStop→onDestroyView
- 销毁顺序:onDestroy(释放资源)→onDetach(解除关联)→Activity.onDestroy
- 对应关系:onDestroyView与onCreateView对应,onDestroy与onCreate对应
2.3、Fragment通信
①、在Fragment中调用Activity中的方法:getActivity()
②、在Activity中调用Fragment中的方法
- 接口回调:在Fragment中定义接口,由Activity实现该接口
- 绑定时机:通常在onAttach中检查Activity是否实现了接口
③、在Fragment中调用Fragment中的方法
- 间接通信:通过宿主Activity中转,先getActivity()再findFragmentById()
- 注意事项:要确保目标Fragment已附加到Activity且未被销毁
2.4、Fragment的replace、add、remove方法
- add:将Fragment实例添加到Activity视图的最上层
- remove:从Activity的Fragment队列中彻底移除实例
- replace:替换当前最上层的Fragment(相当于remove+add组合操作)
- 关键区别:replace会销毁原有Fragment实例,而add会保留并叠加显示
三、Service
3.1、Service基础
- 本质特性:Service是Android四大组件之一,它是一个能在后台执行长时间运行操作而没有用户界面的应用组件
- 运行机制:它可以由其他组件(Activity/Broadcast)启动,启动后持续运行不受组件生命周期影响
- 重要限制:默认运行在主线程,不能直接执行耗时操作,否则会导致ANR(Application Not Responding)
- 跨进程能力:支持绑定到Activity进行数据交互,可以通过IPC实现跨进程通信
- 典型应用:音乐播放、数据统计、天气更新等需要长期后台运行的任务
3.2、Service和Thread的区别
- 定义层面:
- Thread:程序执行的最小单元,CPU调度的基本单位,可以创建独立执行的异步任务
- Service:Android系统机制,本质上是主线程组件,受系统进程托管
- 开发实践:
- Thread:用于处理网络IO、文件读写等阻塞UI线程的操作
- Service:适合需要长期运行且不依赖UI的后台任务(如音乐播放)
- 核心差异:
- 生命周期管理:Service可独立于Activity存在,提供稳定的后台任务管理
- 进程通信:Service支持跨进程绑定,Thread无法直接实现IPC
- 系统优先级:Service进程优先级高于普通Thread,更不易被系统回收
- 典型误区:
- 错误认为"后台服务"等同于"后台线程"
- 混淆Service的"后台运行"特性与Thread的"异步执行"特性
- 正确实践:
- 复杂场景应该组合使用:在Service中创建Thread执行耗时任务
- 通过IntentService自动管理工作线程
3.3、Service的两种启动方式
3.3.1、startService
①、startService使用步骤
- 继承Service类
- 在AndroidManifest.xml中配置该service
- 通过context.startService()方法启动服务
- 不再使用时调用stopService()停止服务
②、API介绍
- 启动方法:通过创建Intent对象指定要启动的服务,调用startService(intent)方法启动服务。
- 停止方法:通过stopService(intent)方法停止服务,需要传入相同的intent对象。
- onBind方法:返回null即可,与bindService启动方式相关,在startService方式中不会调用。
- onCreate方法:首次创建服务时调用,只执行一次,在onStartCommand或onBind之前调用。
- onStartCommand方法:每次通过startService()启动时都会被回调,服务正式开启的标志。
- onDestroy方法:服务不再使用时调用,用于资源清理(线程、监听器等),是服务接收的最后一个回调方法。
- START_STICKY:当服务因内存不足被杀后,内存空闲时系统会尝试重新创建服务,但intent为null。适用于音乐播放器、天气预报等需要持续运行的服务。
3.3.2、bindService
①、bindService使用步骤
- 创建BindService服务端,继承自Service并在类中创建一个实现IBinder接口的实例对象并提供公共方法给客户端调用
- 从onBind()回调方法返回此Binder实例
- 客户端实现ServiceConnection接口,在onServiceConnected()中接收Binder
- 通过Binder实例调用服务端方法
- 解绑时调用unbindService(),所有绑定取消后服务自动销毁
②、API介绍
- 绑定特点:服务与activity处于绑定状态,提供客户端-服务端接口,允许交互和数据传输。
- 生命周期关联:多个activity可绑定一个service,当所有绑定取消后,服务会自动销毁。
- ServiceConnection:包含onServiceConnected和onServiceDisconnected两个回调方法,前者在连接成功时调用,后者在服务崩溃或被杀时调用。
- 绑定方法:通过bindService(intent, conn, BIND_AUTO_CREATE)实现绑定,其中conn是ServiceConnection实例。
- LocalBinder:自定义Binder子类,提供getService()方法返回服务实例,客户端可通过该实例调用服务端公共方法。
- onBind方法:返回自定义Binder实例,与startService方式不同。
- onServiceConnected:获取服务端传递的Binder对象,通过该对象实现activity与service的交互。
- onServiceDisconnected:仅在服务意外中断时调用,不是主动解绑时调用。
四、Broadcast Receiver
4.1、广播
4.1.1、广播的定义
- 传输机制:在安卓中广播是应用程序间传输信息的机制,类似于Java的观察者模式,当被观察者数据变化时会通知观察者
- 实现方式:通过Intent发送广播内容,可以携带需要传输的数据
- 核心特性:
- 实现不同程序间的数据传输与共享(只要action相同的接收者都能接收)
- 具有通知作用(如Service可通过广播通知Activity更新UI)
4.1.2、广播的使用场景
- 多进程通信:同一APP内多个进程的不同组件间通信(如主进程与定位进程)
- 跨应用通信:不同APP间的组件通信(如公司多个APP间的互相推广)
4.1.3、广播的种类
- 普通广播:通过sendBroadcast()发送
- 有序广播:通过sendOrderedBroadcast()发送
- 本地广播:只在APP内传播
4.2、实现广播
①、静态注册
- 注册方式:在manifest文件中声明
- 特点:
- 不依赖Activity生命周期(即使进程被杀仍能接收)
- 灵活性较差但接收更可靠
②、动态注册
- 注册方式:代码中调用registerReceiver()方法
- 生命周期:跟随Activity生命周期,需在onDestroy()中调用unregisterReceiver()
- 特点:灵活性高但需手动管理生命周期
4.3、广播的实现机制
1、自定义广播接收者BroadcastReceiver,并复写onRecvice()方法;
2、通过Binder机制向AMS(Activity Manager Service)进行注册;
- AMS作用:Activity Manager Service负责四大组件的启动调度
- Binder机制:安卓进程间通信核心,采用CS架构
3、广播发送者通过Binder机制向AMS发送广播;
4、AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
5、消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
4.4、LocalBroadcastManager
4.4.1、本地广播的特点
- 安全性:本地广播仅在APP内传播,防止数据泄露
- 隔离性:其他APP无法发送该广播
- 高效性:比系统全局广播更高效
4.4.2、LocalBroadcastManager详解
- 构造方法
- 单例模式:private修饰的构造方法
- 线程模型:通过主线程Looper的Handler处理消息
- 集合类
- mReceivers:HashMap<BroadcastReceiver, ArrayList<IntentFilter>>
- mActions:HashMap<String, ArrayList<ReceiverRecord>>
- mPendingBroadcasts:存储待接收的广播对象
- 注册广播接收者方法
- 建立action与receiver的映射关系
- 添加IntentFilter过滤规则
- 发送广播方法
- 实现原理:通过Handler发送Message
- 执行流程:
- 匹配action集合
- 将广播记录添加到mPendingBroadcasts
- 通过Handler发送处理消息
总结如下:
1、LocalBroadcastManager内部是通过Handler实现的,它的sendBroadcast()方法是通过handler发送一个Message实现的;
2、由于它内部是通过Handler来实现广播的发送,相比系统广播通过Binder实现更加高效, 同时使用Handler来实现,别的应用无法向我们的应用发送该广播,,而我们应用内发送的广播也不会离开应用本身;
3、LocalBroadcastManager内部协作主要是靠这两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要就是存储待接收的广播对象。
五、WebView
5.1、WebView常见问题
①、远程代码执行安全漏洞
- 漏洞原理:Android API level 16及之前版本未正确限制使用WebView.addJavascriptInterface方法,攻击者可通过Java反射机制执行任意Java对象方法
- 攻击方式:通过JavaScript桥接口调用本地Java接口,利用反射机制调用未注册的其他Java类
- 危害:攻击者可无限增强JavaScript能力,对客户端实施非法行为
②、WebView在布局文件中的使用
- 销毁流程:在Activity销毁时需先调用容器removeView移除WebView,再调用removeAllViews和destroy方法
- 内存泄露风险:未正确销毁会导致WebView持续持有Activity引用,造成内存泄漏
③、JS Bridge
- JS Bridge本质:建立Web端与Native端的双向通信桥梁
- 功能特点:
- Native端可调用Web端JavaScript代码
- Web端可调用Native端Java代码
④、WebViewClient.onPageFinished
- onPageFinished缺陷:网页跳转时会多次触发,无法准确判断加载完成状态
- 替代方案:推荐使用WebChromeClient.onProgressChanged方法进行页面回调处理
⑤、后台耗电
- 耗电原因:WebView加载网页时自动开启的线程未正确销毁
- 解决方案:
- 暴力方案:在onDestroyon中调用System.exit终止虚拟机
- 优化建议:根据场景细化销毁逻辑
⑥、WebView硬件加速导致的页面渲染问题
- 加速特性:Android 3.0+支持硬件加速,提升页面拖动流畅度
- 副作用:可能导致页面白块和闪烁现象
- 解决方案:临时关闭WebView硬件加速功能
5.2、WebView内存泄漏问题
①、使用独立进程
- 原理:为WebView创建独立进程,隔离Activity生命周期
- 优点:彻底避免内存泄漏,减少主进程内存占用
- 缺点:需要处理进程间通信,实现较复杂
②、动态添加WebView
- 实现方式:
- 对传入WebView中的Context使用弱引用,在布局中创建ViewGroup用来放置WebView,Activity创建时add进来,在Activity停止时remove掉
- 销毁要点:
- Activity停止时先removeView
- 再调用WebView的销毁方法
- 适用场景:常规WebView使用场景的首选方案
六、Binder
6.1、Linux内核基础知识
进程隔离和虚拟地址空间
- 隔离机制:操作系统通过进程隔离技术保护进程间互不干扰,避免进程A操作进程B的数据
- 实现原理:每个进程拥有独立的虚拟地址空间,进程A和进程B的虚拟地址空间不同
- 通信限制:不同进程间数据不共享,必须通过进程间通信(IPC)机制完成交互
- 系统调用:Linux内核通过系统调用保护机制,限制应用程序只能访问许可资源
- 分层架构:分为内核空间和用户空间,用户空间通过系统调用访问内核程序
- 驱动角色:Binder驱动运行在内核空间,负责处理用户进程通过Binder的交互请求
6.2、Binder通信机制
①、为什么要使用Binder
- 性能优势:相比传统socket方式,Binder在移动设备上通信效率更高
- 安全特性:从协议层面支持通信双方身份校验,防止IP地址伪造等安全问题
- 权限基础:Binder的身份校验机制是安卓权限模型的基础
- 必要性:虽然Linux已有多种IPC机制(如管道、socket),但Binder针对移动设备做了特殊优化
②、Binder通信模型
- 类比模型:类似电话通讯系统,包含四个关键角色:
- 通信双方(客户端进程和服务端进程)
- Service Manager(相当于通讯录)
- Binder驱动(相当于电话基站)
- 通信步骤:
- Service Manager建立并管理服务注册表
- 服务进程启动后向Service Manager注册服务信息
- 客户端查询Service Manager获取服务引用
- 通过Binder驱动完成实际通信
- 代理机制:客户端获取的是服务端的代理对象,而非真实对象
- 驱动转换:Binder驱动自动完成代理对象和服务端对象的转换
- 透明调用:客户端调用代理方法时,驱动会将调用转发给真实服务端方法
③、Binder总结
- 通信机制:本质上是一种跨进程通信(IPC)机制
- 对象视角:
- 对服务端:指Binder本地对象
- 对客户端:指Binder代理对象
- 传输特性:Binder是可以跨进程传递的对象,驱动会特殊处理这类对象
6.3、AIDL使用
- Stub类:编译工具生成的静态内部类,继承自自定义AIDL接口
- asInterface方法:
- 参数为IBinder接口,代表跨进程传输能力
- 同进程直接返回本地对象,跨进程返回代理对象
- onTransact方法:
- 根据函数编号调用对应方法
- 处理参数序列化和结果返回
- 完整流程:
- 客户端调用代理方法
- 驱动转发调用到服务端
- 服务端执行实际方法
- 结果通过驱动返回客户端
- 数据序列化:使用Parcel进行参数和结果的序列化
- 方法标识:每个AIDL方法都有唯一的transaction编号
- 异常处理:通过writeNoException()方法处理调用异常
使用详情参考:Android接口定义语言(AIDL)
OK,今天的内容就到这里了,下期再会!