PicPat Android 项目深挖面试知识点整理

整理自项目模拟面试,覆盖桌面 Widget 架构、图片与视频链路、点击与动画实现、Firebase Storage 媒体处理、相机方案与工程取舍


一、PicPat 是一个什么样的项目

一句话

客户端最核心的挑战

面试里可以怎么讲职责


二、Widget 的整体架构是怎么设计的

一句话

为什么不把逻辑直接写在 AppWidgetProvider 里

具体怎么拆层

  1. BasePhotoWidget
  2. 负责承接系统回调,例如 onUpdateonAppWidgetOptionsChangedonDeleted
  3. 不直接拼 UI,而是转发给 WidgetUpdateService

  4. WidgetUpdateService

  5. 统一收口 widget 的各种更新入口
  6. 根据 action 决定走:
  7. 全量更新
  8. 局部更新
  9. 菜单态更新
  10. 视频播放更新
  11. 恢复正常态

  12. WidgetUpdater

  13. 抽象每一类 widget 的更新逻辑
  14. 不同 provider 通过 @RemoteViewsUpdater(...) 绑定不同 updater
  15. 例如:
  16. PhotoWidgetUpdater
  17. MiddlePhotoWidgetUpdater
  18. BigPhotoWidgetUpdater
  19. Mix42WidgetUpdater
  20. Mix44WidgetUpdater

  21. LayerChain

  22. 把复杂 widget 视图拆成多个 layer 按顺序拼装
  23. 例如:
  24. EventLayer
  25. LikeLayer
  26. AvatarLayer
  27. VideoLayer
  28. PendantLayer
  29. FrameLayer
  30. PhotoLayer
  31. BackgroundLayer
  32. BasisLayer

这样设计的价值


三、Widget 是如何完成系统添加、展示和更新的

系统添加阶段

首次展示阶段

后续更新来源

面试里可强调的点


四、WidgetUpdater 是如何更新 Widget 的

一句话

更新流程

  1. WidgetUpdateService 根据 provider 找到具体 updater
  2. updater 根据 widgetId 从本地状态中拿到当前数据
  3. 组装成 WidgetInfo
  4. getRemoteView(...) 生成完整 RemoteViews
  5. AppWidgetManager.updateAppWidget(...) 完成全量更新

局部更新怎么做

设计上的意义


五、LayerChain 是什么,为什么像 OkHttp 拦截器

先说结论

为什么这么设计

它和 OkHttp 拦截器相似在哪里

区别在哪里

面试里的推荐说法


六、Widget 图片是如何展示的,用的是 Bitmap 还是共享文件

先说结论

具体链路

  1. 图片从 Firebase Storage 下载到本地
  2. 本地路径写入数据库和 WidgetSp
  3. Widget 更新时从 WidgetViewData 里取本地路径
  4. PhotoLayer 用 Glide asBitmap().load(filePath) 加载本地文件
  5. 做裁剪、圆角、shape 等处理
  6. 最后通过 RemoteViews.setImageViewBitmap(...) 写入 widget

为什么不是直接共享文件给桌面宿主

更准确的描述


七、既然是 Bitmap 传给 Widget,会不会有 IPC 限制

先说结论

风险点

项目里如何规避

工程上的理解


八、为什么要做 partial update,如何避免 Widget ANR

一句话

风险来源

项目里的处理方式

  1. 区分全量更新和局部更新
  2. 主图、头像、皮肤、点赞、视频态尽量走 partiallyUpdateAppWidget(...)

  3. 统一调度更新入口

  4. 所有更新都收口到 WidgetUpdateService
  5. 中间层可以做去重、拦截和状态控制

  6. 重逻辑放后台

  7. 图片解码、数据组装、下载等工作不放在 Provider 主线程里直接做

  8. 控制图片尺寸

  9. 只传 widget 需要尺寸的 bitmap

  10. 使用本地缓存

  11. WidgetSp 会缓存图片路径、视频路径、Top3 数据和状态信息

面试收口


九、Widget 点击事件是如何处理的

一句话

点击处理链路

  1. 生成 RemoteViews 时,为不同区域绑定不同 PendingIntent
  2. 点击后先回到 WidgetClickBroadcastReceiver
  3. 广播再把事件转给 WidgetUpdateService
  4. WidgetUpdateService 根据 action 分发:
  5. 菜单态更新
  6. 视频播放
  7. 局部刷新
  8. 恢复正常态
  9. 页面跳转

为什么还要加状态机

核心状态

价值


十、Widget 动画是如何实现的

先说结论

1. 挂件类动画:主要靠 XML

2. 点赞动画:用 ValueAnimator 驱动帧资源

3. 视频播放:解码帧后逐帧刷新

一个重要 trade-off

项目里的降级处理


十一、Widget 视频是如何处理的

一句话

为什么不能直接用普通播放器

处理流程

  1. 视频封面和视频文件分别缓存到本地
  2. Widget 先展示可播放态
  3. 用户点击播放按钮后,事件回到 WidgetUpdateService
  4. WidgetUpdateService 创建 MediaCodeFrameUtil
  5. MediaExtractor + MediaCodec 解码视频帧
  6. 每拿到一帧 bitmap,就通过局部刷新更新 widget
  7. 播放结束后恢复成静态封面图

性能控制

面试里可强调的点


十二、Firebase Storage 的图片和视频是如何处理的

先说结论

图片下载链路

视频下载链路

为什么先落地本地


十三、Glide 是怎么用的,有没有自定义 Target

先说结论

普通展示链路

Widget 场景里的自定义 Target

为什么这里不能直接 into(ImageView)

还有哪些 Target


十四、视频发送前的压缩是怎么做的

一句话

处理流程

  1. 发送页拿到原始视频路径 videoPath
  2. 预先生成压缩输出路径 compressedPath
  3. FirebaseStorageMgr.uploadVideoFromPath(...)
  4. 进入 MyUploadVideoService
  5. 先用 FFmpeg JNI 压缩
  6. 再把 compressedPath 对应文件上传到 Firebase Storage

用的是什么方案

为什么这样做

上传完成后还有什么


十五、相机方案是 CameraX 吗

先说结论

CameraX 承担什么

为什么又有 GL

视频录制怎么做

这样设计的价值


十六、这个项目里最能体现工程能力的亮点是什么

我会优先讲 Widget 体系

亮点拆分

  1. 把 Widget 更新收口到统一调度中心
  2. 把不同类型 widget 抽象成 WidgetUpdater
  3. LayerChain 解决复杂 RemoteViews 拼装问题
  4. 用局部刷新和状态机控制性能和交互稳定性
  5. 把视频能力降级成 widget 可承受的逐帧刷新方案

为什么这件事值得讲


十七、如果面试官继续追问,可以展开哪些 trade-off

为什么图片社交项目要把 Widget 做得这么重

为什么 Widget 不直接共享文件或在线加载

为什么动画不统一用一种方案

为什么视频不直接用播放器

为什么相机不纯用 CameraX 默认链路


十八、面试回答时的推荐收口

推荐收口 1

推荐收口 2

推荐收口 3