开源的Android系统实际上只提供基本的系统服务,不提供常见的扩展服务诸如地图、邮箱、搜索、推送、机器学习、应用内支付等,这些扩展服务被谷歌公司打包成GMS套件(全称Google Mobile Service,中文名叫谷歌移动服务)。在海外市场,许多商用App都依赖于GMS提供的服务,手机缺少GMS会使得这些App没法使用,而手机厂商预装GMS套件需要获得谷歌公司授权。2019年华为公司遭到美国制裁,导致华为手机没能获得GMS授权,致使海外市场陷入寒冬。为此,华为公司推出了自主可控的HMS套件(全称Huawei Mobile Service,中文名叫华为移动服务),意图打破制裁。 HMS是华为公司提供的一套App扩展服务框架,它分为两部分,一部分是面向普通用户的预装App,包括花瓣地图、花瓣邮箱、花瓣搜索、花瓣支付等;另一部分是面向开发者的HMS Core,它给开发者提供API接口,用于在App开发时集成相关服务。HMS Core是华为移动服务提供的端、云开放能力的合集,包含华为账号、应用内支付、推送服务、游戏服务、定位服务、地图服务、广告服务和机器学习服务等,它的开源代码仓库地址为https://gitee.com/hms-core,开发者可在该仓库下载对应源码学习。 扫描二维码是HMS的一项基础服务,虽然谷歌公司也提供了zxing扫码框架,但是zxing框架的集成步骤不够简洁,而且它的识别速度偏慢,识别准确率也不高,远不如HMS的扫码服务来得好用。下面介绍如何在App工程中集成HMS的扫码服务。 首先,因为扫码属于第三方服务,所以要修改模块的build.gradle,往dependencies节点添加如下一行配置,表示导入指定版本的扫码库:
implementation 'com.huawei.hms:scanplus:1.3.1.300'
接着打开AndroidManifest.xml,补充以下的相机权限配置
<!-- 相机 -->
<uses-permission android:name="android.permission.CAMERA" />
然后在Java代码中增加用于扫码的远程视图,并指定扫码结果的回调事件,新增的代码片段如下所示:
private RemoteView remoteView; // 声明一个HMS的远程视图对象
private int SCAN_FRAME_SIZE = 240; // 扫码框的默认尺寸
// 添加扫码的远程视图
private void addRemoteView(Bundle savedInstanceState) {
int screenWidth = Utils.getScreenWidth(this); // 获取屏幕宽度
int screenHeight = Utils.getScreenHeight(this); // 获取屏幕高度
int scanFrameSize = (int) (SCAN_FRAME_SIZE * Utils.getScreenDensity(this));
// 计算取景器的四周边缘。如果没有指定设置,它将位于布局的中间位置。
Rect rect = new Rect();
rect.left = screenWidth / 2 - scanFrameSize / 2;
rect.right = screenWidth / 2 + scanFrameSize / 2;
rect.top = screenHeight / 2 - scanFrameSize / 2;
rect.bottom = screenHeight / 2 + scanFrameSize / 2;
// 初始化远程视图实例
remoteView = new RemoteView.Builder().setContext(this)
.setBoundingBox(rect).setFormat(HmsScan.ALL_SCAN_TYPE).build();
// 当光线昏暗时,展示闪光灯开关按钮,以便用户决定是否开灯
remoteView.setOnLightVisibleCallback(visible -> {
if (visible) {
iv_flash.setVisibility(View.VISIBLE);
}
});
// 设置扫描结果的回调事件
remoteView.setOnResultCallback(result -> showResult(result));
// 将自定义视图加载到活动中.
remoteView.onCreate(savedInstanceState);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
rl_scan.addView(remoteView, params); // 往相对布局添加远程视图
}
// 显示扫码识别结果
private void showResult(HmsScan[] result) {
if (result != null && result.length > 0 && result[0] != null &&
!TextUtils.isEmpty(result[0].getOriginalValue())) {
Intent intent = new Intent(this, ScanResultActivity.class);
intent.putExtra(ScanUtil.RESULT, result[0]);
startActivity(intent); // 跳转到扫码结果页
}
}
在扫码结果页面,HMS不但支持获取结果文本,还支持获取条码的编码格式与结果类型,从而允许开发者更精准地辨别条码归属。下面是具体的扫码结果解析代码:
// 解析扫码结果
private void parserScanResult() {
// 从意图中获取可折叠的扫码结果
HmsScan hmsScan = getIntent().getParcelableExtra(ScanUtil.RESULT);
try {
String desc = String.format("扫码结果如下:\n\t\t格式为%s\n\t\t类型为%s\n\t\t内容为%s",
getCodeFormat(hmsScan.getScanType()),
getResultType(hmsScan.getScanType(), hmsScan.getScanTypeForm()),
hmsScan.getOriginalValue());
tv_result.setText(desc);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取扫码格式
private String getCodeFormat(int scan_type) {
String codeFormat = "未知(Unknown)";
if (scan_type == HmsScan.QRCODE_SCAN_TYPE) {
codeFormat = "快速响应码(QR code)";
} else if (scan_type == HmsScan.AZTEC_SCAN_TYPE) {
codeFormat = "阿兹特克码(AZTEC code)";
} else if (scan_type == HmsScan.DATAMATRIX_SCAN_TYPE) {
codeFormat = "数据矩阵码(DATAMATRIX code)";
} else if (scan_type == HmsScan.PDF417_SCAN_TYPE) {
codeFormat = "便携数据文件码(PDF417 code)";
} else if (scan_type == HmsScan.EAN13_SCAN_TYPE) {
codeFormat = "欧洲商品编码-标准版(EAN13 code)";
} else if (scan_type == HmsScan.EAN8_SCAN_TYPE) {
codeFormat = "欧洲商品编码-缩短版(EAN8 code)";
} else if (scan_type == HmsScan.ITF14_SCAN_TYPE) {
codeFormat = "外箱条码(ITF14 code)";
} else if (scan_type == HmsScan.UPCCODE_A_SCAN_TYPE) {
codeFormat = "商品统一代码-通用(UPCCODE_A)";
} else if (scan_type == HmsScan.UPCCODE_E_SCAN_TYPE) {
codeFormat = "商品统一代码-短码(UPCCODE_E)";
} else if (scan_type == HmsScan.CODABAR_SCAN_TYPE) {
codeFormat = "库德巴码(CODABAR)";
}
return codeFormat;
}
// 获取结果类型
private String getResultType(int scan_type, int scanForm) {
String resultType = "文本(Text)";
if (scan_type == HmsScan.QRCODE_SCAN_TYPE) {
if (scanForm == HmsScan.PURE_TEXT_FORM) {
resultType = "文本(Text)";
} else if (scanForm == HmsScan.URL_FORM) {
resultType = "网址(WebSite)";
} // 此处省略若干格式判断
} else if (scan_type == HmsScan.EAN13_SCAN_TYPE) {
if (scanForm == HmsScan.ISBN_NUMBER_FORM) {
resultType = "国际标准书号(ISBN)";
} else if (scanForm == HmsScan.ARTICLE_NUMBER_FORM) {
resultType = "产品(Product)";
}
} else if (scan_type == HmsScan.EAN8_SCAN_TYPE
|| scan_type == HmsScan.UPCCODE_A_SCAN_TYPE
|| scan_type == HmsScan.UPCCODE_E_SCAN_TYPE) {
if (scanForm == HmsScan.ARTICLE_NUMBER_FORM) {
resultType = "产品(Product)";
}
}
return resultType;
}
接下来分别举个条形码例子与二维码例子,看看到底能扫出什么东西,条形码例子如下图所示,这是某个商品的条形码。
二维码例子如下图所示,这是清华大学的微信公众号二维码。
运行测试App,打开扫码界面如下图所示。
把扫码框对准条形码图片,App识别成功跳到结果页面如下图所示。
返回之后继续扫描二维码图片,App识别成功跳到结果页面如下图所示。
由此验证了HMS扫码服务的准确性和高效率。你还等什么呢?快来试试HMS的扫码服务吧,完整的范例源码见https://gitee.com/hms-core/hms-scan-demo。