唤醒APP的那些事

3,127 阅读12分钟
原文链接: www.jianshu.com
登录 注册写文章 首页下载APP

唤醒APP的那些事

zyl04401 关注赞赏支持

唤醒APP的那些事

1 字数 2766阅读 33756

移动互联时代,很多互联网服务都会同时具备网站以及移动客户端,很多人认为APP的能帮助建立更稳固的用户关系,于是经常会接到各种从浏览器、webview、短信、甚至是在其他APP中唤醒APP的运营需求。

运营推广场景

  • 微信、QQ等 -> 唤醒APP
    用户通过某APP分享了一条链接至微信或QQ,用户B点开该链接后,会引导用户B打开该APP或者下载该APP。
  • 浏览器 -> 唤醒APP
    用户A通过浏览器打开了某APP的M站或者官网,如果检测到A来自手机端,则会引导用户打开该APP或者下载该APP。
  • 短信、邮件、二维码等 -> 唤醒APP
    用户A打开了某APP的推广短信,邮件或者扫描二维码等,会引导用户打开该APP或者下载该APP。
  • 其他APP -> 唤醒APP
    用户A通过第三方APP分享了(任何可以分享信息的品台或工具:IM或者短信等)一条链接至用户B,用户B点开该链接后,链接会引导用户B打开指定APP或者下载指定APP。

APP服务化理念

所谓APP的服务化就是利用唤醒功能将APP的特定页面做为一个单独的服务或者内容,通过一定的渠道和载体传播出去,并且能够像传统的网页链接那样被一键唤醒。

更多关于APP服务化理念,推荐大家看看这篇文章

那么移动平台提供了哪些唤醒APP的方法呢?

如何唤醒APP

目前常见的唤醒APP方式有几种:

  • URL Scheme

URL Scheme是iOS,Android平台都支持,只需要原生APP开发时注册scheme, 那么用户点击到此类链接时,会自动唤醒APP,借助于URL Router机制,则还可以跳转至指定页面。比如:

<!-- 唤醒APP并跳转至指定的path页面 -->
<a href="<scheme>://<path>?<params>=<value>">打开APP</a>

<!-- JS设置iframe src跳转至指定的path页面 -->
//创建一个隐藏的iframe
var ifr = document.createElement('iframe');
ifr.src = '<scheme>://<path>?<params>=<value>';
ifr.style.display = 'none';
document.body.appendChild(ifr);
//记录唤醒时间
var openTime = +new Date();
window.setTimeout(function(){
    document.body.removeChild(ifr);
    //如果setTimeout 回调超过2500ms,则弹出下载
    if( (+new Date()) - openTime > 2500 ){
        window.location = '指定的下载页面';
    }
},2000)

这种方式是当期使用最广泛,也是最简单的,但是需要手机,APP支持URL Scheme
优点: 开发成本低,绝大多数都支持,web-native协议制定也简单。
缺点: 错误处理情况因平台不同,难以统一处理,部分APP会直接跳错误页(比如Android Chrome/41,iOS中老版的Lofter);也有的停留在原页面,但弹出提示“无法打开网页”(比如iOS7);iOS8以及最新的Android Chrome/43 目前都是直接停留在当前页,不会跳出错误提示。
支持情况: iOS在实际使用中,腾讯系的微信,QQ明确禁止使用,iOS9以后Safari不再支持通过js,iframe等来触发scheme跳转,并且还加入了确认机制,使得通过js超时机制来自动唤醒APP的方式基本不可用;Android平台则各个app厂商差异很大,比如Chrome从25及以后就同Safari情况一样。

  • Android intent

这是Android平台独有的,使用方式如下:

intent:
HOST/URI-path // Optional host 
#Intent; 
  package=[string]; 
  action=[string]; 
  category=[string]; 
  component=[string]; 
  scheme=[string]; 
end; 

这里的HOST/URI-path, 与普通http URL 的host/path书写方式相同, package是Android APP的包名,其它参数如action、category、component不是很理解, 有兴趣可以去了解官方文档。代码如下:

<!-- 唤醒APP并跳转至指定的path页面 -->
<a href="intent://<role>/<path>#Intent;scheme=<scheme>;package=com. domain;end"">打开APP</a>

如果手机能匹配到相应的APP,则会直接打开;如没有安装,则会跳到手机默认的应用商店,比如Google原生系统Nexus 5,将会直接跳到Google Play,对于国内各厂商定制过的系统,则跳转到各自的默认应用商店,或者弹出商店供选择。intentscheme相对完善的一点是,提供一个打开失败去向URL的选项,可以通过指定参数S.browser_fallback_url来指定去向URL。比如打开APP动作,如果打开失败,则跳转到APP下载页,这对于国内的特殊网络环境,还是挺有用的。

  • Safari内置APP广告条

在页面Head中增加如下meta, 添加智能App广告条,可以自动判断是否已安装应用,只能用于Safari,在第三方应用中就不行了。

<meta"apple-itunes-app"content"app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL"
  • Android Chrome内置APP安装提示

这个是Mobile Chrome 43 beta新加入的特性,在用户浏览某一个网站多次后,如果Chrome发现该站点有原生APP,则会提示用户下载原生APP,此项特性开发者无法干预,完全是Google的推荐行为。

  • Universal Links

在2015年的WWDC大会上,Apple推出了iOS 9的一个功能:Universal Links通用链接。如果你的App支持Universal Links,那就可以访问HTTP/HTTPS链接直接唤起APP进入具体页面,不需要其他额外判断;如果未安装App,访问此通用链接时,可以一个自定义网页。

优点:

  • 唯一性:不像自定义的scheme,因为它使用标准的HTTP/HTTPS链接到你的web站点,所以它不会被其它的app所声明.另外,Custom URL scheme 因为是自定义的协议,所以在没有安装 app 的情况下是无法直接打开的,而Universal Links本身是一个HTTP/HTTPS链接,所以有更好的兼容性;
  • 安全:当用户的手机上安装了你的app,那么iOS将去你的网站上去下载你上传上去的说明文件(这个说明文件声明了APP可以打开哪些类型的http链接)。因为只有你自己才能上传文件到你网站的根目录,所以你的网站和你的app之间的关联是安全的;
  • 可变:当用户手机上没有安装你的app的时候,Universal Links也能够工作。如果你愿意,在没有安装APP的时候,用户点击链接,会在safari中展示你网站的内容;
  • 简单:一个URL链接,可以同时作用于网站和app,可以定义统一的web-native协议;
  • 私有:其它APP可以在不需要知道是否安装了的情况下和你的APP相互通信;

缺点:

  • 只支持iOS9及以上系统;当使用Universal Link打开APP之后,状态栏右上角会出现链接地址,点击它会取消Universal Link,需引导用户重新使用Safari再次打开该链接,弹出Safari内置APP广告条,再点击打开重新开启Universal Link。

iOS9开启Universal Links

首先,你必须有一个域名,且这个域名的网站需要支持https,然后拥有网站的上传到.well-known目录的权限(这个权限是为了上传一个Apple指定的文件apple-app-site-association),有了这个先决条件才能够继续下面的步骤:

  • 创建一个json格式的命名为apple-app-site-association文件,注意这个文件必须没有后缀名,文件名必须为```apple-app-site-association``!!!
{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "9JA89QQLNQ.com.apple.wwdc", 
                "paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]
            },
            {
                "appID": "ABCD1234.com.apple.wwdc", 
                "paths": [ "*" ]
            }
        ]
    }
}

说明: appID = teamId.yourapp's bundle identifier
paths = APP支持的路径列表,只有这些指定的路径的链接,才能被APP所处理,大小写敏感。举个例子,如果你的网站是www.domain.com,你的path写的是"/support/*",那么当用户点击www.domain.com/support/<path>?<params>=<value>,就可以唤醒APP了,相反www.domain.com/other就不会。此外Apple为了方便开发者,提供了一个 网址来验证我们编写的这个apple-app-site-association是否合法有效。

  • 激活Xcode工程中的Associated Domains能力,在其中的Domains中填入你想支持的域名(这里不是随便填的,是可以支持你需要的Universal Links的域名), 必须以applinks:为前缀,例如:applinks:www.domain.comApple将会在合适的时候,从这个域名请求apple-app-site-association文件。注意:当你打开Associated Domains后,Xcode会在你的工程中添加.entitlements文件,并且登录开发者中心,可以看到Associated Domains处于Enable状态。

  • AppDelegate里实现如下代理方法:

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
    // NSUserActivityTypeBrowsingWeb 由Universal Links唤醒的APP
    if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
        NSURL *webpageURL = userActivity.webpageURL;
        NSString *host = webpageURL.host;
        if ([host isEqualToString:@"yohunl.com"]) {
            //进行我们需要的处理
        } else {
            [[UIApplication sharedApplication]openURL:webpageURL];
        }
    }
    return YES;
}

至此APP已经开启Universal Links,可以通过链接唤醒APP,并跳转至指定页面了。

  • Android App Links

在2015年的Google I/O大会上,Android M宣布了一个新特性:App Links让用户在点击一个普通web链接的时候可以打开指定APP的指定页面,前提是这个APP已经安装并且经过了验证,否则会显示一个打开确认选项的弹出框。在推动deep linking上Google和Apple可谓英雄所见略同,优缺点也大致相同,只支持Android M以上系统。

Android M开启Universal Links

开启Android App Links的方式也大致同iOS一致:

先决条件:
  1. 注册一个域名
  • 域名的SSL通道

  • 具有上传JSON文件到域名的能力

  • Android Studio 1.3 Preview 及以上

  • Gradle 版本 — com.android.tools.build:gradle:1.3.0-beta3 及以上

  • 设置 compileSdkVersion 为 android-MNC 及以上

  • buildToolsVersion — 23.0.0 rc2 及以上

  • 创建一个json格式的web-app关联文件,如assetlinks.json,上传到web端

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "example.com.puppies.app",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
  },
  {
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "example.com.monkeys.app",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

其中
package_name: manifest中声明的包名。
sha256_cert_fingerprints: 可以使用如下命令生成APP的sha256指纹签名

// keystore中持有app release keys的app路径。
// 这个路径依赖于项目设置,因此不同的app是不同的。
keytool -list -v -keystore my-release-key.keystore

上传这个文件到服务器的.well-known/assetlinks.json,为了避免今后每个app链接请求都访问网络,安卓只会在app安装的时候检查这个文件。

  • 创建一个处理App Links的activity,这个activity的目的是为了实现一种这样的机制:负责捕获与解析深度链接,同时转发用户到正确的视图。同时配置激活App Links能力,如下所示:
<activity
  android:name="com.your.app.activity.ParseDeepLinkActivity"
  android:alwaysRetainTaskState="true"
  android:launchMode="singleTask"
  android:noHistory="true"
  android:theme="@android:style/Theme.Translucent.NoTitleBar">
  
  // 此处激活 App Links
  <intent-filter android:autoVerify="true">
    // 注意yourdomain.com 与 www.yourdomain.com 被看成两个不同的域名,因此你需要为每个域名添加一对http和https
    <data android:scheme="http" android:host="yourdomain.com" />
    <data android:scheme="https" android:host="yourdomain.com" />
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
  </intent-filter>
</activity>
  • 实现App Linksactivity的处理逻辑
public class ParseDeepLinkActivity extends Activity {
  public static final String PRODUCTS_DEEP_LINK = "/products";
  public static final String XMAS_DEEP_LINK = "/campaigns/xmas";
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    // Extrapolates the deeplink data
    Intent intent = getIntent();
    Uri deeplink = intent.getData();
 
    // Parse the deeplink and take the adequate action 
    if (deeplink != null) {
      parseDeepLink(deeplink);
    }
  }
  
  private void parseDeepLink(Uri deeplink) {
    // The path of the deep link, e.g. '/products/123?coupon=save90'
    String path = deeplink.getPath();
 
    if (path.startsWith(PRODUCTS_DEEP_LINK)) {
      // Handles a product deep link
      Intent intent = new Intent(this, ProductActivity.class);
      intent.putExtra("id", deeplink.getLastPathSegment()); // 123
      intent.putExtra("coupon", deeplink.getQueryParameter("coupon")); // save90
      startActivity(intent);
    } else if (XMAS_DEEP_LINK.equals(path)) {
      // Handles a special xmas deep link
      startActivity(new Intent(this, XmasCampaign.class));
    }  else {
      // Fall back to the main activity
      startActivity(new Intent(context, MainActivity.class));
    }
  }
}

至此APP已经开启App Links,可以通过链接唤醒APP,并跳转至指定页面了。

后记

总结以上各种方案,唤醒能力似乎都不是很完美,从长远技术趋势来看都是Deep Links,都需要

  • 一个支持HTTPS的web站

但面对移动互联网浪潮中海量APP的唤醒能力需求,一定会有创业公司来做这件事,比如国外的HoKoLinks,国内的魔窗,是自己造轮子,还是用轮子,各有利弊。

67人点赞 iOS进阶   zyl04401 拥有4钻 (约0.77元) 关注 "小礼物走一走,来简书关注我" 赞赏 广告

被以下专题收入,发现更多相似内容

Android世界 Mobile Dev 知识祝专栏 Android... 当我们在谈论编... iOS进阶指南 iOS开发 展开更多

推荐阅读更多精彩内容

广告 zyl04401关注 拥有4钻 (约0.77元) 浅谈动态跟踪技术之DTrace 阅读 3166 iOS 10 UserNotifications 实践 阅读 336 广告 评论27 赞67 67赞68赞 赞赏