公司动态
安卓+PC信息发布系统开发实践:架构、协议与避坑指南
简介一套面向餐饮、零售等行业大屏终端的多媒体信息发布系统包含安卓播放端与PC端配套软件可播放视频、图片、字幕等节目并支持场景化定时编排适合单机或局域网内直接部署使用。资源共134个文件以Java与XML源码为主同时包含PNG图标、TTF字体、Gradle构建脚本及少量C/JAR依赖其中Java/XML构成播放与配置界面逻辑PNG/TTF是界面资源与字体Gradle负责工程构建整体约111.62MB结构完整便于二次开发。播放端支持多场景轮流播放、后台下载播放内容、音量设置与截图、文件数据校验等功能并兼容avi、mkv、mp4、jpg、png、bmp等常见多媒体格式PC端负责场景与节目单的配置下发形成从制作到播放的闭环。目前已有5543人学习下载非常适合需要快速搭建自有信息发布系统、深入掌握安卓大屏播放器开发或研究场景与节目单管理逻辑的工程师参考。 做信息发布系统这活儿最早是从一块广告屏开始的。甲方要求其实很简单店里那块安卓屏每天按时间段播放不同公告总部在电脑上改内容别让我天天拿U盘去插。后来发现这需求到处都是——银行引导屏、工厂车间看板、学校电子班牌、社区公告屏本质都是同一个东西一台安卓显示终端加一个 PC 管理端。我今天说的这套东西最终整理成了“信息发布系统安卓PC.zip”安卓端负责在屏幕上跑展示逻辑PC 端负责内容下发和设备管理两端走 HTTP 接口通信项目不算大但该有的模块基本都有。这篇内容适合三类人看第一类是刚接到信发项目、想找参考实现的开发者第二类是想把某个现成 zip 解压后改成自己业务做二次开发的人第三类是已经跑起来但遇到各种设备兼容性、断网、升级毛病的维护者。我下面按整个项目的实际组织方式来讲不按教科书目录讲方便你解压之后直接对着目录看。1. 拆开 zip 之前先说清这个系统要解决什么1.1 盒子里那台安卓显示终端安卓端在我的项目里不是普通手机 App而是“信发终端”。普通手机 App 的核心是用户主动操作信发终端的核心是无人值守、持续展示。开机之后最好自己跳进播放页面不要用户手点屏幕保持常亮杜绝锁屏到了一定时间自动切到夜间模板网络断了要能自动重连断电恢复之后还要回到原来的状态。所以我在安卓端做了几个偏底层的模块只出现一次的引导页、主播放页面的电源管理 WakeLock、系统广播 BOOT_COMPLETED 的自启、一个轻量级的播放调度器。播放调度器不直接依赖 Activity 的生命周期而是自己维护一个队列按 PC 端下发的播放单循环执行。这个设计在后续扩展多屏模板时非常省事加一个模板就是往队列里注册一种渲染器。一个典型工程的目录结构大概长这样InfoPushSystem ├── AndroidClient │ ├── app │ │ ├── src/main/java/.../player │ │ ├── src/main/java/.../network │ │ ├── src/main/java/.../service │ │ └── src/main/assets/config.properties │ └── build.gradle ├── PcManager │ ├── InfoPushManager.sln │ ├── FfmpegWorker │ └── Database └── docs我第一次拿到这类工程时习惯先看config.properties和数据库脚本因为通信地址和表结构最能反映系统全貌。界面代码反而是最后才看的。1.2 PC 管理端的角色PC 端在我这套项目里担任两个角色一是内容管理后台操作员登录后上传图片、视频、PDF把它们拖到一个播放时间轴里二是设备管理中心能看到每台终端的在线状态、当前播放节目、磁盘占用和日志。我用的技术栈是 C# WinForm因为这类系统通常要求部署在 Windows 服务器或者办公电脑上WinForm 在局域网环境下开发效率高部署也省事。如果你不熟 C#换成 Java Swing、Electron 甚至 PHPWeb 管理后台都可以协议层保持一致就行。1.3 部署形态单机直连与公网中转这个系统的网络部署有两种典型形态单机直连终端和 PC 在同一个局域网PC 管理端直接访问终端 IP适合单个门店几十块屏的规模。公网中转终端分布在不同网络PC 管理端部署在服务器上终端定时轮询服务器接口拉取播放单适合连锁门店或跨地域项目。两种形态在我这个 zip 里都跑得通区别只在客户端连接的 baseUrl 配置。我第一次搞的时候忽略了这一点把接口地址写死成了局域网 IP到了另一个项目上全是 0后来才抽象成一个属性配置文件。2. 安卓端最容易被低估的三个工程点2.1 开机自启、断电重启与看门狗逻辑信发终端很少做优雅关机绝大多数时候是直接断电。所以你光有 BOOT_COMPLETED 自启还不够还要处理“开机后还没收到广播就被用户 UI 抢先”和“系统有自杀式回收策略”这两类问题。我实测下来的组合拳是这样MainActivity 注册 BOOT_COMPLETED同时在自己的 Application 里维护一个前台服务服务里申请 WakeLock 并标记为 START_STICKY。START_STICKY 的意思是系统把服务杀了之后只要资源允许会重新回调 onStartCommand这样可以在 onStartCommand 里再次拉起播放页。不少国产 ROM 还有自己的后台限制。我的做法是在首次引导时引导用户把 App 加入白名单并把“自启动”、“关联启动”、“后台弹窗”三个开关全部打开。别嫌这一步麻烦信发终端如果被系统杀掉你远程想改都改不了。2.2 显示适配、旋转锁定和后台进程保活安卓屏的尺寸五花八门有 1024x768 的老式横屏有 1920x1080 的广告机也有竖屏的电子班牌。我在布局里几乎不用固定 dp 值而是用百分比坐标和比例布局。原因很简单你没法跑到现场给每台机器单独调布局只能用一条规则适配所有屏幕。旋转锁定上信发场景一般是固定方向。我的建议是在 Manifest 里锁死 orientation别靠 Activity 里的 setRequestedOrientation 动态切后者在部分 ROM 上会出现短暂黑屏。既然信息发布系统的使用场景固定锁死就是最稳的方案。至于保活最实在的不是市面上那些“像素页保活”花招而是处理好崩溃和重启。我在主播放页捕获了全局未处理异常记录崩溃堆栈并上报到 PC 端然后在 2 秒后自动重启页面。这样就算用户不碰终端它也能自我恢复。2.3 播放内核选型图片、视频与网页混排信发播放列表里往往同时有图片、视频、网页和滚动字幕。我试过几种方案纯 View 渲染适合图片和字幕但视频要接播放器。WebView 渲染适合 HTML 模板和网页但视频播放坑多。混合图片、字幕用原生 View视频用 ExoPlayer网页用 WebView用一个 FrameLayout 做容器切换。最后我选了混合方案。ExoPlayer 比 MediaPlayer 在信发场景里好用太多错误恢复、HLS 直播流、缓存策略都能调。我的播放队列里每个节目就是一个 JSON 节点渲染器根据 type 字段决定用哪个内核切换节目时不用销毁整个 Activity只替换容器里的内容。3. PC 管理端的核心业务素材、排期与终端状态3.1 素材仓库和上传转码的取舍PC 端第一个问题是素材管理。用户上传一个 2GB 的 4K 视频终端在弱网下要拉多久我的做法是上传后在后端做一次转码统一转成 H.264 编码、1080p、码率 4Mbps 以内的 MP4。这个方案极大降低了播放器的兼容压力也避免了视频在终端上拉不起来。不过转码服务在 WinForm 里直接集成比较重我用的是一个独立的转码进程监听素材目录变化调用 FFmpeg 命令行完成转换。Windows 上直接带一个 ffmpeg.exe 即可不引入额外框架。转码完成后系统会写一条素材状态记录PC 端列表里能看到“转码中 / 转码完成 / 转码失败”三种状态。素材管理这块有个容易被忽略的点图片素材最好也做一次统一压缩。很多运营人员会直接拖一张手机拍的原图上来一张能到 8MB多放几张终端加载就慢。我在上传接口里对图片做了等比压缩单张图片最大边控制在 1920px质量 80%肉眼基本看不出差别加载速度却能快出一大截。3.2 播放计划的优先级和生效规则信发项目里排期规则经常是业务核心。我的 PC 端播放计划设计成三张表模板表、节目表、排期表。模板表定义页面布局节目表塞具体素材排期表决定“哪个节目在哪个时间段由哪些终端播放”。优先级规则一定要定清楚。我一开始没设优先级紧急通知发出去了却被默认循环盖住现场又看不到被骂了一顿。后来给每条排期增加 priority 字段终端取播放单时按 priority 排序再按时间段过滤总算把问题解决干净。优先级场景说明10临时插播最高级立刻替换当前播放内容20定时任务按时间段生效如早间公告30默认循环没有任何定时任务时兜底播放这个优先级模型很简单但足够覆盖 90% 的信息发布需求。真正要花心思的是定时任务跨天的场景比如“晚上 22:00 到次日 07:00”这种排期过滤逻辑里要单独判断结束时间小于开始时间的情况否则节目会在零点以后消失。3.3 用列表页盯住所有终端的在线状态终端状态页面看起来简单其实最容易做烂。我建议 PC 端至少展示设备编码、IP、最后心跳时间、在线状态、当前播放节目、存储剩余、App 版本。终端数量一多靠人眼盯列表不现实还要有简单的筛选和导出功能。这里有一个细节在线状态不能只靠心跳时间戳算因为终端可能一直在线但网络不通已经很久了。我在 PC 端单独起一个定时任务每 30 秒扫描一次心跳表把超过 60 秒没心跳的设备置为离线同时在列表里标红给运维一个明确的处理入口。4. 两端协议设计能一次调通的接口约定4.1 REST 接口和 JSON 字段命名两端通信我全部用 REST JSON字段命名统一为驼峰。接口只有五个注册、获取播放单、上报心跳、上传日志、检查升级。不要一上来就搞一堆复杂接口信发系统的核心诉求就是“把播放内容发给终端”。以获取播放单为例终端请求后返回{ code: 0, data: { deviceId: A10001, timestamp: 1736300000, playlist: [ { id: 1001, name: 早间公告, type: video, url: http://192.168.1.10/media/a.mp4, duration: 0, startTime: 08:00, endTime: 09:30 } ] } }终端拿到之后自己算时间窗口到点播对应素材。type 字段在前面说过决定了渲染器类型duration 对视频是 0对图片是停留秒数。设备注册接口也值得单独说明。终端第一次启动时会读取本机 MAC 地址和设备序列号生成唯一编码调用/register上报PC 端把这个编码和终端名称绑定。这一步如果漏掉后面所有状态页都是乱码因为 PC 端不知道你上报的设备是谁。4.2 心跳、离线补拉与时间校准心跳接口是终端每 30 秒调一次 PC 端的上报“我还活着、我在播什么、磁盘还剩多少”。PC 端通过心跳判断在线状态同时响应终端是否需要切换播放单版本。离线补拉这事我一直强调。终端可能在网络抖动时错过播放单更新如果 PC 端一更新就主动推送弱网环境很容易丢。我的策略是每次心跳响应里带上当前播放单的 updateTime终端发现和自己本地缓存的不一致就重新拉取。这样即使中断半小时恢复后也能自动对齐。另外时间校准很重要。终端如果本地时间慢了定时播放就不准。我允许 PC 端在心跳响应里下发服务器时间终端每秒累计误差超过 30 秒就自动校正一次。校正用 SystemClock.elapsedRealtime 做基准避免用户手动改系统时间造成的时间跳变。4.3 回到 PC 端的日志回传日志回传是排查问题最省事的武器。终端本地只保留最近 5 个日志文件每个 1MB轮转覆盖。PC 端有一个“拉日志”按钮向指定终端下发一条指令终端收到后把日志文件压缩上传PC 端解压后按时间线展示。这一点在真机调试点位时非常重要。终端分布在各个楼层你不可能每次拿着数据线跑现场。日志只要包含崩溃栈、播放器实例状态、HTTP 请求状态码绝大多数问题都能定位。5. 编译、签名、上真机时踩过的坑5.1 老设备 WebView 和 TLS 握手问题第一坑是老设备上的 HTTPS。很多终端是低端盒子系统 WebView 和 TLS 版本很老。PC 管理端如果上了 HTTPS证书链稍微新一点终端就会出现类似“SSL 握手失败”的错误但你在新手机上完全测不出来。我的处理方式是PC 管理端在局域网环境默认用 HTTP公网环境用 HTTPS但必须在服务器配置兼容 TLS 1.2 以上并检查证书链完整。终端侧的网络库用 OkHttp调低连接超时时间到 5 秒失败后走离线重试不回抛崩溃。5.2 权限弹窗和厂家 ROM 的“强制停止”策略第二个坑是权限弹窗焦点。第一次打开 App系统会弹存储权限、悬浮窗权限、电池优化白名单权限如果这些弹窗恰好盖在播放页上面终端就卡在授权界面。我的做法是把权限请求全部收拢到首次启动引导页用户或现场安装工人一次性点完之后正式进入播放页不再触发运行时权限请求。还有一个让我印象很深的坑部分厂家 ROM 在用户从“最近任务”里滑动删除 App 后会直接给 App 发强制停止信号连前台服务一起杀掉。解决办法是在引导页教用户关闭“从最近任务移除时杀死应用”的开关同时在服务里监听任务移除事件被移除后 2 秒内通过 AlarmManager 重新拉起进程。代码不复杂但缺失就是致命问题。5.3 拿到 zip 之后的二次开发建议如果你是从头解压这套 zip 开始做我建议按这个顺序改先跑通编译拿到一个能在模拟器上启动的 apk。改配置中心的 baseUrl指向你自己的 PC 端接口。对照五个接口把 PC 端数据库表建好。加一门业务比如把公告里多一个“头条新闻”类型。不要一上来就改界面。这个系统的难点不在界面复杂度而在于两端的数据同步和异常恢复是否可靠。界面是最后一步数据链路先通了界面随手换。我自己长期维护这套系统最大的感悟是信息发布项目不怕功能多就怕设备掉线后无法自愈。安卓端死机不可怕可怕的是没人知道PC 端下发遗漏也不可怕可怕的是没有版本对比机制。如果你也想在这个基础上改我建议优先把心跳、离线补拉和自动重启三件事做扎实其他锦上添花的功能后面再说。本文还有配套的精品资源点击获取