使用 Google TWA 把 Web 移形换影成原生 App

1,107 阅读12分钟

背景

对于alibaba.com来讲,随着ICBU全球买,全球卖战略的迅速发展,弱网弱地区的用户日益增多,其中印度地区用户已经超过美国地区用户成为网站最大的用户来源。但这部分用户的网络差、机型差、内存小,下载并保留APP对他们的来讲成本较高,本着客户第一的精神,我们需要思考,如何才能让用户既能在应用市场下载我们的APP并产生持续转化,又能保证用户在浏览网站的体验是较好的。

那么这里就有几点需要考虑的:

  • 体验是较好的
  • 原生APP体积足够小
  • 开发成本和维护成本需要尽可能的低

根据以上几点诉求,目前来看最好的方式就是把符合PWA规范(体验好)的WEB网站(开发和维护成本低)封装为一个独立的可上架应用市场的原生APP(体积小)。


效果展示

qqq.gif


PWA是什么

PWA(Progressive web apps,渐进式 Web 应用),指运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序,使其具有与原生应用相同的用户体验优势。

主要包含以下能力(PWA具体怎么实现不是本文的重点,大家可以参阅MDN上面的文档。):

  • APP shell
  • service worker
  • web 存储
  • index DB
  • 将网站添加到桌面
  • 消息推送和通知
  • 离线访问

总之,通过使用PWA能力的网站,会让用户的在浏览网站中的体验更加顺畅,更加具有native的感觉。


WEBVIEW/CCT/TWA的区别

webview

一般来讲,在安卓开发中,可以通过webview的方式很好的承担web的展示,但他的局限性也很明显,webview并不是一个浏览器,如果我们想要实现一部分浏览器的能力的话,需要我们在端上独立开发,且无法享受到浏览器升级给我们带来的便捷性。

CCT(Chrome Custom Tabs)

为了解决webview上述的一些问题,Google 推出了CCT(Chrome Custom Tabs)的技术,他可以在native内使用用户手机的浏览器的特性去渲染网站。但他也有一个关键性的限制,就是会在顶部有一个地址栏的显示,这样的体验就会让用户感觉不是在native中。

TWA(Trusted Web Activities )

为了解决CCT的问题,Google推出了新的技术体系:TWA(Trusted Web Activities ),TWA是集成 Web 应用的新方法,可以通过基于 Custom Tabs 的协议将 PWA 应用和 Android app 进行集成。通过使用 Digital AssetLinks 的方式对网站和native进行鉴权,保证双方的开发者是相同的,这样变可以隐藏CCT中的地址栏,使得网站更加native化。

下面我们通过图片来看一下三者之间的区别:

image.png


怎么实现TWA

通过上图我们可以看到TWA拥有了非常多的优点,他不仅可以全屏展示网站内容,还可以共享Chrome的存储,使用最新的Chrome API且不需要native的开发能力。另外,他还有一个最大的优点就是,实现成本很低(前提是网站已经支持了PWA)。

所有需要做的事情如下,且我会在本文中,全部介绍完:

1.拿到通过Lighthouse认证的PWA图标

2.初始化项目

3.建立网站和TWA的互信关系(为了隐藏地址栏)

4.构建并签名你的APP


拿到通过Lighthouse认证的PWA图标

运行 Lighthouse 的方式有两种:1.作为 Chrome 扩展程序运行,2.作为命令行工具运行。 Chrome 扩展程序提供了一个对用户更友好的界面,方便读取报告。 命令行工具允许您将 Lighthouse 集成到持续集成系统。我们今天来介绍下如何通过Chrome 扩展程序运行。

  • 下载 Google Chrome 52 或更高版本。
  • 安装 Lighthouse Chrome 扩展程序
  • 点击 Chrome 工具栏上的 Lighthouse 图标(如果没有看到此图标,它可能隐藏在 Chrome 的主菜单中。)
  • 在需要分析的页面中,点击此图标,再点击 Generate report 按钮
  • 稍等片刻,变可以得到当前页面的报告

拿到报告后,我们遍可以看到当前页面有那些需要优化的点,对应的修改方案,报告中都会有完整的方案,我们只需要按照方案解决即可。


初始化项目(以下为官网译文和总结)

新建项目

当新建 Trusted Web Activities 时,项目必须为 API 16 或更高版本。

  • 打开 Android Studio并点击 Start a new Android Studio project选项。
  • Android Studio 将提示您选择 Activity 类型。由于 TWA 使用 support 库提供的 Activity,因此选择 Add No Activity 并点击 Next。以下是每个字段的简短描述:
  • Name: Android 桌面 上的应用程序名称 。
  • Package Name: Play 商店和 Android 设备上 Android 应用程序的唯一标识符。 有关为 Android 应用程序创建程序包名称的要求和最佳实践的请查看 文档
  • Save location: Android Studio 将在电脑中创建项目的目录。
  • Language: 该项目不需要编写任何 Java 或 Kotlin 代码。选择 Java 作为默认值。
  • Minimum API Level: support 库至少需要 API 16。选择 API 16 以上的版本。
  • 忽略其他选项,然后点击 Finish


获取 TWA 依赖库

为了设置TWA依赖库,你需要编辑几个文件。

首先找到 APP 目录下的 build.gradle(Trusted Web Activities 库使用 Java 8 功能 ,首先启用 Java 8。在 android 下添加 compileOptions 配置)


android {
        ...
    compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
    }
}


下一步将 TWA Support 库添加到项目中。向 dependencies 添加新的依赖:


dependencies {
   implementation 'com.github.GoogleChrome.custom-tabs-client:customtabs:d08e93fce3'
}


Android studio将会请求询问是否需要同步项目,点击 Sync Now 来同步项目。


启动 TWA Activity

通过编辑 Android App Manifest 来实现设置 TWA activity 。

Project Navigator 上,展开 app 部分,然后展开 manifests 并双击 AndroidManifest.xml 打开文件。

因为我们在创建项目时没有添加任何 Activity,因此 manifest 为空且仅包含 application 标记。

通过在 application 标记中插入 activity 标记来 添加 TWA Activity:


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.twa.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity
            android:name="com.google.androidbrowserhelper.trusted.LauncherActivity">

           <!-- Edit android:value to change the url opened by the Trusted Web Activity -->
           <meta-data
               android:name="android.support.customtabs.trusted.DEFAULT_URL"
               android:value="https://airhorner.com" />

           <!-- This intent-filter adds the Trusted Web Activity to the Android Launcher -->
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>

           <!--
             This intent-filter allows the Trusted Web Activity to handle Intents to open
             airhorner.com.
           -->
           <intent-filter>
               <action android:name="android.intent.action.VIEW"/>
               <category android:name="android.intent.category.DEFAULT" />
               <category android:name="android.intent.category.BROWSABLE"/>

               <!-- Edit android:host to handle links to the target URL-->
               <data
                 android:scheme="https"
                 android:host="airhorner.com"/>
           </intent-filter>
        </activity>
    </application>
</manifest>


添加到 XML 的标记是标准的 Android App Manifest。Trusted Web Activities 有两个相关信息:

  • meta-data 标记告诉 TWA 活动打开哪个 URL 。修改 android:value 为你要打开的 PWA 页面的 URL。示例为 https://airhorner.com
  • 在第二个 intent-filter 标签允许 TWA 拦截 Android Intents 打开 https://airhorner.com。该 android:host 内部属性 data 标签必须指向被 TWA 打开的 网址。


建立网站和TWA的互信关系(隐藏地址栏)

Trusted Web Activities 需要在 Android 应用程序和网站之间建立关联以删除 URL 栏。此关联是通过 Digital Asset Links 创建的, 必须用两种方式建立关联:从 APP 链接到网站从网站链接到 APP。也可以将 APP 设置为网站验证,并设置 Chrome 以跳过网站到 APP 验证,以进行调试。


建立从 APP 到网站的关联

打开 string 资源文件 app > res > values > strings.xml 并在下面添加 Digital AssetLinks 语句:


<resources>
    <string name="app_name">AirHorner Trusted Web Activity</string>
    <string name="asset_statements">
        [{
            \"relation\": [\"delegate_permission/common.handle_all_urls\"],
            \"target\": {
                \"namespace\": \"web\",
                \"site\": \"https://airhorner.com\"}
        }]
    </string>
</resources>


更改 site 属性的内容以匹配 TWA 打开的 scheme 和 domain。

回到 AndroidManifest.xml 文件,通过在 application 标记添加新 meta-data 标记链接到该 statements :


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.twa.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <meta-data
            android:name="asset_statements"
            android:resource="@string/asset_statements" />

        <activity>
            ...
        </activity>

    </application>
</manifest>


我们现在已经建立了从 Android APP 到网站的关联。我们可以不创建网站到 APP 的验证来调试。以下是在开发设备上测试它的方法:

启用调试模式
  1. 在开发设备上打开 Chrome,导航到 chrome://flags,在非 root 设备上搜索名为 Enable command line 的项目,然后将其更改为 ENABLED,然后重新启动浏览器。
  2. 接下来,在终端上,使用 Android Debug Bridge (随着 Android Studio一起安装),并运行以下命令:
adb shell "echo '_ --disable-digital-asset-link-verification-for-url=\"https://airhorner.com\"' > /data/local/tmp/chrome-command-line"

关闭 Chrome 并从 Android Studio 重新启动您的应用程序。现在应该以全屏显示应用程序。


建立从网站到APP的关联

开发人员需要从 APP 拿到2条信息才能创建关联:

  • Package Name: 第一个信息是 APP 的包名称。这与创建 APP 时生成的包名称相同。可以在 Gradle Scripts > build.gradle (Module: app) 中找到 applicationId 的值。
  • SHA-256 Fingerprint: 必须签名 Android 应用程序才能上传到 Play 商店。相同的签名用于通过上传密钥的 SHA-256 在网站和 APP 之间建立连接。

Android文档 详细说明了如何使用Android Studio生成密钥。请记下密钥的路径别名密码,因为下一步需要用到。使用 keytool 提取 SHA-256,使用以下命令:


keytool -list -v -keystore  -alias  -storepass  -keypass 


SHA-256 打印在 Certificate 中。下面是个示例输出:


keytool -list -v -keystore ./mykeystore.ks -alias test -storepass password -keypass password

Alias name: key0
Creation date: 28 Jan 2019
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Test Test, OU=Test, O=Test, L=London, ST=London, C=GB
Issuer: CN=Test Test, OU=Test, O=Test, L=London, ST=London, C=GB
Serial number: ea67d3d
Valid from: Mon Jan 28 14:58:00 GMT 2019 until: Fri Jan 22 14:58:00 GMT 2044
Certificate fingerprints:
   SHA1: 38:03:D6:95:91:7C:9C:EE:4A:A0:58:43:A7:43:A5:D2:76:52:EF:9B
   SHA256: F5:08:9F:8A:D4:C8:4A:15:6D:0A:B1:3F:61:96:BE:C7:87:8C:DE:05:59:92:B2:A3:2D:05:05:A5:62:A5:2F:34
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3

得到这两条信息后,请打开 assetlinks 生成器,填写字段并点击 Generate Statement。复制生成的语句,并设置到你网站的 /.well-known/assetlinks.json 目录中。


创建图标

当通过 Android Studio 新建一个项目后,他会自带一个默认的图标。作为开发中,你希望使用一个与其他APP不同的图标。 Android Studio 有 Image Asset Studio 的功能,他可以提供必要的工具去创造一个适用于任何尺寸和像素的图标。

打开 Android Studio, 导航到 File > New > Image Asset, 选择Launcher Icons (Adaptative and Legacy),通过提示为你的应用创建一个合适图标。


添加启动画面

在 Chrome 75 以上版本,TWA支持添加启动动画的动能,我们只需通过通过添加几张图片和设置来实现。(确保使用 Chrome 75 以上版本,并使用最新的TWA依赖库


给启动动画添加一些图片

安卓有不同的屏幕尺寸和像素密度,为了保证启动动画适配所有设备,您需要为每个像素密度生成图像。

完全解释 支持所有像素 超出了本文的范围,但是一个例子是创建一个320x320dp的图像,它表示任何密度的设备屏幕上2x2英寸的正方形,在mdpi密度下相当于320x320像素。

从那里我们可以得到其他像素密度所需的大小。下面是一个列表,其中包含像素密度、应用于基本大小(320x320dp)的乘数、以像素为单位的结果大小以及应在Android Studio项目中添加图像的位置。

image.png


更新一些配置

首先,在Android Manifest (AndroidManifest.xml)添加 content-provider


<application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.example.twa.myapplication.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>
</application>


然后,添加 res/xml/filepaths.xml资源,并且制定路径为TWA的启动动画


<paths>
    <files-path path="twa_splash/" name="twa_splash" />
</paths>


最后,在Android Manifest (AndroidManifest.xml)添加 meta-tags


<activity android:name="com.google.androidbrowserhelper.trusted.LauncherActivity">
    ...
    <meta-data android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
               android:resource="@drawable/splash"/>
    <meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
               android:resource="@color/colorPrimary"/>
    <meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_FADE_OUT_DURATION"
               android:value="300"/>
    <meta-data android:name="android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY"
               android:value="com.example.twa.myapplication.fileprovider"/>
    ...
</activity>


确保启动时页面是透明的

另外,需要确保启动时页面是透明的,从而避免启动动画前出现白屏。

首先,在 res/styles.xml 添加一个新的主题:


<style name="Theme.LauncherActivity" parent="Theme.AppCompat.NoActionBar">
     <item name="android:windowAnimationStyle">@null</item>
     <item name="android:windowIsTranslucent">true</item>
     <item name="android:windowNoTitle">true</item>
     <item name="android:windowBackground">@android:color/transparent</item>
     <item name="android:backgroundDimEnabled">false</item>
 </style>


然后在Android Manifest (AndroidManifest.xml)添加对新样式的引用


<application>
    ...
    <activity android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
              android:theme="@style/Theme.LauncherActivity">
    ...
    </activity>
</application>


构建并签名你的APP

assetlinks 存在您的网站中,asset_statements在 Android APP 中配置完成后,下一步就是生成签名的 APP。请查看 文档

可以使用 adb 将 APK 安装到测试设备中:


adb install app-release.apk


如果验证失败,则可以使用 adb 从连接设备并电脑终端查看错误消息。


adb logcat | grep -e OriginVerifier -e digital_asset_links


bingo,生成上传 APK 后,就 可以将应用上传到 Play 商店 了。


总结

Alibaba.com版本的TWA目前已经在Amazon Play上架,他所带来的业务价值比现有的网站要高出很多,虽然离原生的APP还是有一定的差距。

另外补充一点:TWA永远不是取代原生APP的方案,他一定是通过弥补原生APP的一些不足,或者帮助原生APP去服务那些使得原生APP负重前行(安卓低版本等)的用户,对于任何技术来说,一定是相互配合实现业务价值的最大化,才是一个好的技术方案。


PS:TWA指通过Google TWA技术封装的原生APP。原生APP指通过java开发的原生APP


引用参考

developers.google.com/web/android…

www.infoq.cn/article/XTE…

juejin.cn/post/684490…