分类 Web 下的文章

2018年11月26日发表的“360 AI音箱H5开发实践”一文中,曾简单提到“与Native交互”。本文将就此主题深入探讨H5与App交互的几种常见模式。

首先声明,本文涉及的H5与App交互协议和模式没有什么特别独到之处,相反,它们恰恰是在业界既有经验基础上结合项目实际归纳提炼出来的。因此,文中涉及的技术和代码可以看作是行业经验落地的产物,不涉秘,也不是权威做法,仅供业界同仁参考。

本文内容如下:

  • 概述
  • 基础接口
  • 单向调用
  • 双向调用
  • 实现模式
  • 哪方主导

1. 概述

H5,在中国被专门用来指代开发内嵌于手机应用中的网页的技术,外国好像并没有这个说法。从技术上讲,H5是HTML5即Hyper Text Markup Language(超文本标记语言)第5版的简称。而HTML只是开发网页要用到的多种技术之一。除了HTML,还要用CSS设计界面,用JavaScript实现交互,甚至要用Node.js实现服务端逻辑。为什么H5会被用来笼统地指代这些技术呢?我猜一是因为它简单,二是移动端网页开发技术又恰好需要这么一个概念。

移动端网页运行在手机应用内嵌的浏览器引擎中,这个没有UI的内核容器统称WebView,即iPhone的UIWebView(iOS 2.0–12.0)、WKWebView(iOS 8.0+,macOS 10.10+)和Android的WebView。总之,WebView就是在手机应用中运行和展示网页的界面和接口(神奇的是,英文Interface,既可以翻译成“界面”也可以翻译成“接口”)。

H5与原生应用的交互都是通过原生应用中的WebView实现的。通过这个环境,H5可以调用原生应用注入其中的原生对象的方法,原生应用也可以调用H5暴露在这个环境中的JavaScript对象的方法,从而实现指令与数据的传输。

比如,在Android应用中,WebView类有一个公有方法addJavascriptInterface,签名为:

public void addJavascriptInterface (Object object, String name)

调用这个方法可以向WebView中以指定的名称name注入指定的Java对象object。这样,WebView中的JavaScript就可以通过name调用object的方法。比如:

 class JsObject {
    @JavascriptInterface
    public String toString() { return "injectedObject"; }
 }
 webview.getSettings().setJavaScriptEnabled(true);
 webView.addJavascriptInterface(new JsObject(), "injectedObject");
 webView.loadData("", "text/html", null);
 webView.loadUrl("javascript:alert(injectedObject.toString())");

在iOS或macOS中,需要通过创建WKWebView类的实例在应用中嵌入网页,交互过程类似。

- 阅读剩余部分 -

为什么本地开发要使用HTTPS?

因为有很多Web API必须在HTTPS环境下才可以使用。比如,Clipboard API and events(https://www.w3.org/TR/clipboard-apis)中的navigator.clipboard对象是通过扩展Navigator接口定义的:

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute Clipboard clipboard;
};

在此,partial的意思就是扩展Navigator接口,给它增加一个Clipboard类型的只读成员属性clipboard[SecureContext, SameObject]中的两个关键字是“扩展属性”(extended attribute),在这里修饰接口成员clipboardSecureContext表示`navigator只能在“安全上下文”中暴露clipboard属性,SameObject表示每次访问navigator.clipboard必须都返回相同的值。(参见:https://heycam.github.io/webidl/#SecureContext和https://heycam.github.io/webidl/#SameObject。)

关于“安全上下文”,W3C的Secure Contexts文档(https://w3c.github.io/webappsec-secure-contexts/)中有详细解释。根据MDN,全站HTTPS和通过http://localhost交付的网页是安全的。可以通过window.isSecureContext属性来检测当前上下文是否安全。

if (window.isSecureContext) {
  navigator.clipboard.writeText('Write this to clipboard!').then(ok => true, err => false)
}

另外还有一个重要原因,就是有时候HTTP的本地请求可能会被HTTPS服务器拒绝。

无论如何,我们在开发实践中都有可能碰到将本地Web服务HTTPS化的需求。这时候,我们可以创建自己私钥并签名一个根证书,并在开发环境中配置安装和信任自己的根证书。然后再通过这个根证书和私钥签发相应域名的SSL证书。

好吧,开始干吧。

- 阅读剩余部分 -

如果我问:你知道“剪贴板”(clipboard)吗?

恐怕没人不知道。我们每天都不知道自己要在电脑或手机上“复制”、“粘贴”多少回。每次“复制”、“粘贴”的背后,都会用到“剪贴板”。

根据“维基百科”:

The clipboard is a data buffer used for short-term data storage and/or data transfer between documents or applications used by cut, copy and paste operations and provided by the operating system.

翻译一下:

剪贴板是一种数据缓存,用于文档或应用间短期数据的存储/转移,在用户执行剪切、复制和粘贴操作时会用到,由操作系统提供。

这里最重要的一点在于,“剪贴板”是“由操作系统提供”的,因此它是系统级的一个软件特性。

对于前端开发者来说,如果我问:你知道怎么操作“剪贴板”吗?

很多人的第一反应可能是:使用clipboard.js吧……

clipboard.js的原理

clipboard.js(https://clipboardjs.com/)是在Github上有24000多个星,其流行程度可见一斑。关于这个库的用法,大家可以自己去看,我们这里主要分析其实现原理,以便了解目前操作剪贴板的主流技术。

简单来说,clipboard.js利用了两个已有的Web API(前者属于HTML5,后者属于HTML Editing API):

  • HTMLInputElement.select()
  • document.execCommand()

相应地,原理也只有两步。

- 阅读剩余部分 -

“360 AI音箱”即将发布,移动应用也在紧张有序地开发中。本文将介绍“360 AI音箱”移动应用H5部分的实践,主要包括:

  • 项目环境搭建
  • 与Native交互
  • 自定义中文字体
  • 表单输入
  • Docker部署

360 AI音箱应用H5部分简介

应用主要分4大版块:

  1. 内容:音箱可以播放的音乐、故事、有声书等等
  2. 技能:运营预配置的音箱指令
  3. 场景:用户自定义的音箱指令
  4. 我的:用户的智能设备、账号等

其中,“技能”和“场景”版块由H5制作。如下图所示,技能部分主要包括运营后端预配置的指令列表和详情两个页面。

注意:本文图片为“360 AI音箱”版权所有。另外,因为是设计稿截图,应用实际发布后的外观可能会有所不同。

- 阅读剩余部分 -

为什么要截取字体?

众所周知,相对于英文字体,中文字体天生是“庞然大物”。英文字体两三百KB已经很大了,而中文字体几MB十几MB都算小的。一方面,中文字体包含的字形数量极多,动辄数以千计甚至万计,而英文字体则只需包含几十个基本字符和符号,哪怕支持多种语言及字符变体,容量达到三千多个字形已经算非常庞大的了。另一方面,中文字形的曲折变化复杂度高,在基于轮廓的矢量字体设计中,用于控制中文字形曲线的控制点普遍比英文更多,因而需要的数据量更大,也会导致字体文件膨胀。

前端开发实践中,为了实现一些特殊视觉效果,经常需要使用某些特殊字体,而用户电脑上几乎不太可能安装这些字体,这时候通常需要使用Web字体技术,让浏览器动态下载我们的自定义字体。可是中文字体非常庞大,很多时候“全量”加载某个字体文件是不现实的。特别是对于一些动态页面且每个页面只有少量字符用到该字体的情况下。当然,也不是每个页面都会用到一个字体文件中的所有字符,全量加载本身也极其浪费。

研究表明,3500常用中文汉字(中国义务教育9年级需要掌握的汉字数量)即可覆盖日常使用汉字的99.8%:

  • 500 字(78.53202%)
  • 1000字(91.91527%)
  • 1500字(96.47563%)
  • 2000字(98.38765%)
  • 2500字(99.24388%)
  • 3000字(99.63322%)
  • 3500字(99.82015%)

可见,最常用的前500个汉字的覆盖率已经达到78%。因此,“全量”加载某个字体,特别是中文字体,在当前网络环境下不仅浪费流量和时间,而且也是完全没有必要的。这时候,我们可以根据网页用到的字符来截取字体的片段,这个技术英文叫subset,也就是“取子集”。

本文首先简单回顾Web自定义字体的技术规范,然后通过实例介绍两种前端常用的截取字体的技术。首先是CSS中的unicode-range属性,我们称之为“软截取技术”,因为它只是在本地既有字体或者浏览器已经下载的字体基础上做一个指向子集的“软链接”,并不能真正减小浏览器下载文件的大小。其次是Node命令行工具glyphhanger,我们称之为“硬截取技术”,即在服务端从“全量”字体中分离出一个体积相对极小的字体子集,做成Web字体通过Web服务器或CDN下发给浏览器。

无论是“软截取”,还是“硬截取”,都会用到Web字体和@font-face规则。因此,我们需要先来了解一下这个基础的Web标准语法。

- 阅读剩余部分 -