Android 常见面试题

Android 常见面试题

涵盖四大组件、性能优化、架构、Kotlin、Jetpack 等主流方向


一、四大组件

1. Activity 生命周期

复制代码
onCreate → onStart → onResume → [运行中]
    ↓
onPause → onStop → onDestroy(完全退出)
onPause → onStop → onRestart → onStart(重新回到前台)
onPause → onResume(轻度遮挡,如弹窗)

常见考点:

  • A 跳转 B:A.onPause → B.onCreate → B.onStart → B.onResume → A.onStop
  • 按 Home:onPause → onStop
  • 旋转屏幕:onPause → onStop → onDestroy → onCreate(可用 onSaveInstanceState 保存数据)

2. Activity 启动模式(launchMode)

模式 说明 场景
standard 每次新建实例 默认
singleTop 栈顶已有则复用,回调 onNewIntent 通知跳转详情页
singleTask 栈中已有则复用并清除其上所有 Activity 首页、主界面
singleInstance 独占一个任务栈 来电界面、系统级页面

3. Service 两种启动方式的区别

startService bindService
生命周期 独立于调用者 与调用者绑定
通信 无直接通信 通过 IBinder 通信
停止方式 stopService / stopSelf 所有客户端 unbind 后自动销毁
场景 后台下载、音乐播放 Activity 与 Service 双向通信

4. BroadcastReceiver 静态注册 vs 动态注册

静态注册(Manifest) 动态注册(代码)
生命周期 常驻,APP 未启动也能收到 跟随组件生命周期
注意 Android 8.0+ 限制隐式广播静态注册 需手动 unregisterReceiver
场景 开机广播、安装广播 网络变化、电量变化

5. ContentProvider 的作用

  • 跨进程共享数据(如通讯录、媒体库)
  • 通过 URI 统一访问格式:content://authority/path/id
  • 底层基于 Binder IPC

二、Handler / 消息机制

6. Handler 消息机制原理

复制代码
Handler → sendMessage → MessageQueue(链表)
                              ↓
                         Looper.loop() 轮询
                              ↓
                    Handler.dispatchMessage → handleMessage

核心类:

  • Handler:发送和处理消息
  • Message:消息载体(what / obj / arg1 / arg2)
  • MessageQueue:优先级队列,按 when 排序
  • Looper:轮询 MessageQueue,每个线程最多一个

主线程为什么不会因为 Looper.loop() 阻塞 ANR?

主线程的 Looper 通过 epoll 机制进入休眠,有消息时才唤醒,不占用 CPU,ANR 是因为主线程消息处理超时(5s),不是因为 loop() 本身。


7. Handler 内存泄漏原因及解决

原因: 非静态内部类 Handler 持有外部 Activity 的引用,如果 Message 还在队列中,Activity 无法被 GC。

解决:

kotlin 复制代码
// 使用静态内部类 + WeakReference
class MyHandler(activity: WeakReference<MainActivity>) : Handler(Looper.getMainLooper()) {
    override fun handleMessage(msg: Message) {
        activity.get()?.let {
            // 处理消息
        }
    }
}

// onDestroy 时移除消息
override fun onDestroy() {
    super.onDestroy()
    handler.removeCallbacksAndMessages(null)
}

三、View 体系

8. View 的绘制流程

复制代码
measure(测量宽高)→ layout(确定位置)→ draw(绘制内容)
  • onMeasure:通过 MeasureSpec 计算宽高
  • onLayout:ViewGroup 负责摆放子 View 位置
  • onDraw:实际绘制,使用 Canvas

MeasureSpec 三种模式:

  • EXACTLY:确定大小(match_parent 或具体 dp)
  • AT_MOST:最大不超过(wrap_content)
  • UNSPECIFIED:不限制(ScrollView 内的子 View)

9. View 事件分发机制

复制代码
Activity.dispatchTouchEvent
    → ViewGroup.dispatchTouchEvent
        → ViewGroup.onInterceptTouchEvent(是否拦截)
            → View.dispatchTouchEvent
                → View.onTouchEvent(处理)

返回值含义:

  • true:消费事件,不再向下/向上传递
  • false:不消费,继续传递

解决滑动冲突:

  • 外部拦截法:在 ViewGroup 的 onInterceptTouchEvent 中判断
  • 内部拦截法:在 View 中调用 requestDisallowInterceptTouchEvent

10. RecyclerView 缓存机制

RecyclerView 有 4 级缓存:

缓存层级 名称 说明
第一级 Scrap 屏幕内可见的 View,不需要重新绑定
第二级 CacheViews 默认 2 个,滑出屏幕的 View,不需要重新绑定
第三级 ViewCacheExtension 自定义缓存(很少用)
第四级 RecycledViewPool 按 viewType 缓存,需要重新绑定数据

与 ListView 的区别:

  • RecyclerView 强制使用 ViewHolder
  • 支持多种布局管理器(LinearLayout / Grid / Staggered)
  • 支持局部刷新(notifyItemChanged)
  • 默认支持 Item 动画

四、内存 & 性能优化

11. 内存泄漏常见场景

场景 解决方案
静态变量持有 Context 使用 ApplicationContext 或及时置空
非静态内部类(Handler/AsyncTask) 改为静态内部类 + WeakReference
注册未注销(BroadcastReceiver / EventBus) onDestroy 中注销
资源未关闭(Cursor / IO流) 用完 close()
单例持有 Activity 改为持有 ApplicationContext
Bitmap 未回收 recycle() 或使用 Glide/Picasso 管理

检测工具: LeakCanary、Android Studio Memory Profiler


12. ANR 是什么,如何避免

ANR 触发条件:

  • 主线程 5 秒内未响应输入事件
  • BroadcastReceiver 10 秒内未完成
  • Service 前台 20 秒 / 后台 200 秒

避免方法:

  • 耗时操作放子线程(网络、IO、数据库)
  • 使用 AsyncTask / Coroutine / RxJava
  • StrictMode 检测主线程耗时操作

查看 ANR 日志: /data/anr/traces.txt


13. OOM 的原因及解决

常见原因:

  • Bitmap 加载过大图片
  • 内存泄漏积累
  • 大量对象分配

解决:

kotlin 复制代码
// Bitmap 压缩采样
val options = BitmapFactory.Options().apply {
    inJustDecodeBounds = true
    BitmapFactory.decodeFile(path, this)
    inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
    inJustDecodeBounds = false
}
val bitmap = BitmapFactory.decodeFile(path, options)

// 使用 inBitmap 复用内存
options.inBitmap = reusableBitmap

14. 启动优化

冷启动 / 温启动 / 热启动:

  • 冷启动:进程不存在,从零开始(最慢)
  • 温启动:进程存在但 Activity 被销毁
  • 热启动:进程和 Activity 都存在(最快)

优化方向:

  • Application.onCreate() 中的初始化延迟或异步化(子线程/IdleHandler)
  • 避免 Activity.onCreate() 中耗时操作
  • 主题设置 windowBackground 避免白屏/黑屏
  • 使用 App Startup 库管理依赖初始化顺序

15. 卡顿优化

  • 主线程避免 IO / 大量计算
  • 布局层级不超过 5 层,用 ConstraintLayout 减少嵌套
  • RecyclerView:使用 DiffUtil 替代 notifyDataSetChanged
  • 使用 ViewStub 延迟加载不常用 View
  • 图片加载使用 Glide / Coil(自动处理缓存和生命周期)
  • 使用 Systrace / Perfetto 分析掉帧原因

五、进程间通信(IPC)

16. Android IPC 方式

方式 说明 场景
Binder Android 专有,高效安全 AIDL、系统服务
AIDL 基于 Binder,定义接口 跨进程服务调用
Messenger 基于 AIDL,串行处理消息 简单跨进程通信
ContentProvider 基于 Binder,统一数据访问 跨 APP 共享数据
广播 系统级消息传递 全局事件通知
文件/SharedPreferences 共享存储 简单数据共享
Socket 网络通信 跨设备或本地通信

17. Binder 原理简述

传统 IPC(socket / pipe)需要两次数据拷贝:用户空间 → 内核空间 → 用户空间

Binder 只需一次拷贝 :通过 mmap 将内核空间与目标进程用户空间映射同一块内存,发送方拷贝一次即可。


六、Kotlin 特性

18. Kotlin 协程

kotlin 复制代码
// 启动协程
viewModelScope.launch {
    val result = withContext(Dispatchers.IO) {
        // 耗时操作
        fetchData()
    }
    // 回到主线程更新 UI
    updateUI(result)
}

// 并发执行
viewModelScope.launch {
    val deferred1 = async(Dispatchers.IO) { fetchUser() }
    val deferred2 = async(Dispatchers.IO) { fetchOrders() }
    val user = deferred1.await()
    val orders = deferred2.await()
}

调度器:

  • Dispatchers.Main:主线程,更新 UI
  • Dispatchers.IO:IO 密集型(网络、数据库)
  • Dispatchers.Default:CPU 密集型(排序、计算)

19. Kotlin 常用特性

kotlin 复制代码
// 空安全
val name: String? = null
val len = name?.length ?: 0  // Elvis 操作符
val len2 = name!!.length      // 强制非空(可能 NPE)

// 扩展函数
fun String.isEmail(): Boolean = contains("@")

// 数据类(自动生成 equals/hashCode/copy/toString)
data class User(val name: String, val age: Int)

// 密封类(限制继承)
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val msg: String) : Result()
}

// 委托属性
val viewModel: MyViewModel by viewModels()
var name: String by Delegates.observable("") { _, old, new ->
    println("$old → $new")
}

// 内联函数
inline fun <reified T> Gson.fromJson(json: String): T =
    fromJson(json, T::class.java)

七、Jetpack 组件

20. ViewModel

  • 存储 UI 相关数据,在配置变更(旋转)时存活
  • 不持有 View 引用,避免内存泄漏
  • 通过 LiveData / StateFlow 向 UI 层暴露数据
kotlin 复制代码
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user

    fun loadUser(id: String) {
        viewModelScope.launch {
            _user.value = repository.getUser(id)
        }
    }
}

21. LiveData vs StateFlow / SharedFlow

LiveData StateFlow SharedFlow
粘性事件 可配置
生命周期感知 是(自带) repeatOnLifecycle repeatOnLifecycle
初始值 必须有
适用场景 UI 状态 UI 状态 一次性事件(Toast)

22. Room 数据库

kotlin 复制代码
// Entity
@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey val id: String,
    val name: String,
    val age: Int
)

// DAO
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll(): Flow<List<UserEntity>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: UserEntity)

    @Delete
    suspend fun delete(user: UserEntity)
}

// Database
@Database(entities = [UserEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

  • 统一管理 Fragment 跳转,避免 FragmentManager 手动操作
  • NavController.navigate(R.id.action_home_to_detail)
  • 支持 Safe Args(类型安全传参)
  • 支持 Deep Link

八、架构设计

24. MVC / MVP / MVVM 区别

MVC MVP MVVM
View 与逻辑耦合
可测试性
双向绑定 有(DataBinding)
Android 推荐 一般 推荐

MVVM 在 Android 中:

  • View = Activity / Fragment
  • ViewModel = ViewModel
  • Model = Repository + 数据源

25. Repository 模式

复制代码
View → ViewModel → Repository → (RemoteDataSource / LocalDataSource)
                                        ↓              ↓
                                    Retrofit         Room

Repository 负责数据来源决策(优先缓存?先请求网络?),ViewModel 不关心数据从哪来。


九、网络

26. Retrofit + OkHttp 原理

OkHttp 核心:

  • 拦截器链(Interceptor Chain):LoggingInterceptor → CacheInterceptor → ConnectInterceptor → CallServerInterceptor
  • 连接池复用(减少 TCP 握手开销)
  • 支持 HTTP/2

Retrofit 核心:

  • 动态代理:通过注解生成 HTTP 请求
  • Converter:Gson / Moshi 解析响应体
  • CallAdapter:适配 RxJava / Coroutine
kotlin 复制代码
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: String): User

    @POST("login")
    suspend fun login(@Body request: LoginRequest): LoginResponse
}

27. 图片加载框架(Glide)

Glide 缓存机制:

  • 活动缓存(ActiveResources):当前正在展示的图片
  • 内存缓存(LruCache):最近使用的图片
  • 磁盘缓存(DiskLruCache):本地文件
kotlin 复制代码
Glide.with(context)
    .load(url)
    .placeholder(R.drawable.loading)
    .error(R.drawable.error)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(imageView)

十、安全 & 其他

28. Android 安全机制

  • 沙箱机制:每个 APP 运行在独立进程,分配唯一 UID
  • 权限机制:dangerous 权限需运行时申请(Android 6.0+)
  • 签名验证:APK 必须签名,更新时签名需一致
  • ProGuard / R8:代码混淆,防止反编译

29. 序列化:Serializable vs Parcelable

Serializable Parcelable
实现方式 Java 反射,自动 手动实现(或 @Parcelize)
性能 慢(产生大量临时对象) 快(直接读写内存)
使用场景 数据持久化、网络传输 Intent / Bundle 传递
Android 推荐
kotlin 复制代码
@Parcelize
data class User(val name: String, val age: Int) : Parcelable

30. APK 体积优化

  • 开启 minifyEnabled = true(代码混淆 + 删除无用代码)
  • 开启 shrinkResources = true(删除无用资源)
  • 使用 WebP 替代 PNG
  • 动态下发资源(插件化 / 按需下载)
  • abiFilters 只保留必要 ABI(如只保留 arm64-v8a)
  • 使用 Android App Bundle(AAB)按需分发

共 30 题,涵盖:四大组件 / Handler / View 体系 / 性能优化 / IPC / Kotlin / Jetpack / 架构 / 网络 / 安全

相关推荐
货拉拉技术2 小时前
Hook植入日志协助定位问题方案
android
FlightYe2 小时前
Android投屏MirrorCast全链路
android
Ehtan_Zheng2 小时前
Kotlin const val vs val:字节码、性能与隐藏陷阱详解
android·kotlin
墨狂之逸才2 小时前
Android TV 垃圾应用清理指南
android
源来猿往3 小时前
记ffmpeg-8.1.1 之Android库编译(window)
android·ffmpeg
恋猫de小郭3 小时前
Android 17 正式版发布,全新 AI 和各种破坏性更新
android·前端·flutter
我命由我123453 小时前
Jetpack Room - Room 查询返回列表无需判空、LIKE 关键字
android·java·开发语言·java-ee·android jetpack·android-studio·android runtime
朝星4 小时前
Android开发[14]:网络优化之OkHttp
android·okhttp·kotlin
私人珍藏库4 小时前
[Android] FX Player-安卓全格式播放器-比MX播放器好用
android·学习·工具·软件·多功能