Android 高频面试题与解答

Android 高频面试题与解答

    • 第一部分:题目清单(自测用)
    • 第二部分:逐题解答
      • [Q1. Activity 完整生命周期?](#Q1. Activity 完整生命周期?)
      • [Q2. A 跳 B 生命周期调用顺序?](#Q2. A 跳 B 生命周期调用顺序?)
      • [Q3. 四种启动模式区别?](#Q3. 四种启动模式区别?)
      • [Q4. onSaveInstanceState 何时调用?](#Q4. onSaveInstanceState 何时调用?)
      • [Q5. Service 与 IntentService 区别?](#Q5. Service 与 IntentService 区别?)
      • [Q6. 前台服务为什么必须通知?](#Q6. 前台服务为什么必须通知?)
      • [Q7. 广播静态注册限制(Android 8+)?](#Q7. 广播静态注册限制(Android 8+)?)
      • [Q8. Handler 原理?子线程能用吗?](#Q8. Handler 原理?子线程能用吗?)
      • [Q9. 为什么 Handler 会内存泄漏?](#Q9. 为什么 Handler 会内存泄漏?)
      • [Q10. Looper.loop 为什么不会阻塞主线程?](#Q10. Looper.loop 为什么不会阻塞主线程?)
      • [Q11. 简述 MessageQueue 的阻塞(epoll)?](#Q11. 简述 MessageQueue 的阻塞(epoll)?)
      • [Q12. SharedPreferences 的 apply 与 commit 区别?](#Q12. SharedPreferences 的 apply 与 commit 区别?)
      • [Q13. 内部存储与外部存储区别?](#Q13. 内部存储与外部存储区别?)
      • [Q14. 分区存储是什么?](#Q14. 分区存储是什么?)
      • [Q15. OkHttp 拦截器有几种?区别?](#Q15. OkHttp 拦截器有几种?区别?)
      • [Q16. Retrofit 原理?](#Q16. Retrofit 原理?)
      • [Q17. 为什么 AsyncTask 被废弃?](#Q17. 为什么 AsyncTask 被废弃?)
      • [Q18. 协程相比线程优势?](#Q18. 协程相比线程优势?)
      • [Q19. 常见内存泄漏场景?](#Q19. 常见内存泄漏场景?)
      • [Q20. LeakCanary 原理?](#Q20. LeakCanary 原理?)
      • [Q21. Bitmap 如何防止 OOM?](#Q21. Bitmap 如何防止 OOM?)
      • [Q22. 启动优化手段?](#Q22. 启动优化手段?)
      • [Q23. 布局过度绘制如何排查?](#Q23. 布局过度绘制如何排查?)
      • [Q24. ViewModel 为什么不会被重建销毁?](#Q24. ViewModel 为什么不会被重建销毁?)
      • [Q25. Kotlin 空安全怎么实现?](#Q25. Kotlin 空安全怎么实现?)

定位:Android 开发岗面试自测与背诵。

用法:第一部分是纯题目清单,先遮住答案自己答一遍;第二部分逐题对照解答。


第一部分:题目清单(自测用)

  1. Activity 完整生命周期?
  2. A 跳 B 生命周期调用顺序?
  3. 四种启动模式区别?
  4. onSaveInstanceState 何时调用?
  5. Service 与 IntentService 区别?
  6. 前台服务为什么必须通知?
  7. 广播静态注册限制(Android 8+)?
  8. Handler 原理?子线程能用吗?
  9. 为什么 Handler 会内存泄漏?
  10. Looper.loop 为什么不会阻塞主线程?
  11. 简述 MessageQueue 的阻塞(epoll)?
  12. SharedPreferences 的 apply 与 commit 区别?
  13. 内部存储与外部存储区别?
  14. 分区存储是什么?
  15. OkHttp 拦截器有几种?区别?
  16. Retrofit 原理?
  17. 为什么 AsyncTask 被废弃?
  18. 协程相比线程优势?
  19. 常见内存泄漏场景?
  20. LeakCanary 原理?
  21. Bitmap 如何防止 OOM?
  22. 启动优化手段?
  23. 布局过度绘制如何排查?
  24. ViewModel 为什么不会被重建销毁?
  25. Kotlin 空安全怎么实现?

第二部分:逐题解答

Q1. Activity 完整生命周期?

核心结论onCreate → onStart → onResume → onPause → onStop → onDestroyonRestart 在重新回到前台时触发。

复制代码
onCreate → onStart → onResume  (前台可交互)
   ↑                   ↓
onRestart ← onStop ← onPause   (被遮挡)
   ↓
onDestroy
  • onCreate :创建,做初始化、setContentView(只调一次)。
  • onStart:已可见,但还不能交互。
  • onResume:位于前台、可交互,此时才真正获取焦点。
  • onPause :失去焦点(被新页面部分遮挡),做轻量暂停与数据保存,不能耗时,否则拖慢下一个页面启动。
  • onStop:完全不可见,释放重量资源、注销监听。
  • onDestroy:销毁,回收资源。
  • onRestartonStop 之后重新回到前台时先触发,再走 onStart

Q2. A 跳 B 生命周期调用顺序?

核心结论A.onPause → B.onCreate→onStart→onResume → A.onStop,保证新页面先起来、旧页面后停。

复制代码
启动 A      : A.onCreate → A.onStart → A.onResume
A 跳 B(标准): A.onPause → B.onCreate → B.onStart → B.onResume → A.onStop
按返回       : B.onPause → A.onRestart → A.onStart → A.onResume → B.onStop → B.onDestroy
  • 为什么要这个顺序A.onPause 先执行,系统确认 A 能暂停后才会启动 B,所以 onPause 里做耗时操作会直接拖慢 B 的启动。
  • 特例 :若 B 是透明主题或 Dialog 主题,A 只走到 onPause不会 执行 onStop
  • 横竖屏切换 (未配置 configChanges):onPause→onStop→onDestroy→onCreate→onStart→onResume,数据用 ViewModel / onSaveInstanceState 保存。

Q3. 四种启动模式区别?

核心结论standard 每次新建、singleTop 栈顶复用、singleTask 栈内唯一、singleInstance 独占任务栈。

模式 行为
standard 每次新建实例,可存在多个,谁启动就进谁的栈
singleTop 已在栈顶 则复用,走 onNewIntent,不新建
singleTask 栈内唯一,把其上的 Activity 全部出栈,走 onNewIntent
singleInstance 独占一个任务栈,该栈内只有它,供多应用共享
  • 复用时 都回调 onNewIntent(intent),需要在其中手动 setIntent(intent) 更新数据。
  • singleTask 常配合 taskAffinity 指定任务栈;用于主页、登录页等"只应存在一个"的页面。
  • 另有一组 Intent flag 可覆盖清单声明:FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_SINGLE_TOPFLAG_ACTIVITY_CLEAR_TOP

Q4. onSaveInstanceState 何时调用?

核心结论 :在 Activity 非用户主动销毁、可能被重建之前调用,用于保存瞬态 UI 状态,不是生命周期的必经回调。

  • 会触发:按 HOME、跳到其他应用、横竖屏切换、系统内存不足回收。
  • 不会触发:用户按返回键主动退出(视为正常销毁,状态本就该丢弃)。
  • 时机 :Android 9(API 28)之前相对 onPause 的顺序不固定,Android 9+ 保证在 onStop 之后,不要依赖它的精确位置。
  • 恢复onCreate(savedInstanceState)onRestoreInstanceState()(在 onStart 之后,有 Bundle 才调)。
kotlin 复制代码
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putString("keyword", keyword)
}
  • 限制 :数据经 Binder 事务传输,总量建议 < 50KB(超过有 TransactionTooLargeException 风险);大数据用 ViewModel 或持久化存储。

Q5. Service 与 IntentService 区别?

核心结论 :Service 是默认跑在主线程的通用组件,IntentService 是自带单线程的串行任务 Service,且已废弃

  • Service 生命周期onCreate → onStartCommand → onDestroybindService 时走 onBind,随调用者解绑而销毁。
  • Service 默认运行在主线程,耗时操作必须自己开线程。
  • IntentService :内部封装 HandlerThread,在子线程按串行顺序 执行 onHandleIntent,任务全部完成后自动 stopSelf
  • 废弃原因 :Android 8+ 对后台服务严格限制,IntentService 的后台模型已不适用。
  • 替代方案 :Kotlin 协程 + WorkManager;需要自带 Looper 的子线程直接用 HandlerThread

Q6. 前台服务为什么必须通知?

核心结论 :前台服务是"用户可感知"的服务,Android 8+ 强制要求 startForeground() 时携带一条常驻通知,否则系统抛异常终止应用。

  • 目的:让用户明确知道有任务在跑(如音乐播放、导航、下载),并能从通知栏查看/停止,避免应用在后台偷偷常驻。
  • 代价与收益:正因为"用户可见",前台服务进程优先级更高,不易被系统回收;后台服务在 Android 8+ 会被限制甚至被杀。
  • 用法startForeground(id, notification),停止时 stopForeground(true)
  • 权限 :Android 9+ 需要声明 FOREGROUND_SERVICE;Android 13+ 通知还要 POST_NOTIFICATIONS 运行时权限。
  • 可延迟、可约束的后台任务优先用 WorkManager,别滥用前台服务。

Q7. 广播静态注册限制(Android 8+)?

核心结论 :Android 8(API 26)起,Manifest 里静态注册的隐式广播绝大部分收不到,只有系统白名单例外。

  • 原因:大量应用静态监听开机、网络变化等广播,一发广播就全场唤醒,严重耗电卡顿,故从源头限制。
  • 仍可静态注册的例外 (白名单):BOOT_COMPLETEDLOCKED_BOOT_COMPLETEDTIME_SET/时区变化、LOCALE_CHANGED、短信彩信类广播等。
  • 不受限 :显式广播(明确指定 setPackage / 目标类名)依然可以静态注册并送达。
  • 实践 :应用内通信一律用动态注册 registerReceiver,并在对应生命周期 unregister 防泄漏;更推荐 LiveData / 事件总线 / 协程流。

Q8. Handler 原理?子线程能用吗?

核心结论 :Handler 负责"发送 + 处理",由 Looper 循环从 MessageQueue 取消息回调;子线程能用,但必须自己 prepare + loop

  • 四件套Handler(发送/处理)、Looper(循环)、MessageQueue(队列)、Message(消息)。
  • 流程sendMessageenqueueMessage 入队 → Looper.loop() 取消息 → msg.target.dispatchMessagehandleMessage
  • 主线程ActivityThread.main() 里已调用 Looper.prepareMainLooper(),直接 new Handler 即可。
kotlin 复制代码
// 子线程使用
Looper.prepare()
val handler = Handler(Looper.myLooper()!!) { msg -> /* 处理 */ true }
Looper.loop()          // 死循环,之后的代码不会执行
handler.looper.quitSafely()   // 用完必须退出,否则线程永不结束
  • 省事做法HandlerThread 就是自带 Looper 的子线程;postDelayed 的延时靠 MessageQueue 的 when 字段排序实现。

Q9. 为什么 Handler 会内存泄漏?

核心结论:非静态内部类/匿名 Handler 隐式持有 Activity,而 Message 又持有 Handler,队列中未处理完的消息会让 Activity 一直无法回收。

  • 引用链MessageQueue → Message → msg.target(Handler) → Activity
  • 典型场景postDelayed(1000 * 60) 后立刻退出页面,消息还在队列里,泄漏会持续到消息执行完。
  • 解决方案 :静态内部类 + WeakReference<Activity>;或在 onDestroyhandler.removeCallbacksAndMessages(null) 清空队列。
  • 检测:接入 LeakCanary,或 Android Profiler 抓 HPROF 查看 GC Root 引用链。

Q10. Looper.loop 为什么不会阻塞主线程?

核心结论loop() 确实是死循环,但没消息时会阻塞在 MessageQueue.next() 的 epoll 等待上让出 CPU,有消息时被唤醒分发,不会空耗资源。

  • 它是应用的动力源:UI 绘制、触摸事件、四大组件生命周期回调,全都是一条条 Message 靠这个循环驱动执行。
  • 真正导致"卡"的是某条消息执行太久(超过 16ms 掉帧、超过 5s ANR),而不是循环本身。
  • 一旦 loop 退出 (如调用 Looper.quit()),主线程会走到异常分支终止,所以 ActivityThread.main()loop() 之后的代码只是兜底抛异常。
  • IdleHandler:队列空闲时触发,常用于启动优化里把非必要任务延迟到首帧之后执行。

Q11. 简述 MessageQueue 的阻塞(epoll)?

核心结论 :Java 层用 synchronized 保证入/出队互斥,等待则由 native 层的 epoll 监听 eventfd 完成------没消息就睡眠,有人写入事件就唤醒。

  • next() 主循环:先 nativePollOnce(ptr, timeoutMillis) 阻塞,再取队头消息。
  • timeoutMillis 取值-1 队列为空、无限等待;0 立即返回;>0 等到最近一条延时消息的触发时刻。
  • 唤醒enqueueMessage 时发现新消息插到队头或需要立即处理,调用 nativeWake 往 eventfd 写 1,epoll 监听到可读事件 → next() 返回继续分发。
  • 这也是 postDelayed 能按时触发的原因;但受系统调度影响,不保证绝对精确。

Q12. SharedPreferences 的 apply 与 commit 区别?

核心结论apply 异步落盘、无返回值;commit 同步落盘、返回是否成功。

对比项 apply commit
落盘方式 异步(先改内存,再排队写盘) 同步(当场写完)
返回值 boolean
是否阻塞主线程
数据风险 进程立刻被杀可能丢失 可靠
  • 建议 :主线程一律用 apply;只有需要确认写盘结果(写后立即退出、跨进程交接)才用 commit
  • 其他坑 :不支持跨进程可靠读写(MODE_MULTI_PROCESS 已废弃),跨进程用 ContentProvider / MMKV。
  • 全量 XML 会被整体加载进内存,不要存大数据或高频字段;新项目迁 DataStore

Q13. 内部存储与外部存储区别?

核心结论:内部存储私有、随卸载清除、无需权限;外部存储共享、需权限,Android 10+ 受分区存储约束。

对比项 内部存储 外部存储
路径 /data/data/包名/ /sdcard/
访问 openFileOutput / getFilesDir / getCacheDir getExternalFilesDir / MediaStore
权限 无需 公共目录需存储权限
卸载 一并清除 专属目录清除,公共目录保留
空间
  • 应用专属外部目录getExternalFilesDir)自 Android 4.4+ 免权限,但仍随卸载删除。
  • 选择策略:私密小数据放内部;大文件/缓存放应用专属外部目录;需要共享或持久保留的媒体用 MediaStore / SAF 系统选择器。

Q14. 分区存储是什么?

核心结论:Android 10(API 29)起默认限制应用访问外部公共存储,只能自由读写自己的应用专属目录,公共文件须经 MediaStore / SAF 访问。

  • 目的:杜绝应用在外置存储乱建目录、乱扫他人文件,解决存储混乱与隐私泄漏。
  • 访问方式getExternalFilesDir() 专属目录免权限;图片/视频/音频等公共媒体走 MediaStore;任意文档走 SAF(系统文件选择器,无需权限)。
  • 过渡 :Android 10 可用 android:requestLegacyExternalStorage="true" 临时关闭,Android 11+ 强制生效
  • 需要整盘访问(文件管理器类)要申请 MANAGE_EXTERNAL_STORAGE 特殊权限,应用市场上架需说明用途。

Q15. OkHttp 拦截器有几种?区别?

核心结论 :分应用拦截器网络拦截器两大类,按责任链依次执行。

复制代码
自定义应用拦截器 → RetryAndFollowUp → Bridge → Cache → Connect
                → 自定义网络拦截器 → CallServer(真正发起请求)
  • 应用拦截器addInterceptor):整个请求只调一次(重试/重定向不重复),可短路直接返回自己构造的 Response,适合加公共 Header、鉴权、统一日志。
  • 网络拦截器addNetworkInterceptor):每次真实网络交互都调,能看到重定向与重试全过程,可读取 ConnectionCache-Control 等头,适合抓包调试。
  • 内置拦截器职责Bridge 补 Header + 自动 Gzip;Cache 按服务端缓存头决定是否复用;Connect 负责连接池复用与建连。

Q16. Retrofit 原理?

核心结论 :用动态代理把接口方法解析成 HTTP 请求描述,交给 OkHttp 执行,再用 Converter 解析响应。

  • 动态代理create() 时用 Proxy.newProxyInstance 生成代理对象,所有方法调用都落到 InvocationHandler.invoke()
  • 解析与缓存 :把 Method + 注解(@GET/@POST/@Query/@Body/@Path)解析成 ServiceMethod 并缓存,避免重复反射。
  • 两个扩展点CallAdapter 适配返回类型(Call / Observable / suspend),ConverterFactory(Gson/Moshi)负责序列化与反序列化。
  • 执行 :最终构造 OkHttp 的 Request,由 Call 发出;Kotlin suspend 函数由 Retrofit 内部走挂起实现,无需返回 Call。
  • 一句话:注解描述请求 → 动态代理生成实现 → OkHttp 干活。

Q17. 为什么 AsyncTask 被废弃?

核心结论:API 30 起官方标记废弃,因为它把太多坑藏在了便利 API 背后,推荐改用协程 / Executor。

  • 内存泄漏 :非静态内部类持有 Activity,doInBackground 未结束时 Activity 无法回收。
  • 屏幕旋转后回调失效onPostExecute 可能作用于已销毁的 Activity,或结果直接丢失。
  • 行为不一致:串行/并行策略随系统版本反复变化(1.6 串行 → 2.3 并行 → 3.0 又串行),难以预期。
  • 生命周期无感知cancel()doInBackground 仍会跑完,无法真正中断。
  • 替代 :Kotlin 协程(结构化并发 + viewModelScope),后台可靠任务用 WorkManager

Q18. 协程相比线程优势?

核心结论 :协程是运行在线程之上的轻量任务,挂起不阻塞线程,且结构化并发让取消与生命周期管理天然安全。

  • 轻量:一个线程可承载上万协程,切换只保存/恢复 Continuation,无内核态切换;线程默认栈在 MB 级,创建成本高。
  • 同步写法suspend 让异步代码写成顺序代码,告别回调地狱。
  • 结构化并发CoroutineScope 统一管辖,父协程取消则子协程自动取消,规避泄漏。
  • 调度器Dispatchers.Main / IO / Default 一行切换线程,配合 viewModelScope / lifecycleScope 随页面自动销毁。
  • 与 RxJava 对比:协程更轻、可读性更好;RxJava 操作符更全、老项目存量大,可按场景共存。

Q19. 常见内存泄漏场景?

核心结论:本质是"短生命周期对象被长生命周期对象持有",Android 里最典型就是 Activity 被持有。

  • 静态变量持有 :单例、companion object 里存 Activity / Context / View → 改用 applicationContext
  • 非静态内部类:Handler、Thread、AsyncTask 隐式持有外部类 → 改静态 + 弱引用。
  • 未注销监听 :广播、EventBus、Sensor、回调、RxJava 订阅 → 对应生命周期反注册。
  • 资源未关闭 :Cursor、Stream、Bitmap、Typeface
  • 其他WebView 未销毁、属性动画未 cancelDialog 窗口泄漏。
  • 检测:LeakCanary 常规监控,Android Profiler 抓 HPROF 看引用链。

Q20. LeakCanary 原理?

核心结论 :在对象"本该被回收"的时机用弱引用 + ReferenceQueue 探测,存活则 dump 堆快照并解析出最短引用链。

  • 监听时机Activity.onDestroy / Fragment.onDestroyView 之后,把对象包进 WeakReference 并关联 ReferenceQueue
  • 探测 :触发 GC 并等待一小段时间,若对象未进入 ReferenceQueue,再二次确认后判定为泄漏。
  • 分析Debug.dumpHprofData 生成 hprof,用 Shark 解析,从 GC Root 搜索到泄漏对象的最短路径。
  • 展示:按应用/泄漏类分组,展示完整引用链并推送通知。
  • 只在 debugImplementation 引入,release 构建中是 no-op。

Q21. Bitmap 如何防止 OOM?

核心结论:按需加载合适尺寸 + 降格式 + 复用内存 + 及时释放,优先交给生命周期感知的图片库。

  • 采样inJustDecodeBounds = true 先只读尺寸,按控件实际大小计算 inSampleSize 再解码。
  • 降格式 :无透明通道场景用 RGB_565 代替 ARGB_8888,内存减半。
  • 复用inBitmap 配合 LruCache 复用内存块,减少 GC 抖动(4.4+ 要求尺寸不小于被复用图)。
  • 放对目录 :按密度放 drawable-xxhdpi 等,避免低密度图在高密度机上被放大数倍。
  • 释放 :Android 8 之前需手动 recycle;8+ 像素数据放 native,由 GC 管理。
  • 工程实践 :统一用 Glide / Coil(内存 + 磁盘缓存、跟随生命周期清理);长图用 BitmapRegionDecoder 局部加载。

Q22. 启动优化手段?

核心结论 :先量化冷/温/热启动耗时,再把启动路径上的任务异步化、延迟化、懒加载

  • 冷启动链路:加载并启动 App → 空白 Window → 创建 Application → 启动 Activity → 首帧绘制。
  • 视觉优化 :给启动 Activity 设 windowBackground 占位图,消除白屏/黑屏。
  • 初始化治理 :三方 SDK 按优先级拆分,能并行的用启动器或协程并行,非必需的用 IdleHandler 挪到首帧之后。
  • 代码侧 :合并各 SDK 借 ContentProvider 自启动的初始化(用 App Startup),避免主线程 IO 与锁等待,关注 MultiDex 开销。
  • 度量adb shell am start -WThisTime/TotalTime,或用 Perfetto 与埋点长期监控。

Q23. 布局过度绘制如何排查?

核心结论:用开发者选项的"调试 GPU 过度绘制"肉眼定位,再用 Layout Inspector 拆层级、逐个消掉重复背景。

  • 开启:开发者选项 → 调试 GPU 过度绘制 → 显示过度绘制区域;颜色由浅到深代表 1x~4x+,目标是把多数区域压到原色/蓝色。
  • 常见成因:多层不透明背景叠加(windowBackground 与根布局重复)、父容器已设背景子 View 又设、不可见 View 仍在绘制、复杂 shape 与阴影。
  • 解决 :移除多余背景、用 ConstraintLayout 减少嵌套、<merge> 合并层级、<ViewStub> 懒加载、自定义 View 用 clipRect 裁剪绘制区域。
  • 配合"Profile GPU Rendering"看每帧耗时,或用 Choreographer 做掉帧埋点定位卡顿。

Q24. ViewModel 为什么不会被重建销毁?

核心结论 :ViewModel 存在 ViewModelStore 中,配置变更时系统通过 NonConfigurationInstances同一个 Store 交给新 Activity,所以旋屏后取回的是同一实例。

  • 流程 :配置变更 → ActivityThreadActivityClientRecord 中保留 lastNonConfigurationInstances → 新 Activity 在 onAttach/onCreate 时取回旧的 ViewModelStoreViewModelProvider 从 Store 中命中原对象。
  • 何时真正清空ComponentActivityON_DESTROY 时判断 isChangingConfigurations(),只有非配置变更 (真正 finish)才调 ViewModelStore.clear();进程被杀也会清空。
  • 注意 :ViewModel 生命周期长于 Activity,严禁持有 View / Activity 引用 ;需要 Context 时用 AndroidViewModelapplication

Q25. Kotlin 空安全怎么实现?

核心结论 :靠类型系统把"可空 / 非空"分开,在编译期 拦截空指针;对非空参数还会在字节码中插入 Intrinsics.checkNotNullParameter 做运行时兜底校验。

  • 声明String 不可为 null,String? 可为 null,把 null 赋给非空类型直接编译报错。
  • 操作符?. 安全调用、?: Elvis 给默认值、!! 强制断言(NPE 抛在调用处,慎用 )、as? 安全转换、配合 ?.let {}
  • 智能转换 :判空后自动转为非空类型(var 需注意并发修改);lateinit var 延迟初始化,未初始化就访问抛 UninitializedPropertyAccessException
  • 边界 :与 Java 互调时 Java 类型是平台类型(如 String!),编译器不强制,需自行判空;反射、泛型擦除仍可能绕过检查。
相关推荐
MyBili1 小时前
【安卓开发/搞机】快图浏览(QuickPic)技术解析:为何这款3MB应用仍是本地图库的性能天花板?
android·app·安卓·文件管理·相册·看图软件·手机相册
事圆则缓1 小时前
Kotlin 高阶工程化与 Android 深入实践
android·开发语言·kotlin
zhangphil1 小时前
AI大模型生成maxTokens 与上下文context
android·llama
淡淡的香烟2 小时前
Androidiot开发之猫脸识别
android·物联网
2501_915106322 小时前
iOS数据采集技术详解:从性能监控到崩溃分析的全链路实践
android·ios·小程序·https·uni-app·iphone·webview
天空之城--11 小时前
Android Koin 完全指南:从原理到实践
android
zhangphil12 小时前
Android main thread主线程Choreographer doFrame发生FullSuspendCheck
android
TimeFine15 小时前
智能眼镜开发:获取真实的音频路由
android
pengyu16 小时前
【Kotlin 协程修仙录 · 渡劫境 · 中阶】 | 造化神兵:自定义 CoroutineDispatcher 与调度器的终极定制
android·kotlin