引言:安卓开发面试的挑战与机遇
在当今移动互联网时代,安卓开发作为热门技术领域,其面试竞争日益激烈。面试官不仅考察候选人的基础知识,更注重实际问题解决能力和架构设计思维。本文将系统性地梳理安卓开发面试的核心知识点,从基础概念到高级特性,从理论理解到实战技巧,帮助求职者构建完整的知识体系,从容应对各类技术考核。
安卓开发面试通常涵盖四大维度:Java/Kotlin语言基础、Android框架核心机制、性能优化与架构设计,以及工程化实践。每个维度都有其独特的考察重点和应对策略。通过本文的学习,你将掌握面试中的高频考点,理解背后的原理,并学会如何在实际项目中应用这些知识。
值得注意的是,随着技术的快速迭代,面试重点也在不断变化。Jetpack Compose、Kotlin协程、MVVM架构等现代技术栈已成为考察热点。因此,我们不仅会回顾经典内容,还会深入探讨前沿技术,确保你的知识储备与时俱进。
第一部分:Java与Kotlin语言基础
1.1 Java基础核心考点
1.1.1 面向对象特性深度解析
面试官常常从面向对象三大特性(封装、继承、多态)入手,考察候选人对Java语言本质的理解。封装通过访问修饰符实现数据隐藏,继承建立类之间的层次关系,多态则允许不同类型的对象对同一消息做出不同响应。
关键考察点:
- 访问修饰符的作用范围与使用场景
- 方法重写(Override)与方法重载(Overload)的区别
- 抽象类与接口的异同及选择策略
- 多态的实现机制与动态绑定原理
面试真题示例: “请解释Java中抽象类和接口的区别,并说明在什么情况下应该选择抽象类而不是接口?”
标准答案: 抽象类和接口的主要区别体现在以下方面:
- 方法实现:抽象类可以包含具体方法的实现,而接口在Java 8之前只能包含抽象方法(Java 8后允许默认方法和静态方法)
- 成员变量:抽象类可以包含各种类型的成员变量,接口中的变量默认是public static final
- 构造方法:抽象类可以有构造方法,接口不能有构造方法
- 继承关系:一个类只能继承一个抽象类,但可以实现多个接口
- 设计层次:抽象类用于”is-a”关系,接口用于”can-do”关系
选择策略:当需要定义一些公共行为并提供默认实现时,使用抽象类;当需要定义多个类共享的行为契约时,使用接口。
1.1.2 集合框架与数据结构
Java集合框架是面试高频考点,需要深入理解各接口的层次结构和实现类的特点。
核心接口层次:
Collection
├── List (有序可重复)
│ ├── ArrayList (动态数组)
│ ├── LinkedList (双向链表)
│ └── Vector (线程安全)
├── Set (无序不可重复)
│ ├── HashSet (哈希表)
│ └── TreeSet (红黑树)
└── Queue (队列)
├── LinkedList
└── PriorityQueue
Map (键值对)
├── HashMap (哈希表)
├── LinkedHashMap (保持插入顺序)
└── TreeMap (红黑树)
关键面试问题: “ArrayList和LinkedList的区别是什么?在实际项目中如何选择?”
详细对比分析:
- 数据结构:ArrayList基于动态数组,LinkedList基于双向链表
- 随机访问:ArrayList通过索引访问的时间复杂度为O(1),LinkedList为O(n)
- 插入删除:ArrayList在尾部操作效率高,中间插入需要移动元素;LinkedList在任意位置插入删除效率高
- 内存占用:ArrayList占用内存较少,LinkedList每个节点需要额外存储前后节点指针
- 使用场景:频繁查询用ArrayList,频繁插入删除用LinkedList
实战建议:在实际Android开发中,由于大多数场景是查询操作,ArrayList使用更广泛。但需要注意ArrayList的扩容机制(默认扩容1.5倍),在已知数据量时应尽量指定初始容量以避免多次扩容。
1.1.3 多线程与并发编程
多线程是Java的难点也是面试重点,需要理解线程生命周期、同步机制、锁的分类等。
线程生命周期:
NEW → RUNNABLE → BLOCKED → WAITING → TIMED_WAITING → TERMINATED
同步机制对比:
| 方式 | 原理 | 适用场景 | 性能 |
|---|---|---|---|
| synchronized | JVM层面,自动释放 | 代码块简单同步 | 一般 |
| ReentrantLock | API层面,需手动释放 | 需要公平锁、可中断等特性 | 较好 |
| volatile | 保证可见性,不保证原子性 | 状态标记 | 高 |
| Atomic类 | CAS操作 | 计数器等原子操作 | 高 |
面试真题: “请实现一个线程安全的单例模式,并解释每种实现方式的优缺点。”
完整代码实现:
// 饿汉式 - 类加载时就初始化
public class Singleton1 {
private static final Singleton1 instance = new Singleton1();
private Singleton1() {}
public static Singleton1 getInstance() {
return instance;
}
// 优点:线程安全,实现简单
// 缺点:可能造成资源浪费
}
// 懒汉式 - 双重检查锁定
public class Singleton2 {
private static volatile Singleton2 instance;
private Singleton2() {}
public static Singleton2 getInstance() {
if (instance == null) {
synchronized (Singleton2.class) {
if (instance == null) {
instance = new Singleton2();
}
}
}
return instance;
}
// 优点:延迟加载,性能较好
// 缺点:实现复杂,需要理解volatile和指令重排序
}
// 静态内部类
public class Singleton3 {
private Singleton3() {}
private static class Holder {
private static final Singleton3 INSTANCE = new Singleton3();
}
public static Singleton3 getInstance() {
return Holder.INSTANCE;
}
// 优点:线程安全,延迟加载,实现优雅
// 推荐使用
}
// 枚举(最佳方式)
public enum Singleton4 {
INSTANCE;
// 优点:线程安全,防止反射攻击,防止反序列化重新创建
}
1.2 Kotlin语言特性
1.2.1 空安全机制
Kotlin的空安全是其核心特性,通过类型系统在编译期避免空指针异常。
核心概念:
String:不可为空的字符串String?:可为空的字符串- 安全调用操作符:
?. - Elvis操作符:
?: - 非空断言:
!!
代码示例:
// 安全调用
val name: String? = "Kotlin"
val length = name?.length // 结果为Int?,可能为null
// Elvis操作符
val displayName = name ?: "Unknown" // 如果name为null,使用"Unknown"
// 非空断言(应避免使用)
val forcedLength = name!!.length // 如果name为null,抛出KotlinNullPointerException
// 安全转换
val obj: Any = "String"
val str = obj as? String // 安全转换,失败返回null
面试问题: “Kotlin的空安全机制是如何在编译期避免空指针异常的?”
回答要点:
- 类型系统区分可空与不可空类型
- 编译器在编译期进行空值检查
- 安全调用操作符和Elvis操作符提供便捷的空值处理
- 与Java互操作时,平台类型(Platform Type)需要特别注意
1.2.2 协程(Coroutines)
协程是Kotlin的异步编程解决方案,在Android开发中越来越重要。
核心概念:
- 挂起函数(Suspend Function):可以暂停执行而不阻塞线程
- 协程上下文(CoroutineContext):包含调度器、异常处理器等
- 作用域(Scope):管理协程的生命周期
- 调度器(Dispatcher):指定协程运行的线程
代码示例:
// 基础使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在生命周期作用域中启动协程
lifecycleScope.launch {
// 在主线程执行
val result = fetchDataFromNetwork()
updateUI(result)
}
}
// 挂起函数
private suspend fun fetchDataFromNetwork(): String {
return withContext(Dispatchers.IO) {
// 在IO线程执行网络请求
delay(2000) // 模拟网络延迟
"Network Data"
}
}
private fun updateUI(data: String) {
// 更新UI操作
findViewById<TextView>(R.id.textView).text = data
}
}
// 协程异常处理
class CoroutineExceptionHandlerExample {
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
// 处理协程异常
Log.e("Coroutine", "Error: ${throwable.message}")
}
fun launchWithHandler() {
CoroutineScope(Dispatchers.Main + exceptionHandler).launch {
throw RuntimeException("Test Exception")
}
}
}
面试真题: “Kotlin协程与RxJava相比有哪些优势?在Android开发中为什么推荐使用协程?”
回答要点:
- 学习曲线:协程更符合命令式编程思维,易于理解和调试
- 结构化并发:协程具有生命周期管理,避免内存泄漏
- 取消机制:协程可以轻松取消,而RxJava的取消相对复杂
- 与Jetpack集成:lifecycleScope、viewModelScope等提供生命周期感知
- 性能:协程更轻量,创建成本远低于线程
1.2.3 扩展函数与高阶函数
这些特性使Kotlin代码更简洁、更具表现力。
扩展函数示例:
// 为String类添加扩展函数
fun String.isEmail(): Boolean {
return Patterns.EMAIL_ADDRESS.matcher(this).matches()
}
// 使用
val email = "test@example.com"
if (email.isEmail()) {
// 验证通过
}
// 为View添加扩展函数
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
高阶函数示例:
// 定义高阶函数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// 使用
val sum = calculate(5, 3) { x, y -> x + y } // 8
val product = calculate(5, 3) { x, y -> x * y } // 15
// 实际应用:网络请求封装
fun <T> request(
apiCall: suspend () -> T,
onSuccess: (T) -> Unit,
onError: (Exception) -> Unit
) {
lifecycleScope.launch {
try {
val result = apiCall()
onSuccess(result)
} catch (e: Exception) {
onError(e)
}
}
}
第二部分:Android框架核心机制
2.1 Activity与Fragment生命周期
2.1.1 生命周期方法详解
Activity的生命周期是Android开发的基础,面试中常考察对各个回调方法的理解。
生命周期状态与回调:
onCreate() → onStart() → onResume() → [运行状态]
↓
onPause() → onStop() → onDestroy()
关键方法说明:
- onCreate():初始化UI,调用setContentView(),恢复保存的状态
- onStart():Activity变为可见,但不可交互
- onResume():Activity获得焦点,可与用户交互
- onPause():Activity失去焦点但仍可见,可进行轻量级数据保存
- onStop():Activity完全不可见,可进行重量级数据保存
- onDestroy():Activity被销毁,释放所有资源
面试真题: “在Activity A启动Activity B的过程中,生命周期回调的执行顺序是什么?”
详细执行流程:
- A.onPause() - A失去焦点
- B.onCreate() - B初始化
- B.onStart() - B变为可见
- B.onResume() - B获得焦点
- A.onStop() - A完全不可见
特殊情况:
- 如果B是透明主题或对话框样式,A不会调用onStop()
- 配置变更(如屏幕旋转)会导致Activity销毁并重建
2.1.2 Fragment生命周期
Fragment生命周期与Activity类似,但增加了一些特有的回调。
生命周期回调:
onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()
↓
onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()
关键区别:
- onCreateView():创建Fragment的视图层次结构
- onViewCreated():视图创建完成后调用,适合初始化视图控件
- onDestroyView():Fragment视图被移除,适合释放视图相关资源
- onDetach():Fragment与Activity解绑
面试问题: “Fragment中onAttach()和onCreate()的区别是什么?”
回答:
- onAttach():Fragment与Activity建立关联时调用,此时Activity已创建但可能还未完成onCreate()
- onCreate():Fragment初始化时调用,此时可以通过getActivity()获取Activity引用
2.1.3 生命周期实战技巧
场景1:内存不足时生命周期调用
用户操作:启动应用 → 打开多个Activity → 按Home键 → 内存不足
生命周期:onCreate → onStart → onResume → onPause → onStop → [系统回收] → onDestroy
场景2:配置变更生命周期
屏幕旋转:onPause → onStop → onDestroy → onCreate → onStart → onResume
解决方案:
// 1. 防止Activity重建
AndroidManifest.xml中配置:
android:configChanges="orientation|screenSize"
// 2. 保存状态
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("key", "value")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 恢复状态
val value = savedInstanceState?.getString("key")
}
// 3. ViewModel自动保存
class MyViewModel : ViewModel() {
val data = MutableLiveData<String>()
}
2.2 消息机制:Handler与Looper
2.2.1 消息机制原理
Android消息机制是线程间通信的核心,理解其原理对解决内存泄漏和ANR至关重要。
核心组件:
- MessageQueue:消息队列,存储待处理的Message
- Looper:循环器,不断从MessageQueue中取出Message并分发给Handler
- Handler:消息处理者,发送和处理Message
- Message:消息实体,包含what、arg1、arg2、obj等字段
工作流程:
Handler.sendMessage() → MessageQueue.enqueueMessage() → Looper.loop() → Handler.handleMessage()
代码示例:
// 主线程Handler
class MainActivity : AppCompatActivity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {
val data = msg.obj as String
textView.text = data
}
}
}
}
private fun startTask() {
Thread {
// 子线程执行耗时操作
Thread.sleep(2000)
val message = Message.obtain()
message.what = 1
message.obj = "Task Completed"
handler.sendMessage(message)
}.start()
}
}
// 自线程Looper
class WorkThread : Thread() {
lateinit var handler: Handler
override fun run() {
Looper.prepare()
handler = object : Handler() {
override fun handleMessage(msg: Message) {
// 处理消息
}
}
Looper.loop()
}
}
2.2.2 内存泄漏防范
Handler内存泄漏是Android开发常见问题,需要特别注意。
泄漏原因:
// 错误示例:非静态内部类持有外部类引用
class MainActivity : AppCompatActivity() {
private val handler = Handler(Looper.getMainLooper()) {
// 匿名内部类隐式持有MainActivity引用
true
}
private fun startTask() {
Thread {
Thread.sleep(5000)
// 如果Activity在此期间被销毁,handler仍持有引用,导致泄漏
handler.sendEmptyMessage(1)
}.start()
}
}
解决方案:
// 方案1:静态内部类 + 弱引用
class MainActivity : AppCompatActivity() {
private val myHandler = MyHandler(this)
override fun onDestroy() {
super.onDestroy()
// 移除所有消息,防止泄漏
myHandler.removeCallbacksAndMessages(null)
}
private class MyHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val weakActivity = WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get()?.let { activity ->
// 处理消息
}
}
}
}
// 方案2:使用ViewModel + 协程(推荐)
class MyViewModel : ViewModel() {
fun startTask() {
viewModelScope.launch {
delay(5000)
// 自动生命周期管理,无需手动清理
}
}
}
2.3 View系统与事件分发
2.3.1 View绘制流程
View的绘制流程是理解自定义View的基础,面试中常考察对measure、layout、draw的理解。
三大流程:
- measure:测量View的大小,由setMeasuredDimension()确定最终尺寸
- layout:确定View的位置,调用onLayout()确定子View位置
- draw:绘制View内容,调用onDraw()进行实际绘制
MeasureSpec模式:
// 三种测量模式
EXACTLY:精确值(如100dp或match_parent)
AT_MOST:最大值(如wrap_content)
UNSPECIFIED:未指定(系统内部使用)
代码示例:自定义View
class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val paint = Paint().apply {
color = Color.BLUE
style = Paint.Style.FILL
isAntiAlias = true
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// 解析MeasureSpec
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
// 计算期望尺寸
val desiredWidth = 200 // 期望宽度
val desiredHeight = 200 // 期望高度
val finalWidth = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> min(desiredWidth, widthSize)
MeasureSpec.UNSPECIFIED -> desiredWidth
else -> widthSize
}
setMeasuredDimension(finalWidth, desiredHeight)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 绘制圆形
canvas.drawCircle(
width / 2f,
height / 2f,
min(width, height) / 2f,
paint
)
}
}
2.3.2 事件分发机制
事件分发是Android开发的难点,理解其原理有助于解决滑动冲突。
核心方法:
- dispatchTouchEvent():事件分发,返回true表示消费
- onInterceptTouchEvent():事件拦截(ViewGroup特有)
- onTouchEvent():事件消费
分发流程:
Activity.dispatchTouchEvent() → ViewGroup.dispatchTouchEvent() →
ViewGroup.onInterceptTouchEvent() → View.dispatchTouchEvent() →
View.onTouchEvent() → ViewGroup.onTouchEvent() → Activity.onTouchEvent()
滑动冲突解决方案:
// 外部拦截法:父容器根据需要拦截事件
class ParentLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
private var lastX = 0f
private var lastY = 0f
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
lastX = ev.x
lastY = ev.y
return false // 不拦截DOWN事件
}
MotionEvent.ACTION_MOVE -> {
val deltaX = ev.x - lastX
val deltaY = ev.y - lastY
// 水平滑动距离大于垂直滑动距离时拦截
if (abs(deltaX) > abs(deltaY) && abs(deltaX) > 10) {
return true
}
}
}
return super.onInterceptTouchEvent(ev)
}
}
// 内部拦截法:子View请求父容器不拦截
class ChildView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private var lastX = 0f
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// 请求父容器不拦截DOWN事件
parent.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_MOVE -> {
val deltaX = event.x - lastX
// 如果需要父容器处理,可以重新允许拦截
if (needParentIntercept(deltaX)) {
parent.requestDisallowInterceptTouchEvent(false)
}
}
}
lastX = event.x
return true
}
private fun needParentIntercept(deltaX: Float): Boolean {
// 判断逻辑
return false
}
}
第三部分:性能优化与内存管理
3.1 内存优化
3.1.1 内存泄漏检测与防范
内存泄漏是Android应用性能问题的头号杀手,需要系统性防范。
常见泄漏场景:
- 静态变量持有Context
- 非静态内部类(匿名内部类)
- 资源未释放(Bitmap、Cursor、Socket)
- 监听器未移除
- Activity被Fragment引用
检测工具:
- LeakCanary:自动检测内存泄漏
- Android Profiler:官方性能分析工具
- MAT(Memory Analyzer Tool):分析heap dump
代码示例:
// 错误示例:静态变量持有Activity
class AppManager {
companion object {
var currentActivity: Activity? = null // 危险!
}
}
// 正确做法:使用弱引用
class AppManager {
companion object {
private val activityRefs = mutableListOf<WeakReference<Activity>>()
fun registerActivity(activity: Activity) {
activityRefs.add(WeakReference(activity))
}
fun getCurrentActivity(): Activity? {
// 清理已销毁的引用
activityRefs.removeAll { it.get() == null }
return activityRefs.lastOrNull()?.get()
}
}
}
// 非静态内部类导致泄漏
class MainActivity : AppCompatActivity() {
private val myThread = MyThread() // 持有Activity引用
inner class MyThread : Thread() {
override fun run() {
// 长时间运行,持有Activity引用
Thread.sleep(10000)
}
}
// 解决方案:改为静态内部类 + 弱引用
private class MyThreadStatic(activity: MainActivity) : Thread() {
private val weakActivity = WeakReference(activity)
override fun run() {
Thread.sleep(10000)
weakActivity.get()?.let {
// 安全使用
}
}
}
}
3.1.2 Bitmap优化
Bitmap是内存占用大户,优化Bitmap对应用性能至关重要。
优化策略:
- 采样率压缩:inSampleSize
- 色彩模式:ARGB_8888 → RGB_565
- 及时回收:recycle()
- 缓存策略:LruCache
代码示例:
class BitmapOptimization {
// 计算采样率
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
// 优化加载Bitmap
fun decodeSampledBitmap(
res: Resources,
resId: Int,
reqWidth: Int,
reqHeight: Int
): Bitmap {
// 第一次解析:只获取尺寸信息
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeResource(res, resId, options)
// 计算采样率
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
// 第二次解析:使用计算出的采样率
options.inJustDecodeBounds = false
return BitmapFactory.decodeResource(res, resId, options)
}
// 使用LruCache缓存
private val memoryCache: LruCache<String, Bitmap>
init {
val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
val cacheSize = maxMemory / 8 // 使用1/8的内存
memoryCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, bitmap: Bitmap): Int {
return bitmap.byteCount / 1024
}
}
}
fun addBitmapToCache(key: String, bitmap: Bitmap) {
if (getBitmapFromCache(key) == null) {
memoryCache.put(key, bitmap)
}
}
fun getBitmapFromCache(key: String): Bitmap? {
return memoryCache.get(key)
}
}
3.2 UI性能优化
3.2.1 布局优化
布局嵌套过深会导致测量和布局时间过长,影响UI流畅度。
优化方案:
- 减少嵌套:使用ConstraintLayout扁平化布局
- 避免过度绘制:移除不必要的背景
- 使用
和 :复用布局 - ViewStub:延迟加载
代码示例:
<!-- 优化前:嵌套过深 -->
<LinearLayout>
<LinearLayout>
<TextView />
<ImageView />
</LinearLayout>
</LinearLayout>
<!-- 优化后:ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
app:layout_constraintTop_toBottomOf="@id/textView"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- ViewStub延迟加载 -->
<ViewStub
android:id="@+id/stub_import"
android:layout="@layout/import_ui"
android:inflatedId="@+id/real_import_ui" />
// 代码中加载
findViewById<ViewStub>(R.id.stub_import).inflate()
3.2.2 列表优化(RecyclerView)
RecyclerView是性能优化的重点,需要掌握缓存机制和优化技巧。
缓存机制:
- Scrap缓存:临时缓存,不改变数据
- Cache缓存:缓存离屏View
- Pool缓存:复用ViewHolder
优化技巧:
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
// 1. 使用DiffUtil高效更新
private val diffCallback = object : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem == newItem
}
}
private val differ = AsyncListDiffer(this, diffCallback)
fun updateList(newList: List<Item>) {
differ.submitList(newList)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// 2. 避免在onBindViewHolder中做耗时操作
val item = differ.currentList[position]
// 3. 使用Glide/Picasso等框架加载图片,自动处理缓存和回收
Glide.with(holder.itemView)
.load(item.imageUrl)
.placeholder(R.drawable.placeholder)
.into(holder.imageView)
}
// 4. 设置固定尺寸,避免重新测量
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_layout, parent, false)
// 如果item高度固定,设置这个可以优化性能
view.layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT,
200 // 固定高度
)
return MyViewHolder(view)
}
}
// 5. 在ViewHolder中进行findViewById缓存
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
val imageView: ImageView = itemView.findViewById(R.id.imageView)
init {
// 6. 设置点击防抖
itemView.setOnClickListener {
// 使用防抖点击
if (SystemClock.elapsedRealtime() - lastClickTime > 500) {
lastClickTime = SystemClock.elapsedRealtime()
// 处理点击
}
}
}
companion object {
private var lastClickTime = 0L
}
}
3.3 网络性能优化
3.3.1 网络请求优化策略
优化方向:
- 请求合并:减少请求次数
- 数据压缩:Gzip
- 缓存策略:HTTP缓存、本地缓存
- 连接复用:HTTP/2、WebSocket
代码示例:使用OkHttp拦截器实现缓存
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 有网络时使用网络,无网络时使用缓存
val forceCache = request.header("Cache-Control") == "force-cache"
val response = if (forceCache) {
// 强制使用缓存
val cachedResponse = chain.proceed(request)
if (cachedResponse.isSuccessful) {
cachedResponse
} else {
// 缓存无效,重新请求
chain.proceed(request)
}
} else {
chain.proceed(request)
}
return response.newBuilder()
.header("Cache-Control", "public, max-age=60")
.build()
}
}
// 使用
val okHttpClient = OkHttpClient.Builder()
.cache(Cache(cacheDir, 10 * 1024 * 1024)) // 10MB缓存
.addNetworkInterceptor(CacheInterceptor())
.build()
第四部分:架构设计与模式
4.1 MVVM架构
4.1.1 MVVM核心组件
MVVM(Model-View-ViewModel)是Google推荐的架构模式,与Jetpack组件完美配合。
核心组件:
- Model:数据层,负责业务逻辑和数据源
- View:UI层,负责显示数据和用户交互
- ViewModel:持有UI状态,连接View和Model
代码示例:完整实现
// Model层:数据源
data class User(
val id: String,
val name: String,
val email: String
)
interface UserRepository {
suspend fun getUser(id: String): User
suspend fun updateUser(user: User)
}
class UserRepositoryImpl : UserRepository {
private val apiService = RetrofitClient.apiService
override suspend fun getUser(id: String): User {
return apiService.getUser(id)
}
override suspend fun updateUser(user: User) {
apiService.updateUser(user)
}
}
// ViewModel层
class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {
private val _userState = MutableStateFlow<User?>(null)
val userState: StateFlow<User?> = _userState.asStateFlow()
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error.asStateFlow()
fun loadUser(id: String) {
viewModelScope.launch {
_isLoading.value = true
_error.value = null
try {
val user = userRepository.getUser(id)
_userState.value = user
} catch (e: Exception) {
_error.value = e.message ?: "Unknown error"
} finally {
_isLoading.value = false
}
}
}
fun updateUser(name: String, email: String) {
val currentUser = _userState.value ?: return
viewModelScope.launch {
_isLoading.value = true
try {
val updatedUser = currentUser.copy(name = name, email = email)
userRepository.updateUser(updatedUser)
_userState.value = updatedUser
} catch (e: Exception) {
_error.value = e.message ?: "Update failed"
} finally {
_isLoading.value = false
}
}
}
}
// View层:Activity/Fragment
class UserProfileActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels {
// ViewModel工厂
object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return UserViewModel(UserRepositoryImpl()) as T
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_profile)
// 收集StateFlow
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.userState.collect { user ->
user?.let { updateUserUI(it) }
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.isLoading.collect { isLoading ->
progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.error.collect { error ->
error?.let { showError(it) }
}
}
}
// 用户交互
saveButton.setOnClickListener {
val name = nameEditText.text.toString()
val email = emailEditText.text.toString()
viewModel.updateUser(name, email)
}
// 加载初始数据
intent.getStringExtra("userId")?.let { userId ->
viewModel.loadUser(userId)
}
}
private fun updateUserUI(user: User) {
nameEditText.setText(user.name)
emailEditText.setText(user.email)
}
private fun showError(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
4.1.2 DataBinding与ViewBinding
ViewBinding(推荐):
// build.gradle
android {
viewBinding {
enabled = true
}
}
// Activity中使用
class MainActivity : AppCompatActivity() {
private lateinit binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textView.text = "Hello ViewBinding"
binding.button.setOnClickListener {
// 处理点击
}
}
}
DataBinding(适合复杂场景):
<!-- layout.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User" />
<variable
name="handler"
type="com.example.UserHandler" />
</data>
<LinearLayout>
<TextView
android:text="@{user.name}" />
<Button
android:onClick="@{() -> handler.onSaveClick(user)}"
android:text="Save" />
</LinearLayout>
</layout>
class UserActivity : AppCompatActivity() {
private lateinit binding: ActivityUserBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityUserBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.user = User("1", "John", "john@example.com")
binding.handler = UserHandler()
binding.lifecycleOwner = this // 重要!
}
}
class UserHandler {
fun onSaveClick(user: User) {
// 处理保存
}
}
4.2 Jetpack组件深度集成
4.2.1 ViewModel + LiveData/StateFlow
LiveData vs StateFlow:
- LiveData:生命周期感知,适合UI控制器
- StateFlow:Kotlin原生,更灵活,适合纯逻辑
代码对比:
// LiveData方式
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
fun loadData() {
viewModelScope.launch {
_data.value = "Loading..."
delay(1000)
_data.value = "Loaded"
}
}
}
// StateFlow方式(推荐)
class MyViewModel : ViewModel() {
private val _data = MutableStateFlow<String>("Initial")
val data: StateFlow<String> = _data.asStateFlow()
fun loadData() {
viewModelScope.launch {
_data.value = "Loading..."
delay(1000)
_data.value = "Loaded"
}
}
}
// 在Activity中收集
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.data.collect { value ->
textView.text = value
}
}
}
4.2.2 Navigation组件
Navigation组件简化了Fragment事务管理。
基本使用:
// 1. 创建导航图 nav_graph.xml
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.HomeFragment">
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment">
<argument
android:name="itemId"
app:argType="string" />
</fragment>
</navigation>
// 2. 在Activity中设置
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
// 设置BottomNavigationView
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav)
bottomNav.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
}
// 3. Fragment中导航
class HomeFragment : Fragment() {
private val args: HomeFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
detailButton.setOnClickListener {
// 安全导航
findNavController().navigate(
HomeFragmentDirections.actionHomeToDetail("item123")
)
}
}
}
4.2.3 WorkManager
WorkManager适合执行后台任务,即使应用退出也能保证执行。
基本使用:
// 定义Worker
class UploadWorker(appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
return try {
// 执行上传操作
uploadFile()
Result.success()
} catch (e: Exception) {
Result.retry() // 或 Result.failure()
}
}
private suspend fun uploadFile() {
withContext(Dispatchers.IO) {
// 模拟上传
delay(5000)
}
}
}
// 创建工作请求
class WorkManagerExample {
fun scheduleUpload(context: Context) {
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.setInitialDelay(10, TimeUnit.MINUTES)
.addTag("upload")
.build()
WorkManager.getInstance(context).enqueue(uploadRequest)
}
// 监听进度
fun observeWork(context: Context) {
WorkManager.getInstance(context)
.getWorkInfosByTagLiveData("upload")
.observeForever { workInfos ->
workInfos?.forEach { workInfo ->
when (workInfo.state) {
WorkInfo.State.ENQUEUED -> Log.d("Work", "排队中")
WorkInfo.State.RUNNING -> Log.d("Work", "运行中")
WorkInfo.State.SUCCEEDED -> Log.d("Work", "成功")
WorkInfo.State.FAILED -> Log.d("Work", "失败")
else -> {}
}
}
}
}
}
第五部分:高级特性与实战技巧
5.1 插件化与组件化
5.1.1 组件化架构设计
组件化是大型项目的标准架构,将应用拆分为独立模块。
模块划分:
app (主模块)
├── lib-base (基础库)
│ ├── network (网络)
│ ├── database (数据库)
│ ├── utils (工具类)
│ └── common-ui (通用UI)
├── feature-main (主功能)
├── feature-user (用户模块)
├── feature-shop (商城模块)
└── build-logic (构建逻辑)
模块间通信:
// 方案1:接口隔离 + SPI
// lib-base中定义接口
interface IUserService {
fun getUserInfo(): UserInfo
}
// feature-user中实现
class UserServiceImpl : IUserService {
override fun getUserInfo(): UserInfo {
// 实现
}
}
// 通过SPI加载
object ServiceLoader {
inline fun <reified T> load(): T? {
return ServiceLoader.load(T::class.java).firstOrNull()
}
}
// 方案2:路由框架(ARouter)
// 定义路由
@Route(path = "/user/profile")
class UserProfileActivity : AppCompatActivity()
// 跳转
ARouter.getInstance()
.build("/user/profile")
.withString("userId", "123")
.navigation()
5.1.2 动态加载(插件化)
插件化技术可以实现应用功能的动态更新和扩展。
核心原理:
- 类加载:DexClassLoader加载插件Dex
- 资源加载:AssetManager加载插件资源
- Activity启动:占位Activity + Hook技术
简化示例(仅原理):
// 加载插件Dex
class PluginManager {
private var dexClassLoader: DexClassLoader? = null
fun loadPlugin(context: Context, pluginPath: String) {
// 1. 提取插件到本地
val pluginFile = File(context.cacheDir, "plugin.apk")
File(pluginPath).copyTo(pluginFile, overwrite = true)
// 2. 创建DexClassLoader
val optimizedDir = File(context.cacheDir, "opt_dex")
optimizedDir.mkdirs()
dexClassLoader = DexClassLoader(
pluginFile.path,
optimizedDir.path,
null,
this.javaClass.classLoader
)
}
// 加载插件中的类
fun loadClass(className: String): Class<*>? {
return dexClassLoader?.loadClass(className)
}
}
注意:插件化技术复杂且涉及系统底层,实际项目中建议使用成熟框架如RePlugin、Atlas等。
5.2 AOP编程
5.2.1 AspectJ实现AOP
AOP(面向切面编程)可以在不修改原有代码的情况下,横切关注点(如日志、性能监控、权限检查)。
配置AspectJ:
// build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.9.7'
classpath 'com.hujiang.aspectjx:gradle_plugin:2.0.10'
}
}
apply plugin: 'com.hujiang.aspectjx'
代码示例:性能监控切面
// 定义切面
@Aspect
class PerformanceAspect {
// 切点:所有Activity的onCreate方法
@Pointcut("execution(* android.app.Activity.onCreate(..))")
fun activityOnCreate() {}
// 环绕通知
@Around("activityOnCreate()")
fun measureExecutionTime(joinPoint: ProceedingJoinPoint): Any? {
val start = System.currentTimeMillis()
val signature = joinPoint.signature as MethodSignature
val className = signature.declaringType.simpleName
val methodName = signature.name
try {
// 执行原方法
return joinPoint.proceed()
} finally {
val duration = System.currentTimeMillis() - start
Log.d("Performance", "$className.$methodName took $duration ms")
// 如果超过阈值,上报监控
if (duration > 100) {
// 上报慢方法
}
}
}
}
// 使用:无需修改Activity代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 自动被切面拦截,记录执行时间
}
}
其他切面示例:
// 权限检查切面
@Aspect
class PermissionAspect {
@Pointcut("execution(* com.example..*(..)) && @annotation(RequirePermission)")
fun methodWithPermission() {}
@Around("methodWithPermission()")
fun checkPermission(joinPoint: ProceedingJoinPoint): Any? {
val method = (joinPoint.signature as MethodSignature).method
val annotation = method.getAnnotation(RequirePermission::class.java)
// 检查权限
if (checkSelfPermission(annotation.permission) == PackageManager.PERMISSION_GRANTED) {
return joinPoint.proceed()
} else {
// 请求权限或抛出异常
throw SecurityException("Permission ${annotation.permission} required")
}
}
}
// 自定义注解
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class RequirePermission(val permission: String)
5.3 性能监控与APM
5.3.1 卡顿监控
卡顿监控是性能优化的重要手段,通过监控主线程消息执行时间来检测卡顿。
实现原理:
class BlockMonitor {
private val mainHandler = Handler(Looper.getMainLooper())
private val monitorThread = Thread {
while (true) {
// 向主线程发送任务
val start = System.currentTimeMillis()
mainHandler.post {
// 空任务,只是为了检测主线程是否响应
}
// 等待主线程执行
Thread.sleep(1000)
// 检测是否超时
val duration = System.currentTimeMillis() - start
if (duration > 1500) { // 阈值
// 发生卡顿,获取主线程堆栈
val stackTrace = Looper.getMainLooper().thread.stackTrace
reportBlock(stackTrace)
}
}
}
fun start() {
monitorThread.start()
}
private fun reportBlock(stackTrace: Array<StackTraceElement>) {
// 上报卡顿堆栈
val stackTraceString = stackTrace.joinToString("\n") { it.toString() }
Log.e("BlockMonitor", "Block detected:\n$stackTraceString")
}
}
5.3.2 启动优化
启动优化是提升用户体验的关键,需要从多个维度入手。
优化策略:
- 异步初始化:将非必要初始化放到子线程
- 延迟初始化:首次使用时再初始化
- 类预加载:在Application中预加载常用类
- 启动耗时分析:使用adb命令或systrace
代码示例:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 1. 同步必要初始化
initCrashReport()
// 2. 异步初始化
val startTime = System.currentTimeMillis()
CoroutineScope(Dispatchers.Default).launch {
// 耗时初始化
initDatabases()
initImageLoader()
val cost = System.currentTimeMillis() - startTime
Log.d("AppInit", "Async init cost: $cost ms")
}
// 3. 延迟初始化
MainScope().launch {
delay(3000) // 延迟3秒
initHeavyLibrary()
}
}
private fun initCrashReport() {
// 必要初始化
}
private suspend fun initDatabases() {
// 数据库初始化
}
private suspend fun initImageLoader() {
// 图片库初始化
}
private fun initHeavyLibrary() {
// 重量级库初始化
}
}
// 启动耗时统计
class StartupTracer {
companion object {
private val startupTimes = mutableListOf<Long>()
fun trace(stage: String) {
val time = SystemClock.elapsedRealtime()
startupTimes.add(time)
Log.d("Startup", "$stage: $time")
}
fun printSummary() {
if (startupTimes.size < 2) return
val total = startupTimes.last() - startupTimes.first()
Log.d("Startup", "Total startup time: $total ms")
}
}
}
// 在Application中使用
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
StartupTracer.trace("AppStart")
// ... 初始化代码
StartupTracer.trace("InitComplete")
StartupTracer.printSummary()
}
}
第六部分:面试实战技巧
6.1 简历优化与项目准备
6.1.1 简历撰写要点
技术栈展示:
熟练掌握:Kotlin、Java、Android SDK、Jetpack组件
熟悉:MVVM/MVI架构、组件化、插件化
了解:Flutter、KMM、Compose
项目描述公式:
项目名称 + 技术栈 + 核心职责 + 成果数据
示例:
项目:XX电商App(用户量500万+)
技术:Kotlin + MVVM + Jetpack + 组件化
职责:负责用户模块架构设计,实现组件化拆分
成果:启动时间优化40%,包体积减少30%
6.1.2 项目难点准备
准备3-5个技术难点:
- 复杂UI实现:自定义View、动画优化
- 性能优化:内存泄漏、卡顿、启动优化
- 架构设计:组件化、模块解耦
- 技术选型:为什么选择MVVM而不是MVP
- 团队协作:代码规范、CI/CD
回答模板:
问题背景 → 技术挑战 → 解决方案 → 最终成果 → 反思总结
6.2 算法与数据结构
6.2.1 高频算法题
链表反转:
fun reverseList(head: ListNode?): ListNode? {
var prev: ListNode? = null
var curr = head
while (curr != null) {
val next = curr.next
curr.next = prev
prev = curr
curr = next
}
return prev
}
二叉树层序遍历:
fun levelOrder(root: TreeNode?): List<List<Int>> {
val result = mutableListOf<List<Int>>()
if (root == null) return result
val queue = LinkedList<TreeNode>()
queue.offer(root)
while (queue.isNotEmpty()) {
val levelSize = queue.size
val levelList = mutableListOf<Int>()
repeat(levelSize) {
val node = queue.poll()
levelList.add(node.`val`)
node.left?.let { queue.offer(it) }
node.right?.let { queue.offer(it) }
}
result.add(levelList)
}
return result
}
LRU缓存实现:
class LRUCache(private val capacity: Int) {
private val cache = LinkedHashMap<Int, Int>(capacity, 0.75f, true)
fun get(key: Int): Int {
return cache[key] ?: -1
}
fun put(key: Int, value: Int) {
cache[key] = value
if (cache.size > capacity) {
cache.remove(cache.keys.first())
}
}
}
6.3 行为面试准备
6.3.1 常见问题
“你为什么离开上家公司?”
正面回答:寻求更好的发展空间,希望在技术深度/广度上有所突破
避免:抱怨前公司、薪资是唯一原因
“你的职业规划?”
短期:深入掌握Android核心技术,成为团队技术骨干
中期:扩展技术栈,了解后端、前端,向全栈发展
长期:成为技术负责人,带领团队解决复杂问题
“如何处理技术分歧?”
1. 充分沟通,理解对方观点
2. 技术调研,用数据说话
3. 小范围验证(POC)
4. 团队决策,少数服从多数
5. 持续跟进,及时调整
第七部分:最新技术趋势
7.1 Jetpack Compose
7.1.1 Compose基础
Compose是Android的声明式UI框架,是未来的趋势。
核心概念:
- Composable函数:用@Composable标注,描述UI
- 状态(State):UI随状态变化自动更新
- 重组(Recomposition):状态变化时重新执行Composable
代码示例:
// 基础使用
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
// 状态管理
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text(text = "Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
// 列表
@Composable
fun UserList(users: List<User>) {
LazyColumn {
items(users) { user ->
UserItem(user)
}
}
}
@Composable
fun UserItem(user: User) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.clickable { /* 处理点击 */ }
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = user.name, style = MaterialTheme.typography.h6)
Text(text = user.email, style = MaterialTheme.typography.body2)
}
}
}
7.1.2 Compose与MVVM集成
class UserViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
fun loadUser() {
viewModelScope.launch {
_uiState.value = UserUiState.Loading
try {
val user = repository.getUser()
_uiState.value = UserUiState.Success(user)
} catch (e: Exception) {
_uiState.value = UserUiState.Error(e.message ?: "Unknown error")
}
}
}
}
sealed class UserUiState {
object Loading : UserUiState()
data class Success(val user: User) : UserUiState()
data class Error(val message: String) : UserUiState()
}
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
when (val state = uiState) {
is UserUiState.Loading -> LoadingView()
is UserUiState.Success -> UserDetailView(state.user)
is UserUiState.Error -> ErrorView(state.message)
}
}
7.2 Kotlin Multiplatform Mobile (KMM)
KMM允许在Android和iOS之间共享业务逻辑。
基本结构:
// 共享模块
expect fun platformName(): String
class Greeting {
fun greet(): String {
return "Hello from ${platformName()}"
}
}
// Android实现
actual fun platformName(): String = "Android"
// iOS实现
actual fun platformName(): String = "iOS"
7.3 跨平台技术对比
| 技术 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Flutter | 高性能,热重载,统一UI | 包体积大,生态相对年轻 | 重视UI一致性的应用 |
| React Native | 生态成熟,热更新 | 性能略差,依赖原生桥接 | 需要快速迭代的项目 |
| KMM | 共享业务逻辑,原生UI | iOS开发门槛高 | 业务逻辑复杂的双平台应用 |
总结
安卓开发面试是一个系统性工程,需要扎实的基础、丰富的实践经验和持续的学习能力。本文从语言基础、框架机制、性能优化、架构设计等多个维度进行了全面梳理,并提供了大量实战代码示例。
核心要点回顾:
- 基础为王:Java/Kotlin基础、Android核心机制是面试必考
- 架构思维:MVVM、组件化、模块化是大型项目必备
- 性能意识:内存、UI、网络优化是高级开发的核心竞争力
- 技术前沿:Compose、KMM、跨平台技术是未来趋势
- 实战能力:算法、系统设计、问题解决能力缺一不可
面试准备建议:
- 每天刷LeetCode,保持手感
- 深入理解2-3个开源项目源码
- 准备3-5个技术难点和解决方案
- 模拟面试,练习表达和逻辑
- 关注技术社区,了解最新动态
记住,面试不仅是技术的考察,更是思维方式和学习能力的评估。保持好奇心,持续学习,你一定能在安卓开发的道路上走得更远!
祝你面试顺利,拿到心仪的Offer!
