UI交互异常
- 空显示/白屏
- 一般是因为数据为空或获取失败。要请产品定义加载中、加载失败、数据为空的UI。
- 显示不完整、错位
- 开发时考虑不同屏幕大小、窗体大小、内容量的兼容,做好对齐和层级的设置。内容量会引起折行、显示不全等问题。
- 如果有改变字体大小或多语言设置的需求,应在自测时确保最大字号和最长文字时显示正常。
- 格式显示错误。日期、时间、时区、数值、单位、语言等,请产品明确并准确实现。
- App启动或进入某界面的时间过长
- 耗时操作异步化,让用户更早看到程序界面
- 如果必须等某些操作后才好展示,让产品增加loading状态的UI
- 连续多次操作,数据异常。一般发生在异步操作的场景
- 连续点击按钮。请产品定义loading状态UI,重复点击不响应,或响应前点其它地方都是取消。
- 一个长时间的动画过程中执行反向操作,例如游戏开车前进时突然油门不松开地切成倒挡。需要程序做好兼容,请产品确认能不能这样操作,有什么后果。
- 异步结果返回时,用户已取消操作。一个极端情况是,服务器已处理了请求但网络正好断了,客户端没收到确认信息。
- 通常操作是不管的,下次进入或刷新就能看到最新状态。产品需要知道。
- 多个提示(Toast或自消失弹窗)没有依次显示。请产品定义:
- 如果提示都要在同一个地方显示,则做好全局管理,用队列做消息缓存,等前一个消息提示正常消失后,从队列取下一个来显示。不能让前一个立刻消失或被遮盖。
- 可以做排列显示,即下一个提示在前一个提示的下方,前一个提示消失时,下面的提示自动向上移动
- 卡顿
- 在UI操作的地方打印日志,分析出耗时操作。要么处理消息过多,要么处理某消息时间过长
- 调高UI相关线程的优先级,调低后台线程的优先级
- 用性能监控工具实时记录,分析出CPU或内存剧烈波动的操作
- 去掉主线程执行的IO操作
- 逐步剥离逻辑,定位出产生卡顿的部分。
- 换皮肤、配色方案
- 漏订阅消息
- 漏处理
- 图片、音视频不显示或不清晰
- 源文件的内容本身有问题
- 解码库的参数设置不对
- 缓存过小或被清理
- 系统不支持源格式的编解码
- 系统性能不足,跳帧,降低分辨率、帧率等
- 动画不播放
- 逻辑状态不对,或执行着多个操作
- 相关的View状态不对
- 用户体验问题,需求评审的时候就要提出了。别都推责给产品,东西是我们程序员做出来的,可以反向提要求。给三篇参考文章
网络异常
- 连接超时、读取超时、发送超时、对方断连/死连
- 设好网络库参数(成熟的网络库都有),长连接主动断开重连,短连接主动取消
- 和产品确认重试间隔时长,最大重试次数等
- 请产品定好相关UI提示
- 背压(后向压力,backpresure),数据消费者的处理速度低于数据生产者的生产速度。会造成UI呈现旧数据、数据队列拥塞最终内存耗尽
- 数据消费者
- 增大缓存,先缓存后处理
- 多线程处理
- 丢弃或合并处理部分数据
- 数据生产者
- 增加缓存,合并数据流,由【高频发少数据】变成【低频发多数据】
- 降低数据生产频率
- 丢弃部分数据。生产后,判断上次发送时间,未达指定时间间隔则丢弃
- 增大发送超时时间
- 数据消费者
- 网速波动:其它应用或模块在占用带宽,导致本功能超时
- 限速
- 主动停止不重要不紧急的网络任务,或降低优先级
- 不能对网速有预期,例如觉得1KB数据肯定秒级传完。都要考虑异常
- 信号弱,延时高,丢包
- 使用tcpdump+wireshark确认问题
- 选择更优的链路
- 后端
- 无响应,按超时处理
- 如约地返回错误,请产品定好UI提示
- 未定义地返回错误,例如响应体的JSON反序列化失败。catch exception,不能崩溃,日志做好记录,请产品定好提示,一般是直接toast出exception.message,必然的话做重试。
系统或硬件错误
- 资源被占用,初始化失败。例如文件、摄像头、端口、设备等已被别的线程、进程、应用打开。
- 可能被别的应用占用的资源,要做好打开失败的容错,有一定的UI提示,不能崩溃。
- 应用内的多线程/进程处理,应确保只有一个IO线程/进程,同一个资源的读写都在这个IO线程/进程内处理,其余线程/进程通过发消息来执行操作和传递数据。
- 无权限或动态申请权限失败。请产品设计做好用户不授权情况下的交互,不能崩溃。
- 串口
- 数据可能丢失或错乱,应用层协议要做好检验,例如CRC、MD5
- 发送或接收的单条电线可能断开,会产生"可接收但发送失败"的情况,得仔细检查
- 摄像头打开时间过长
- 硬件或系统层优化
- 预加载
- 请产品定义,用动画优化体感
- 摄像头打开异常
- 硬件或系统层
- 请产品定义,重试规则和用户交互提醒;
- 硬件松动。对应的程序异常时,检查硬件状态。
- 应用层断连,可以是网卡松了,网线被拔了,使用ifconfig查看网卡状态
- 黑屏,检查显卡或监视器连接状态,也可能屏幕电源没接上
- 工程样件硬件就是不可靠的
- 可以起一个线程,定时检测硬件状态,输入到日志,方便定位问题。
- 工程样件的芯片、CPU、GPU固件都可能有bug,可以怀疑到这一层。
SDK错误
- 版本不同
- 使用SDK都要经过真机自测
- 使用更新或更旧的SDK,尝试是否会出现问题
- 是否按预期工作,在不同场景下是否行为一致
- 处理流程或传参等错误,是否按文档做了
- 兼容性,用了当前系统不支持的API
- 三方库本身有bug,多加日志来确认。
程序错误
- 多个条件同时成立,导致UI或逻辑错误。复杂场景需要列一个表尽可能穷举所有情况,缺漏的请产品补充定义
- 死锁、死循环,直接导致ANR
- 在子线程调用了需要在主线程调用的方法
- C++野指针。用好STD或boost的便捷类
- 没有监听/订阅相关消息,状态改变不知道
- 确定监听/订阅是否存在重复
- 确定程序窗口销毁后停止相应的监听/订阅
- 参数未做配置,需要请产品确定默认值
- 内存(RAM)用尽,产生OOM
- 对大量级的数据进行流式读写,保持较少的数据量在内存中,更多部分仍在磁盘
- 检查内存泄漏,主要是排查需要手动release的对象,认真看说明文档
- 内存缓存需要限量,超过限额存入磁盘
- 用测试工具对复杂场景测一遍,长时间运行
- 大型对象慎用单例,不应该使用全局或静态变量保存
- 复用频繁创建销毁的对象
- 资源和对象及时释放
- 循环体内及时释放临时对象,极限情况可主动触发GC
- 磁盘(ROM)用尽
- 设计时考虑可能占用磁盘空间的数量,做好限额
- 产品设计上提供手动清理缓存的入口和缓存占用量的显示
- 数据缓存(例如媒体文件或日志)应做好滚动周期的限制,例如每次启动删除一个月前的数据
- CPU长时间占用过高
- 使用工具找出占用CPU的函数
- 使用工具查看UI渲染频率
- 切换到后台被杀掉或停止工作,回到前台需要恢复现场
- 认证信息过期而未检验,过期后未清理状态
- 外部存储器的文件丢失
- 外部存储器的文件被错误修改。解析的时候catch异常,无法恢复就删除或赋默认值
- 时序
- 是否按照预期的顺序执行
- 是否按预期的时机初始化完毕
- 数值错误
- 多线程/进程/应用,异步时序不对
- 没有加锁
- 共享空间写入乱序,包括内存和磁盘
- 异常退出,文件损坏。
- 线程过多。其中集中管理线程池。
- 背压,参考网络错误中的描述,同理。
- 反射操作错误
PS:不可能的异常
我们不是要处理所有的可能性,否则岂不是所有代码都要加try catch?
应该做的是在写代码前就确定可能有什么异常,有约定、协议、规范不会出错的,都不用做防御,在开发阶段最好就是通过崩溃来达成最高级别的警示。还可以加一些assert,在开发阶段澄清所有的异常。assert也可以防止别人错误地使用你的代码。