注册

DeepLink在转转的实践

1. DeepLink 简介


DeepLink:“深度链接”技术,这个名词初看比较抽象,不过在我们身边却有不少应用,比如以下场景:



  • 刷抖音看到转转的广告,点击视频下方的下载链接,如果没有安装转转则下载转转,并在打开转转后跳转到相应活动页面
  • 在微信看到朋友分享的转转商品,点击后如果没有安装转转则下载转转,并在打开转转跳转到相应商品的详情页
  • 看到转转发送的订单提醒短信,点击链接后如果没有安装转转则下载转转, 并在打开转转跳转到相应订单详情页

DeepLink 使用户能够在目标 APP 之外,比如广告(抖音)/社交媒体(微信)/短信中通过点击链接,直接跳转到目标 APP 特定的页面(对于已经安装了 APP 会直接进行跳转,未安装 APP 会引导下载,下载安装完成之后跳转)。DeepLink 技术可以实现场景的快速还原,缩短用户使用路径,更重要的是能够用于 APP 拉新推广场景,降低用户流失率。


随着短视频的风靡,通过短视频投放广告获客的方式也流行起来, 本文主要介绍在新媒体拉新推广场景中 DeepLink 的应用以及服务端的搭建。


2 .应用场景


我在刷抖音时刷到一个转转回收的广告视频,而家里刚好有闲置的手机,我就抱着试一试的态度点击视频下方的下载链接下载转转,看看能不能在这个平台上处理掉手中的闲置,当下载安装成功之后打开跳转到了回收页面,我可能会眉头一挑,嗯~这个体验还挺好,然后测了下闲置手机值多少钱,回收价又刚好满足我的心理预期,而且还能为碳中和贡献一份自己的力量,何乐而不为之。


新媒体获客场景


以上是比较常见的一种场景,通过在抖音、快手或者其他渠道来投放广告来吸引一些有需求的用户来到转转,并通过 DeepLink 技术在下载完成打开转转后直接跳转到用户感兴趣的页面。


对于上述场景安卓和 IOS 的实现是有所区别的,包括下载策略以及 APP 内部跳转到用户感兴趣页面的策略。


2.1 IOS 应用场景


由于 IOS 下载APP只能通过 AppStore,所以 DeepLink 服务针对 IOS 会重定向到一个 H5 中间页,在 H5 中间页将服务端返回的 DeepLink 跳转链接复制到剪切板中,并拉起 AppStore 引导用户下载转转 APP,安装打开后 IOS 从剪切板中获取跳转链接进行跳转,到达用户感兴趣的页面。


IOS下载


2.2 安卓应用场景


安卓可以直接通过 DeepLink 服务下载转转 APP,而 DeepLink 跳转链接以 APK Signature Scheme v2 方式打入 apk 包中,安装打开后解析跳转链接跳转到用户感兴趣的页面。


安卓下载


APK Signature Scheme v2 是 Android 7.0 引入的一项新的应用签名方案 ,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。


下图是新的签名方案和旧的签名方案的一个对比:


新旧签名方案对比


APK Signing Block 结构
































偏移字节数描述
@+08这个 Block 的长度(本字段的长度不计算在内)
@+8n一组 ID-value
@-248这个 Block 的长度(和第一个字段一样值)
@-1616魔数 “APK Sig Block 42”

APK Signing Block 中的 ID-value 是可扩展的,由于 APK Signing Block 的数据块不会参与签名校验,也就是说我们可以自定义一组 ID-value,用于存储额外的信息,我们通过自定义ID-value将跳转信息打入APK包中。


3. DeepLink 服务


在 IOS DeepLink 方案中服务端只是负责重定向到一个 H5 中间页,因此不再赘述,下面我们主要介绍下安卓的 DeepLink 方案。


概要设计


3.1 投放链接设计


投放链接是投放到各个渠道的下载链接,需要考虑以下几点:




  1. 各个渠道链接规则不一样,保证我们链接规则能够覆盖所有渠道


    通过我们的调研有些渠道只支持 Get 请求,有些渠道不允许带参数,有些渠道必须以.apk 进行结尾




  2. 投放方便,链接投放出去之后不需要再改动


    由于投放链接是给到一些自媒体创作者,在给出链接之后能够保证从始至终都能下到最新的APP




  3. 充分利用 CDN


    转转 APP、找靓机 APP 的包百兆左右,为了保证服务的稳定性同样为了节约带宽,尽量发挥 CDN 的作用把绝大多数请求让 CDN 服务器来进行处理返回




3.1.1 兼容版本1.0


考虑到兼容各个渠道,某些渠道必须以 apk 结尾、某些渠道不支持Get请求带参数,采用什么方式?


既然不能带参数,那我们的参数信息可以直接拼到path中,参数以某种规则组装,服务端解析,需要的信息包括 APP 类型、渠道信息、DeepLink 链接信息、版本号等,简要设计出的投放链接 1.0 大致如下:


apk.zhuanstatic.com/deeplink/**…



  • appType: APP 类型,目前支持转转和找靓机,可扩展,如 zhuanzhuan
  • channel:渠道类型,根据每个投放渠道单独设置渠道 id,如 douyin666
  • version:APP 版本号,如 9.0.0
  • deepLink:deepLink 信息,目前传输 deepLinkId,deepLinkId 和端内跳转链接的映射关系由后台维护,服务端通过映射关系拿到跳转链接打入 apk 包中,如 huishou

3.1.2 升级版本2.0


1.0的版本号是直接写到 path 中的,这会造成很多隐患



  1. 可以通过修改版本号恶意下载 APP 的任意版本
  2. 保证用户一直下到最新的包需要版本更新之后更新所有投放链接

这显然是不合理的,针对以上两点我们必然需要删掉 version,替代方案可以让服务端在处理下载请求的时候通过其他方式拿到版本信息,修正后的投放链接 2.0 如下:


apk.zhuanstatic.com/deeplink/**…


3.1.3 最终版本3.0


2.0中没有了版本信息进而导致相同的渠道投放链接是一致的,只要 CDN 中有老版本APP的缓存,下载的是缓存的老版本APP,无法获取最新APP


因此我们考虑中间做一次重定向,通过一个不接入 CDN 的固定链接去重定向到一个接入 CDN 的带版本号的链接,这样问题就迎刃而解了,因此投放链接 3.0 应运而生:


apk.zhuanzhuan.com/deeplink/**…

apk.zhuanstatic.com/deeplink/**…


apk.zhuanzhuan.com 不走 CDN,只是将链接中的版本号补全并重定向到走 CDN 的 apk.zhuanstatic.com ,这样在投放链接不变的情况下能保证用户下载到最新的包。


3.2 打包&下载


投放链接设计好之后,通过投放链接可以解析到一些参数信息,比如:
apk.zhuanstatic.com/deeplink/zh…


我们知道用户下载的是douyin666渠道转转9.0.0版本的包,并且APP打开后需要跳转回收的页面。


下载渠道包服务端逻辑主要分为两大块,第一部分是拿到相应版本的原始包,然后通过 APK Signature Scheme v2 方式将渠道号和 DeepLink 跳转链接打入原始包中获得渠道包,将渠道包提供给用户进行下载。


为了能应对 APP 升级和渠道投放带来的流量,尤其是 CDN 中还没有缓存的时候,避免大量请求将我们服务打垮,所以需要引入本地缓存,如何引入?


首先我们分析下服务端的主要逻辑找出不可变的数据,第一原始包肯定是不变的,第二在原始包相同的情况下如果 channel 和 deepLink 跳转链接是一致的,那我们打包出来的渠道包也相应是不可变的,因此我们可以针对这两部分来进行缓存。


接下来我们分析缓存选型以及缓存策略,本地缓存的组件有好多可选的,比如 Caffeine Cache、Guava Cache 等,网上关于他们的测评如下:


读场景性能对比


可以看到在读场景下,caffeine cache 是当之无愧的王者,而且我们的场景基本是接近 100%的读,所以我们优先选择了 Caffeine Cache。


以下是两个本地缓存策略介绍:


3.2.1 一级缓存(渠道包)


     /**
* 缓存高频渠道包文件
*/
private static final Cache<String, byte[]> channelFinalAppCache = CacheBuilder
.newBuilder()
.expireAfterAccess(1, TimeUnit.DAYS)
.maximumSize(15)
.build();

渠道包的缓存 key 是 appType+version+channel+deepLink,由于 channel 和 deepLink 组合的众多,通过分析之前的下载数据缓存最高频的 15 个渠道包就基本满足 90%以上的请求而且不至于占用太多的内存,而为了获取最高频的 15 个渠道,我们通过大数据平台以 T+1 的方式将渠道数据更新到数据库中,DeepLink服务通过定时任务读取数据库中的渠道数据刷新缓存。


3.2.2 二级缓存(原始包)


    /**
* 缓存原始包文件
*/
private static final Cache<String, byte[]> channelAppCache = CacheBuilder
.newBuilder()
.expireAfterAccess(2, TimeUnit.DAYS)
.maximumSize(10)
.build();

原始包的缓存 key 是 appType+version,由于我们只下载最新版本的包, APP 类型暂时只有转转和找靓机,所有我们设置最大数量 10 是足够的,在我们应用启动的时候会对这个缓存进行初始化,以避免第一次用户下载速度过慢,并在之后监听APP的发版信息,新版本更新后刷新缓存。


4. 总结


DeepLink 服务支撑了新媒体投放以及 APP 内置更新的下载能力,为了保证服务稳定性和性能,除上述缓存策略外,还有其他策略来协同,比如 APP 发新版本时会进行 CDN 预热,将下载量高的渠道包缓存到 CDN 中,以使大部分流量能够在 CDN 服务器被消化,即使有突发流量打过来也会有限流规则过滤流量以保证服务的稳定性。


作者:转转技术团队
链接:https://juejin.cn/post/7127531093544140831
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册