小米架构师面经:状态管理方案选型、全局异常处理、组件化路由框架设计要点

18 阅读5分钟

小米架构师面试:小米Android开发覆盖MIUI系统定制、IoT设备互联。技术栈偏底层,面试官喜欢问系统级原理和性能数据。技术方向偏向MIUI定制、IoT互联。特色专项包括MIUI系统定制与IoT设备开发。今天8道题覆盖小米架构师面试核心考点。

Q1:状态管理方案选型?

Android状态管理的演进:单组件状态→LiveData/StateFlow(ViewModel)→页面级状态→MVI State(合并多个状态源)→全局状态→Redux/Unidirectional Data Flow。选型关键看状态的作用域和复杂度。简单场景用ViewModel+StateFlow,复杂多页面共享用MVI+Store。绝对不要用EventBus做状态管理——不可追溯、不可预测、调试地狱。

class SearchViewModel(private val repo: SearchRepo) : ViewModel() {
    private val _query = MutableStateFlow("")
    val searchResults: StateFlow<List<Item>> = _query
        .debounce(300).filter { it.length >= 2 }
        .flatMapLatest { repo.search(it) }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
}

追问:Unidirectional Data Flow的核心约束?状态只能通过Intent/Event改变,View不直接改状态——状态流向是单向的。

Q2:全局异常处理和崩溃恢复方案?

崩溃恢复三步走:1)全局捕获——Thread.setDefaultUncaughtExceptionHandler捕获Java未处理异常,上报Bugly/友盟;2)重启——崩溃后跳转到安全页或重启App(ProcessPhoenix);3)Native崩溃——用breakpad捕获C/C++崩溃。ANR监控:独立线程定时post消息到主线程,超时未执行则dump堆栈上报。注意:捕获后不能直接System.exit——可能造成ANR,应该先保存状态再退出。

class CrashHandler : Thread.UncaughtExceptionHandler {
    private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
    override fun uncaughtException(t: Thread, e: Throwable) {
        CrashReport.postCatchException(e)
        // 保存崩溃状态后交给默认处理器
        defaultHandler?.uncaughtException(t, e)
    }
}

追问:为什么不能在UncaughtExceptionHandler中直接restart Activity?此时App可能处于不稳定状态,直接跳转可能引发连锁崩溃。应该重启进程。

Q3:组件化路由框架设计要点?

组件化路由不只是ARouter——核心是解耦。设计要点:1)路由表——编译期APT生成(ARouter)或运行时注册(asm);2)服务发现——接口下沉Common模块,各组件注册实现(SPI);3)跨组件通信——事件总线(RxBus/Flow SharedFlow)或接口调用;4)独立运行——每个组件可作为独立App编译调试(isModuleDebug开关);5)资源冲突——resourcePrefix前缀避免重名。

// gradle.properties 动态切换
isModuleDebug=false
// build.gradle
if (isModuleDebug.toBoolean()) apply plugin: 'com.android.application'
else apply plugin: 'com.android.library'

追问:组件化最大的坑是什么?资源冲突——不同模块可能有同名layout/string,用resourcePrefix强制前缀。其次是依赖传递混乱。

Q4:插件化/热修复的核心技术?

插件化本质:宿主加载未安装的APK(插件)中的类和资源。关键技术:1)类加载——DexClassLoader加载插件DEX(处理ClassLoader隔离);2)资源——AssetManager.addAssetPath加载插件资源;3)四大组件——占坑Activity在宿主AndroidManifest预注册,用Hook AMS/Instrumentation代理插件组件。

val dexClassLoader = DexClassLoader(
    pluginApkPath, optimizedDir, null, parentClassLoader
)
val clazz = dexClassLoader.loadClass("com.plugin.PluginActivity")

追问:Google为什么不推荐插件化?Android 9+限制隐藏API访问、签名校验严格、Google力推App Bundle动态分发。

Q5:设计模式在Android中的实际应用?

不是背23种设计模式的定义,而是理解它们在Android源码中的应用:观察者模式——LiveData/Flow/RecyclerView.Adapter.notifyDataSetChanged;建造者模式——AlertDialog.Builder/OkHttpClient.Builder;适配器模式——RecyclerView.Adapter把数据转换为View;单例模式——Application/OkHttpClient(一个App一个实例);装饰者模式——ContextWrapper层层包装。

val dialog = AlertDialog.Builder(context)
    .setTitle("提示")
    .setMessage("确认删除?")
    .setPositiveButton("确认") { _, _ -> delete() }
    .setNegativeButton("取消", null).create()

追问:ContextWrapper的装饰者链?Application→ContextImpl→ContextWrapper→ContextThemeWrapper→Activity,每一层添加不同职责。

Q6:Clean Architecture分层和Android适配?

经典三层(领域驱动):Presentation(UI+ViewModel)→Domain(UseCase+Repository接口,纯Kotlin无Android依赖)→Data(Repository实现+DataSource,Room/Retrofit)。核心原则:依赖方向向内——外层依赖内层,内层不知道外层存在。Domain层不引Android框架——便于单元测试和跨平台。

// Domain层纯Kotlin,无Android依赖
class GetUserUseCase(private val repo: UserRepository) {
    suspend operator fun invoke(id: String): Result<User> {
        return try { Result.success(repo.getUser(id)) }
            catch (e: Exception) { Result.failure(e) }
    }
}
interface UserRepository { suspend fun getUser(id: String): User }

追问:Clean Architecture在Android中的具体映射?Presentation=app模块,Domain=domain纯Kotlin模块,Data=data模块依赖domain。

Q7:响应式架构:从LiveData到SharedFlow的迁移?

LiveData 生命周期感知、主线程、粘性,适合 UI 观察单一状态;短板是无法背压、不支持独立多订阅者、缺少 cold 流。迁移路径:①单一状态用 StateFlow 替代(需初始值、无粘性、可在任意线程赋值);②一次性事件用 SharedFlow(replay=0、extraBufferCapacity 防丢)替代 SingleLiveEvent;③ViewModel 暴露 StateFlow,UI 在 lifecycleScope 用 repeatOnLifecycle 收集,避免空安全泄漏;④渐进迁移用 asLiveData() 或 liveData { emitSource(flow) } 兼容老代码。注意线程:数据流用 flowOn(Dispatchers.IO) 切换。

追问:StateFlow 和 SharedFlow 怎么选?单状态用 StateFlow,事件流用 SharedFlow。

Q8:DDD 在客户端的应用?

DDD 的实体/值对象/聚合根映射到端的领域模型,用 Repository 隔离数据来源(网络/DB/缓存),用 UseCase/Interactor 封装业务规则,UI 只做展示。复杂业务(如交易、IM)用它理清“状态机+边界”,避免 Activity 里堆烂逻辑。

追问:端上 DDD 和 MVVM 冲突吗?不冲突,MVVM 管 UI 数据流,DDD 管业务内核,Repository/UseCase 正好是 MVVM 的 domain 层。

下一篇进入小米Java基础专项——GC算法、synchronized、动态代理的实现原理。

觉得有用的同学扣1,你面试被追问最狠的是哪道题?

本系列连载中,关注不迷路,下一篇:小米Java基础专项面经:GC算法、synchronized、动态代理的实现原理

系列简介:Android大厂面经连载,覆盖字节跳动、腾讯、阿里、美团等40+企业,从初级到架构师全岗位覆盖。每篇文章包含真实面试题+详细答案+代码示例,帮你拿到大厂Offer。