乐趣区

如何打造一款小巧好用安全稳定的消息推送SDK

一个好的消息推送 SDK 应该具备稳定、易用、安全、小巧灵活等特点。个推作为国内第三方推送市场的早期进入者,一直致力于为开发者提供高效稳定的推送 SDK。经过多年的打磨和创新,个推沉淀了丰厚的技术经验。这期文章将从“小”、“稳”、“好用”以及“安全”四个角度分享个推推送 SDK 的技术经验。

1 小

1.1 65535 限制

好的消息推送 SDK 首要需考虑到包体的小巧灵活性。为什么选择更小体积的包体?从代码层面来看,是由于系统的“65535 限制”。如上图(左)所示,我们的程序最终会生成 dex 文件,dex 文件主要由以下几部分组成:header(标头)、一连串的 ids(标识符列表)、data(数据区)以及 link_data(静态链接文件中使用的数据)。细看上图 (右),它包含了一个 method_ids_size 字段,该字段的主要作用是定义个数。根据谷歌的定义,uint 是一个 16 位的 short 类型,最长长度是 65535。如果将 dex 工程反编译,会生成很多 smali 的文件,再去看 smali 里的函数调用(比如 invoke direct {*} 函数名 @BBB),会发现调用的地址其实就是刚才 unit 里定义的偏移量计算得出的。因此,这个函数地址最多也只能有 65535 个。

1.2 如何使包体体积变小

如何减小包体的体积,可以从以下几个方面考虑:

(1) 自研,不嵌套 :在研发 SDK 时,不建议开发者在 SDK 内嵌套一系列框架,例如三方网络框架、db 框架或任务调度框架等。我们主张开发者选择最核心的一部分进行自主研发。

(2) 代码优化 :从算法层面,在效果相同的情况下,可适当减少代码的行数;对于有默认赋值的变量不需要进行初始化赋值;选择合适的字符串拼接方式,建议使用 StringBuilder 方法拼接字符串,可以解决字符串频繁修改带来的内存消耗,也有利于减少包体体积大小。

(3) 追求实用,放弃完美 :SDK 包体应当追求实用性,以完善主功能为主,其他相对次要的部分可以适当减少时间或精力投入,放弃完美主义思维。

(4) 代码混淆 :借助代码混淆实现更小体积的包体,且不易被逆向。

1.3 省电省流量

省电省流量是“小”的另一个方面。作为一款好的消息推送 SDK,应该对流量和电量有严格的限制,否则会出现手机发烫、高耗电提醒、流量浪费、内置 SDK APP 难以上架等问题。针对上述问题,我们可以通过 Lock 杀手,智能心跳、自定义协议、链路合并、按需活跃等方式尽可能地降低 SDK 对电量以及流量造成的消耗。

(1)Lock 杀手 :代码中 WiFiLock、WakeLock 等会强制唤醒 APP,导致 APP 产生较大耗电量。在不影响功能的前提下,我们应尽量减少或者不用该类锁。

(2) 智能心跳 :应根据不同的运营商、网络状态等, 选择不同的心跳策略,并且根据不同的应用场景探索心跳的最大边界,尽量延长心跳周期,减少电量和网络的消耗。

(3) 自定义协议 :市场上常用的 json、xml、甚至 PB 协议,都有比较好的兼容扩展性,但同样也带来了空间浪费的问题,自定义协议可以充分利用空间,精确利用每一个 byte 甚至 bit,极简化封装,承载最大的信息量,减少流量和电量浪费。

(4) 链路合并 :当一个设备有多个 APP 的推送链路同时活跃时,我们会运用合并链路技术,将使用“个推 SDK”的 APP 之间的长连接链路进行合并,减少流量电量的浪费。

2 稳

2.1 提升稳定性

在实际应用中,开发者可能会遇到 ANR、OOM、Crash、内存泄露、闪退等棘手的问题,为了打造好的消息推送 SDK,我们需要通过持续的迭代和优化来将错误最小化。

(1) 做好代码管理 :除了借助 SVN、GIT 等工具做好代码托管外,开发者还需遵循一定的代码规范,借助类似 gerrit 等工具进行代码 review,使用 verify 流程。在保证机器找不到问题的前提下,再用人眼去辨别是否符合业务逻辑。

(2) 自动化测试 :自动化测试可以大幅提升回归测试的效率,非常适合敏捷的开发过程。此外,自动化测试可以替代大量的手工机械重复性操作,测试工程师可以把更多的时间花在更全面的用例设计和新功能测试上。

(3) 运用代码模块化等小技巧 :代码模块化能以最少的模块、零部件,更快速地满足更多的个性化需求。异常处理可以提高系统的容错性,让程序更加稳定。代码检测能及时发现程序中的缺陷和错误,比如检测内存是否泄露,是否有安全漏洞等,保证代码质量。

(4) 线上灰度 :实际场景中,我们很难去覆盖所有的环境,例如机型、网络等,需要通过线上用户的反馈去验证代码的健壮性。因此在产品大规模推向用户之前,我们需要进行少量的真实用户测试,即灰度上线来帮助减少风险。

(5) 日志系统 :系统有问题是必然的,在尽量保持系统稳定的前提下,要考虑容错性。当问题发生时,需要第一时间以最快的速度排查,因此需要有一套完整的日志系统。此外,平时我们也可以通过日志系统的拨测检测系统的健壮性,可以在用户反馈之前及时发现并解决问题。

2.2 兼容性

兼容性也是保证好的消息推送 SDK 稳定性的一个重要条件,主要考虑以下几个方面:

(1) 接口兼容 :每次版本更新后,对外接口要尽可能保持不变。对于改动较大的接口,可以使用 @Deprecated 注解对老接口进行标记,并且做新接口调用的兼容,而不是直接删除老接口。

(2) 主键兼容 :当主键发生变更(例如去掉 service、provider)时,部分老的安卓系统会有组件缓存,运行时直接告知“类”找不到。建议在 AndroidManifest 中保留声明,且对应“类”进行代码空实现,以减少发生 crash 的概率。

(3) 安卓系统兼容 :可使用 Build.VERSION.SDK_INT 做 API 区分。

(4) 真机兼容 :可以借助云测等平台进行兼容性测试。

3 好用

SDK 的易用性可以从下面几个方面考虑:

(1) 接入简单 :接入 SDK 时有集成 demo 直接可以运行,且接入文档清晰、步骤简单,最好能实现一键集成。

(2) 保持核心优势 :一款好的消息推送 SDK,我们主要考虑及时性、到达率、稳定性和准确性。例如,新闻媒体类 APP 对推送的及时性要求较高;通知类推送(如转账信息)会特别注重消息的到达率;稳定性指的是要保证推送 SDK 在不同环境下的正常运行,尤其是 11.11 等高并发场景;准确性主要针对广告营销类推送,需要在合适的时间、合适的地点和合适的场景把合适的内容推送给合适的人。其中,关于如何保证稳定性,可以从多线路、多 IDC、热备份等角度考虑。① 多线路调整:例如预埋三线域名,做一些轮询策略,防止域名被劫持。② 多 IDC 设置:除了域名被劫持外,还可能遇到网络攻击,物理性损坏的情况。设置多 IDC 一方面是为了实现分流,另一方面也降低了风险。③ 热备份:系统处于正常运转状态下的备份,一旦系统出现问题,可以快速恢复。

(3) 多样化需求 :通过丰富的画像标签,对用户进行场景化的智能推送,满足用户的多样化需求。

(4) 策略可控 :我们还提供静默时间、推送控量、短信补量、定时展示等附加功能,满足客户的实际使用场景。

$4 安全

SDK 开发过程中,我们还需要注意安全性。安全性不仅仅代表网络数据交互的安全、本地数据存储的安全,也涉及到 SDK 的加固、混淆、第三方安全软件审核等。

其中,我们重点讲解 SDK 的加固。目前安卓平台 SDK 绝大部分都是 Java 语言编写,容易被反编译。SDK 如果只是进行了简单的混淆,很容易被窥探到内部实现细节,此外还可能存在 SDK 被二次打包、植入恶意广告等现象。因此,我们需要对 SDK 进行加固,以提升安全性。

如上图所示,SDK 的加固主要是 Java 层面和 so 层面的操作。Java 层面可以进行 SSR IR 指令转换或者做 Java2C 处理,把实现细节放入 native 中;so 层面可以做一些扁平化、虚假控制等来混淆代码,也可以通过指令替换、指令跳转逻辑来增加逆向难度。此外,也可以通过常量字符串加密加固 SDK,这是目前较为简单实用的一种方式。

总结来说,其实开发一款好的消息推送 SDK 并不难,难的是如何让自己开发的 SDK 在复杂的环境下稳定运行,这需要我们对 SDK 的架构有比较清晰的认知,并不断迭代和优化。希望本文能为各位开发者在实际操作中提供一定帮助,个推也会不断打磨技术、持续创新,与各位开发者一同分享关于推送以及其他领域的最新思路及方法。

退出移动版