Splash 闪屏页流程与功能分析

0. 闪屏流程分析

要说到闪屏,几乎已经是所有 App 的标配了。但是各家对于闪屏的理解和应用方式各不相同。比如 Google 自己并不建议在闪屏页中设置过长的停留时间,而只是作为冷启动过程的一种变相缓冲,以就是仅在背景上设置一个 APP Logo 来避免屏幕留白。但在中国,几乎所有的流量还不错的 App 都会在闪屏页中加入广告,或者自己应用中某个主题活动的宣传,在增强品牌效应。这其中又有不同的使用流派,但基本都可以归为一个流程:

部分 App 会直接省略 Logo 这一环,直接加载闪屏页。观察了几个常用 App 后,我发现网易云音乐的闪屏逻辑是最好的,这不是说网易云音乐的闪屏流程与上面的图片有什么不同,而是网易优化了冷启动过程。一般来说,App 在点击 back 退出以后,再次打开会重新进入冷启动流程,也就是会再次进入闪屏显示流程,但是网易云音乐就不会:它只会在你第一次打开该 App 的时候加载 Loge 和 Splash,然后即使你按了 back 退出到屏幕,再次打开就不会再走闪屏流程了。这样即使用户多次退出打开也只会显示一次 Splash 页面,那么就算网易云的 Splash 时间稍微长一点你也不会在乎的吧(斜眼笑)。

1. 闪屏流程优化

那么这种模式是如何实现的呢?开始我以为是因为是通过检测后台服务是否运行栏判断是否跳过 splash 逻辑,但是我实际实验后发现,这个方法虽然可行,但是在此打开时的页面动画就不是那么舒服了,有点生硬的感觉,而且不是所有的 App 都有后台服务的。不管简单的搜索就找到了我觉得正确的方式:

Activity 类中有一个方法:moveTaskToBack,接受一个 boolean 类型参数,传入 true 就会让 App 退出进入后台,但是不会停止该进程。这不就是我们想要的方式么?试了下果然跟网易云一毛一样,这样也是变相的避免了 App 冷启动,减少了用户的等待时间,提升了那么一点用户体验吧。

除了这个方法还有另一种办法:

1
2
3
4
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);

效果与 moveTaskToBack 是相同的。

但是这两个方法都有一个共同的弊端,如果 App 处于后台超过一定时间没有任务执行也会被杀死的。而且这个时间非常短。我在自己手机上测试是 7 秒左右。不过这大概是因为我手机打开了某些清理工具的原因,毕竟写在 onDestroy 里的 log 都没打出来,可见是被强制杀死的。所以我又在虚拟机上测试了下,点击返回以后等了三分钟发现还是处于存活状态我就没有继续测试了,基本已经证明如果没有第三方干涉,应用应该会在后台存活较长时间,直到内存不足被系统杀死。当然作为一个有尊严的 App 应该在用户退出后开个定时器,十分钟用户还没有回来就自毁吧,毕竟占着内存不工作也不太好是吧。

不过相比于很多 App 用 再按一次返回退出 来挽留用户,我觉得这种方式可能更加舒服一点。

2. 闪屏页需求分析

首先要说的是,我这里所说的闪屏页并不包括引导页,这相对是另一个功能了。一般对闪屏而言,无外乎就这么几个功能:

  • 显示 Logo,增强品牌效应
  • 打广告,挣点零花钱
  • 内部活动推广,会带有内链

首先说显示 Logo,如果 App 的 Logo 与开始的图片上的流程是一样的,那么 Logo 是可以有两种方式来显示的。

  1. 通过 windowBackground 来设置:可以在屏幕背景里放一个非全屏显示的图片,作为打开的留白的间隙的替代,当正常内容加载完就会被覆盖掉。Logo 的显示时间取决于正常内容的加载时间,如果加载很快,Logo 就会一闪而过。但为什么是非全屏显示呢,对于 windowBackgroung,很难设置图片的拉伸模式,那么屏幕适配就非常麻烦。所以放一个非全屏图片,设置个背景色或许是个不错的选择。这种方式也是 Google Photos 在用的。至于一闪而过对于用户的体验是否良好就仁者见仁了。另外值得一提的是网易云应该也是使用这种方式,因为 Logo 的显示时间并不总是相同而且没有丝毫的留白间隙,但是它的 Logo 是全屏的,这就得佩服下网易的技术水平了(微笑脸)。

  2. 除了这种方式就是常规的用一个单独的页面来显示 Logo 了,这种没什么好说的,全屏往里面放图片就行了。

2.2 显示广告

然后说打广告,这里的广告是指接的外部广告,比如网易的闪屏经常会放某某车的广告,或者天猫什么节的,还是有很酷炫的动画和声音的那种,简单的图片动态加载比较简单,但是带有多媒体效果的广告就略复杂了,这里就只说动态下拉图片广告吧。

动态下拉图片广告需要后台 API 的配合。比如检测是不是要下载新的广告,广告的过期时间,紧急停用某个广告等。因为我不是后台,所以自己写了个简单的后台广告接口,勉强够用:

  • 请求
    • 地址:
    • 方法:GET
    • 格式:<K,V>
    • 参数:
      • Long : splashId //当前 splash id,为空则返回最新 splash 信息
  • 返回

    • 数据:闪屏信息
    • 格式:json
    • 例子:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      {
      "splash":
      {
      "id":1, //闪屏识别 id
      "imgUrl":"", //闪屏图片下载地址
      "eventUrl":"", //闪屏活动地址
      "eventTitle":"", //闪屏活动标题
      "deadDate":0, //有效期至(Long 毫秒值)
      "disable":true //是否停用,用于紧急下线闪屏
      }
      }
    • 服务器行为:
      • 根据 id 检查是否存在新的 splash 信息,存在则返回 splash 对象,不存在返回空,停用该 splash 则返回 id 相同、 disable=true 的 splash 对象

如果 App 某段时间内只维持一个 Splash,也就是显示同一个广告,那么对象信息可以存在 SP 里,如果要维持多个 Splash,那还是单独建个数据表来存储比较方便。我这个接口只适合维持一个 Splash,因此也不存在考虑显示频次的问题,如果要适配多 Splash 的需求,我想你们肯定能做到的(斜眼笑)。

对移动端来说,每次打开 App 都要请求该接口,以保证 Splash 的最新。当发送 id 与接收到的 id 不同,那么就要开启子线程与下载新的 Splash 图片了。如果相同,但 disable=true,下次冷启动就不会再显示这个 Splash 了。

2.3 显示活动推广

本来我以为只有 App 自己的活动推广才会带有内链,不过网易显然又刷新了我的认知,它竟然在网易云里面加载天猫某某节的页面!不愧是网易,果然有态度。其实看上一小节的 API 文档,就知道 Splash 里还带有两个 url 属性的,imgUrl 是广告图片的下载地址,eventUrl 是活动页面地址,至于 eventTitle 就是活动页面的标题了。

这个其实只要给 Splash 图片设置个点击事件就行了,不过有一点还是要说下:点击图片进入了活动页面,如果 Splash 页面已经 finish 掉了,那么 App 不是就直接退出了么,如果推广是给别家推广的,有种给别人做了嫁衣裳的感觉啊。因此,对于活动页面的退出,还得加一层判断逻辑。考虑到内链不仅会是 web 页面,也可能是内部的某个常规 Activity,因此需要在所有可能由 Splash 跳转而来的页面里进行判断:

首先在开启 Activity 的传入参数:

1
2
3
4
5
6
7
public static void actionStart(Context context, String url, String title, boolean isFromSplash) {
Intent intent = new Intent(context, CommonWebActivity.class);
intent.putExtra("url", url);
intent.putExtra("title", title);
intent.putExtra("isFromSplash", isFromSplash);
context.startActivity(intent);
}

然后在需要做退出判断的地方:

1
2
3
4
5
6
7
8
9
10
@Override
public void onBackPressed() {
if (webView.canGoBack()) {//优先让内置浏览器返回至上一页
webView.goBack();
} else if (isFromSplash) {//如果从闪屏跳转而来,则关闭返回至首页
MainActivity.actionStart(this);
} else {
super.onBackPressed();
}
}

如果你有抽取 BaseActivity,那么在 BaseActivty 中判断更好:

1
2
3
4
5
6
7
8
@Override
public void onBackPressed() {
if (isFromSplash) {//如果从闪屏跳转而来,则关闭返回至首页
MainActivity.actionStart(this);
} else {
super.onBackPressed();
}
}

至此,闪屏的分析就结束了,一般来说 Splash 页面就这么些功能了,这里我没有贴上完整的实现代码,因为测试 API 是用 SpringBoot 建的,而且大家的后台并不一定是自己可以控制的,因此主要还是提供个思路,具体的实现方式还是要因公司(需求)而异。