- 后端
- 前端
- 企业应用
【免费下载链接】GreaterWMS
This Inventory management system is the currently Ford Asia Pacific after-sales logistics warehousing supply chain process . After I leave Ford , I start this project . You can share your vacant warehouse space, use it for those in need, and generate income
本文以 GreaterWMS 开源仓库中内置的com-darryncampbell-cordova-plugin-intent(IntentShim)插件文档为骨架,结合其 JavaScript 桥接层、Android 原生实现与仓库中 ScanAPP.vue 的真实调用代码,系统讲解如何在 Cordova/Quasar 移动应用中收发 Android Intent,并给出在仓储扫码枪(如 Zebra/Symbol DataWedge)场景下的完整实战方案。读完本文,你将掌握该插件的全部 API 用法、参数含义、底层实现机制以及在 GreaterWMS 扫描 App 中的集成方式。
插件定位:Android Intent 机制的通用 shim 层
IntentShim 是一个面向 Android 平台的 Cordova 插件,它提供了一层“通用垫片(general purpose shim)”,把 Android 原生的 Intent 机制(发送与接收)暴露给 JavaScript 层。插件在页面上下文中定义了一个全局对象window.plugins.intentShim,所有 API 都挂载在该对象上,可运行于任何 Android 设备。
从源码结构看,插件由三部分组成:
- JS 桥接层:IntentShim.js —— 定义
IntentShim构造函数及全部 API,通过cordova/exec与原生层通信,最后挂载为window.plugins.intentShim; - Android 原生实现:IntentShim.java —— 继承
CordovaPlugin,负责解析 JS 传来的 JSON 参数、构造并发送android.content.Intent,以及把接收到的 Intent 序列化成 JSON 回传给 JS; - 工程注册文件:plugin.xml —— 声明 feature、intent-filter、权限、FileProvider 与 AndroidX 依赖。
插件本身以 MIT 协议开源,其代码部分借鉴了cordova-plugin-intent(napolitano)与cordova-webintent(chrisekelley)两个 MIT 项目,源码注释中保留了相应致谢(见 IntentShim.java 中的 Credit 注释)。
安装与工程集成
安装命令
文档给出的安装命令按 Cordova 版本区分:
- Cordova < 7(通过 git URL 安装):
cordova plugin add https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent.git- Cordova >= 7(通过 npm 包名安装):
cordova plugin add com-darryncampbell-cordova-plugin-intent在 GreaterWMS 中,该插件以本地插件形式随仓库分发,位于 com-darryncampbell-cordova-plugin-intent 目录,其 package.json 声明的版本为 2.1.0,并已记录在 Cordova 插件清单 android.json 中(对应 Android 平台的org.greaterwms.scanner.app应用)。
Android X 与 Android Support Library 的选择
文档明确了两条版本线:
- 需要 Android X 支持时,使用 2.x.x 及以上版本(仓库内置的 2.1.0 即属于该线);
- 仍使用旧的 Android Support Library 时,只能使用 1.3.x 版本。
Android X 支持不只体现在依赖声明上,插件还带了一个after_prepare钩子脚本 after_prepare.js:每次构建 Android 平台后,它都会检查platforms/android/gradle.properties,若缺失则自动追加android.useAndroidX=true与android.enableJetifier=true两行配置,从而保证 AndroidX 迁移在工程中生效。
与 Camera 插件的安装顺序
文档特别强调:如果同时安装cordova-plugin-camera,必须先安装 camera 插件,再安装本插件(“MUST install cordova-plugin-camera first”)。原因是本插件会向AndroidManifest.xml合并 FileProvider 与相关权限,安装顺序错误可能导致资源或权限合并冲突。
plugin.xml 中注册的内容
从 plugin.xml 可以看到插件在 Android 工程中实际注入了以下内容:
- 注册
IntentShimfeature,并设置onload=true(插件随应用启动即加载,保证广播接收器能力就绪); - 向应用 Activity 合并一个自定义 action 为
com.darryncampbell.cordova.plugin.intent.ACTION的 intent-filter; - 声明
READ_EXTERNAL_STORAGE与REQUEST_INSTALL_PACKAGES两个权限(前者用于读取外部存储中的文件,后者与安装 APK 场景相关); - 注册自定义 FileProvider(
CordovaPluginIntentFileProvider,authority 为${applicationId}.darryncampbell.cordova.plugin.intent.fileprovider),并引用provider_paths.xml资源文件; - 声明 AndroidX 核心库依赖
androidx.core:core:1.1.0。
API 总览
插件共暴露 8 个文档记录的 API,另外 JS 层还提供若干扩展 API。先看整体一览:
| API | 作用 | 对应 Android 原生机制 |
|---|---|---|
registerBroadcastReceiver | 为指定 filter 注册动态广播接收器 | Context.registerReceiver+IntentFilter |
unregisterBroadcastReceiver | 注销全部已注册的广播接收器 | Context.unregisterReceiver |
sendBroadcast | 发送一个广播 Intent | Context.sendBroadcast |
onIntent | 获取并持续监听启动 Activity 的 Intent | Activity.onNewIntent() |
startActivity | 启动一个新的 Activity | Activity.startActivity |
getIntent | 获取启动本 Activity 的 Intent | Activity.getIntent() |
startActivityForResult | 启动 Activity 并接收其返回结果 | Activity.startActivityForResult |
sendResult | 向启动方返回RESULT_OK并结束本 Activity | Activity.setResult+Activity.finish |
JS 层在 IntentShim.js 中还额外定义了startService、onActivityResult、realPathFromUri以及RESULT_CANCELED(0)、RESULT_OK(-1) 两个结果常量,这些在原生实现 IntentShim.java 中均有对应分支(startService、onActivityResult、realPathFromUri),说明是文档之外的扩展能力。
registerBroadcastReceiver:动态注册广播接收器
基本用法
window.plugins.intentShim.registerBroadcastReceiver(filters, callback);该函数为指定的过滤器列表注册一个动态广播接收器,当任意过滤器命中的广播到达时,回调函数被触发。文档示例注册了两个 action:
window.plugins.intentShim.registerBroadcastReceiver({ filterActions: [ 'com.darryncampbell.cordova.plugin.broadcastIntent.ACTION', 'com.darryncampbell.cordova.plugin.broadcastIntent.ACTION_2' ] }, function(intent) { console.log('Received broadcast intent: ' + JSON.stringify(intent.extras)); });底层实现:filter 的三类参数与 keepCallback
原生层 IntentShim.java 的execute("registerBroadcastReceiver", ...)分支会解析三类过滤器参数并依次填入IntentFilter:
filterActions(必填):对应filter.addAction(),为空会直接返回INVALID_ACTION;filterCategories(可选):对应filter.addCategory();filterDataSchemes(可选):对应filter.addDataScheme(),用于按 data scheme(如http、file)过滤,源码注释提到这解决了 GitHub Issue 24 中关于 data scheme 过滤的需求。
注册完成后,插件以PluginResult.Status.NO_RESULT且setKeepCallback(true)的方式持有回调上下文(callback context),广播到达时通过newBroadcastReceiver()内部类把 Intent 转成 JSON 后持续推送到 JS 层,实现“一次注册、多次接收”的实时推送语义。每个接收器与回调上下文的映射保存在receiverCallbacks这个Map<BroadcastReceiver, CallbackContext>中。
unregisterBroadcastReceiver:注销广播接收器
基本用法
window.plugins.intentShim.unregisterBroadcastReceiver();调用后,所有通过registerBroadcastReceiver注册的接收器都会被注销,此后不会再收到任何已注册过滤器的广播。原生层通过遍历receiverCallbacks逐个调用unregisterReceiver并清空映射实现(unregisterAllBroadcastReceivers()方法)。
Android Quirks:生命周期管理
文档明确指出:开发者有责任在应用进入后台(background)或回到前台(foreground)时按需调用 unregister/register。官方示例在应用收到pause事件时注销接收器:
bindEvents: function() { document.addEventListener('pause', this.onPause, false); }, onPause: function() { window.plugins.intentShim.unregisterBroadcastReceiver(); }这一模式在 GreaterWMS 的 ScanAPP.vue 中得到了完整落地:onPause时注销、onResume时重新注册,避免后台状态下持续接收广播造成资源浪费或误触发。
sendBroadcast:发送广播 Intent
基本用法
window.plugins.intentShim.sendBroadcast(action, extras, successCallback, failureCallback);发送一个指定 action 的 Android 广播 Intent。文档示例(原文代码实为startActivity的调用形态,其语义为“向指定 action 发送带随机数 extras 的广播”):
window.plugins.intentShim.sendBroadcast({ action: "com.darryncampbell.cordova.plugin.intent.ACTION", extras: { 'random.number': Math.floor((Math.random() * 1000) + 1) } }, function() {}, function() {alert('Failed to open URL via Android Intent')});底层实现
原生层execute("sendBroadcast", ...)分支先通过populateIntent(obj, callbackContext)把 JSON 参数组装成android.content.Intent,再调用cordova.getActivity().sendBroadcast(intent)。populateIntent是插件最核心的参数解析函数,它支持下列字段:
action:Intent action 字符串;type:MIME 类型(与url同时存在时使用setDataAndType);url:URI 数据;当 Android N(API 24)及以上且以file://开头时,会自动走remapUriWithFileProvider改用 FileProvider 生成 content URI(remapUriWithFileProvider),否则通过CordovaResourceApi.remapUri转换;extras:额外数据,支持 String/Boolean/Integer/Long/Double 等基本类型,嵌套 JSONObject 会被转换为 Bundle;其中EXTRA_TEXT(配合text/html类型)用Html.fromHtml处理、EXTRA_STREAM支持以 URI 方式分享图片附件、EXTRA_EMAIL自动转成字符串数组、EXTRA_KEY_EVENT可构造 KeyEvent;component:显式 Intent 的目标组件(需同时提供package与class);package:目标应用包名;flags:Intent flag 数值数组,逐个addFlags;chooser:传入后通过Intent.createChooser包装为选择器。
组装完成后还会统一加上Intent.FLAG_GRANT_READ_URI_PERMISSION,确保接收方能读取共享的 URI 数据。
onIntent 与 getIntent:读取启动 Intent
onIntent:映射 Android onNewIntent
window.plugins.intentShim.onIntent(callback);onIntent返回启动 Activity 的 Intent,并映射到 Android 的Activity.onNewIntent()方法。注册的回调在 Activity 被(重新)启动时被调用:
window.plugins.intentShim.onIntent(function (intent) { console.log('Received Intent: ' + JSON.stringify(intent.extras)); });原生层onNewIntent(Intent)方法(IntentShim.java)有一个值得注意的细节:如果 JS 层尚未注册onIntent回调,原生层会把新 Intent 暂存到deferredIntent字段;当 JS 层随后调用onIntent时,插件会立刻把暂存的 Intent 补发出去并清空该字段。getIntent同样会优先消费deferredIntent,其次才返回cordova.getActivity().getIntent()。这一机制保证了“Intent 先到、JS 回调后注册”的时序不会丢数据。
Android Quirks:启动模式 SingleTop / SingleTask
文档说明:默认情况下 Android 应用以SingleTop启动模式创建。若想改为SingleTask,需要在config.xml中修改:
<platform name="android"> ... <preference name="AndroidLaunchMode" value="singleTask"/> </platform>两种模式的核心差异在于:SingleTop下 Activity 已在栈顶时新 Intent 走onNewIntent;SingleTask下系统会确保栈中只有一个任务实例,其它实例被销毁并把 Intent 交给栈顶实例。选择哪种模式会影响onNewIntent的触发时机,开发者需根据应用场景权衡。
getIntent:获取启动 Intent
window.plugins.intentShim.getIntent(resultCallback, failureCallback);对应Activity.getIntent(),返回启动本 Activity 的 Intent。文档示例:
window.plugins.intentShim.getIntent( function(intent) { console.log('Action' + JSON.stringify(intent.action)); var intentExtras = intent.extras; if (intentExtras == null) intentExtras = "No extras in intent"; console.log('Launch Intent Extras: ' + JSON.stringify(intentExtras)); }, function() { console.log('Error getting launch intent'); });startActivity:启动新 Activity
基本用法
window.plugins.intentShim.startActivity(params, successCallback, failureCallback);对应Activity.startActivity。文档给出两个典型示例——拉起地图与打开浏览器:
// 启动地图 window.plugins.intentShim.startActivity( { action: window.plugins.intentShim.ACTION_VIEW, url: 'geo:0,0?q=London' }, function() {}, function() {alert('Failed to open URL via Android Intent')} ); // 打开浏览器 window.plugins.intentShim.startActivity( { action: window.plugins.intentShim.ACTION_VIEW, url: 'http://www.google.co.uk' }, function() {}, function() {alert('Failed to open URL via Android Intent')} );底层实现:无处理者时的错误处理
原生层startActivity(Intent, ...)方法(IntentShim.java)会先用intent.resolveActivityInfo(...)检查系统中是否存在能处理该 Intent 的应用;若没有,会向 JS 返回PluginResult.Status.ERROR,从而触发failureCallback。这与文档示例中“Failed to open URL via Android Intent”的失败提示正好对应,也是插件对“打开外部应用失败”场景的兜底设计。
startActivityForResult 与 sendResult:Activity 间往返传值
startActivityForResult
window.plugins.intentShim.startActivityForResult(params, resultCallback, failureCallback);对应Activity.startActivityForResult,启动新 Activity 并把结果通过resultCallback返回。文档示例是选择 Android 联系人:
window.plugins.intentShim.startActivityForResult( { action: window.plugins.intentShim.ACTION_PICK, url: "content://com.android.contacts/contacts", requestCode: 1 }, function(intent) { if (intent.extras.requestCode == 1) { console.log('Picked contact: ' + intent.data); } }, function() { console.log("StartActivityForResult failure"); });原生实现中,requestCode未显式给出时默认取 1;插件通过cordova.setActivityResultCallback(this)接管结果回调,并在 onActivityResult 中把requestCode、resultCode一并塞入返回 Intent 的 extras 后回传(若用户取消导致 intent 为 null,也会构造一个仅含 requestCode/resultCode 的空 Intent 返回,保证回调不丢失)。JS 层的onActivityResult(callback)是另一种接收结果的方式。
sendResult:向启动方回传结果
window.plugins.intentShim.sendResult(args, callback);假设本应用是被startActivityForResult启动的,sendResult会以Activity.RESULT_OK状态把args.extras中的数据返回给启动方,然后调用finish()结束当前 Activity。
Android Quirks:args与callback两个参数都必须提供;不需要该功能时传空对象和空函数即可:
window.plugins.intentShim.sendResult({}, function() {});文档的完整示例演示了多种数据类型的回传:
window.plugins.intentShim.sendResult( { extras: { 'Test Intent': 'Successfully sent', 'Test Intent int': 42, 'Test Intent bool': true, 'Test Intent double': parseFloat("142.12") } }, function() { } );原生层execute("sendResult", ...)会遍历 extras 并依据 JSON 类型分别调用putExtra(嵌套 JSONObject 转 Bundle),随后setResult(Activity.RESULT_OK, result)再finish(),完整对应文档描述。
预定义常量
插件在 JS 层预定义了以下常量(定义见 IntentShim.js),可直接用于构造 Intent:
window.plugins.intentShim.ACTION_SEND(android.intent.action.SEND)window.plugins.intentShim.ACTION_VIEW(android.intent.action.VIEW)window.plugins.intentShim.EXTRA_TEXT(android.intent.extra.TEXT)window.plugins.intentShim.EXTRA_SUBJECT(android.intent.extra.SUBJECT)window.plugins.intentShim.EXTRA_STREAM(android.intent.extra.STREAM)window.plugins.intentShim.EXTRA_EMAIL(android.intent.extra.EMAIL)window.plugins.intentShim.ACTION_CALL(android.intent.action.CALL)window.plugins.intentShim.ACTION_SENDTO(android.intent.action.SENDTO)window.plugins.intentShim.ACTION_GET_CONTENT(android.intent.action.GET_CONTENT)window.plugins.intentShim.ACTION_PICK(android.intent.action.PICK)
此外 JS 层还定义了ACTION_INSTALL_PACKAGE、ACTION_UNINSTALL_PACKAGE、RESULT_CANCELED(0) 与RESULT_OK(-1),可用于安装/卸载应用与结果码判断。
Intent 的 JSON 表示
无论接收广播、接收启动 Intent 还是接收 Activity 结果,原生层都会调用getIntentJson(Intent)(IntentShim.java)把 Android Intent 序列化为 JSON 对象回传给 JS,包含以下字段:
type:MIME 类型;extras:extras Bundle 递归转换成的 JSON 对象;action:Intent action;categories:category 集合;flags:flag 整数值;component:目标组件;data:data URI 字符串;package:目标包名;clipItems(Android 4.4+ 且有 ClipData 时):包含htmlText、intent、text、uri、type、extension的数组,用于处理多内容分享场景。
这也是开发者在回调中经常访问intent.extras、intent.action、intent.data的原因——它们直接对应原生 Intent 的同名字段。
GreaterWMS 实战:基于 IntentShim 的 Zebra DataWedge 扫码集成
该插件在 GreaterWMS 中的核心应用场景是 ScanAPP.vue 的扫码页面:通过 Intent 机制与 Zebra/Symbol 手持终端上的 DataWedge 服务通信,把硬件扫码枪的条码数据实时送入 WebView。
发送 DataWedge 命令(sendBroadcast)
ScanAPP.vue 中的sendCommand函数封装了对 DataWedge API 的广播命令发送,把命令名与参数放进 extras,并携带SEND_RESULT要求 DataWedge 返回执行结果:
function sendCommand (extraName, extraValue) { var broadcastExtras = {} broadcastExtras[extraName] = extraValue broadcastExtras.SEND_RESULT = sendCommandResults window.plugins.intentShim.sendBroadcast({ action: 'com.symbol.datawedge.api.ACTION', extras: broadcastExtras }, function () { }, function () { } ) }基于此封装,页面可以下发com.symbol.datawedge.api.SOFT_SCAN_TRIGGER(软触发扫描)、GET_VERSION_INFO(查询 DataWedge 版本)、SET_CONFIG(配置扫描 profile,包括把扫码结果以intent_output_enabled+intent_action: 'com.zebra.cordovademo.ACTION'的方式输出)等命令。
注册接收器接收扫码结果(registerBroadcastReceiver)
ScanAPP.vue 中注册了两个 action 的广播接收器:
function registerBroadcastReceiver () { window.plugins.intentShim.registerBroadcastReceiver({ filterActions: [ 'com.zebra.cordovademo.ACTION', 'com.symbol.datawedge.api.RESULT_ACTION' ], filterCategories: [ 'android.intent.category.DEFAULT' ] }, function (intent) { // 命令执行结果:RESULT_INFO if (intent.extras.hasOwnProperty('RESULT_INFO')) { ... } // DataWedge 版本信息 if (intent.extras.hasOwnProperty('com.symbol.datawedge.api.RESULT_GET_VERSION_INFO')) { ... } // 扫描到的条码数据 else if (!intent.extras.hasOwnProperty('RESULT_INFO')) { barcodeScanned(intent, new Date().toLocaleString()) } } ) }其中barcodeScanned读取intent.extras['com.symbol.datawedge.data_string']并把条码写入隐藏输入框、触发input事件,从而驱动 GreaterWMS 的扫码业务逻辑。这个例子同时用到了本文讲解的filterActions、filterCategories、extras 读取、生命周期注销等多个知识点,是该插件在真实仓储项目中的完整范式。
生命周期管理(unregisterBroadcastReceiver)
如文档强调,扫描页面在pause事件中调用unregisterBroadcastReceiver()、在resume事件中重新registerBroadcastReceiver()(ScanAPP.vue),确保后台不接收广播、回到前台时恢复扫码能力。
注意事项与限制
- 平台支持:插件仅支持 Android 平台,iOS 与其它平台无对应实现;
- 兼容性前提:仓库内置版本为 2.1.0(AndroidX 线),要求工程启用 AndroidX(钩子脚本会自动写入
gradle.properties);若项目仍使用 Android Support Library,需要回退到 1.3.x 版本; - 已测版本:README 记录插件在 Cordova 6.5.0 与 Cordova Android 6.2.1 上完成测试;在当前仓库环境中实际以 Cordova 工程插件形式随 app/src-cordova 分发使用;
- 权限要求:读取外部存储 URI(
realPathFromUri、file://分享)需要READ_EXTERNAL_STORAGE权限,插件会通过ActivityCompat.requestPermissions主动申请,但未授权时相关 API 会直接报错,开发者需在 JS 层处理失败回调; - 时序注意:
registerBroadcastReceiver持久的回调(keepCallback)要求开发者自行管理注册/注销生命周期,避免泄漏与重复接收;onIntent与getIntent对deferredIntent的消费是单次的,注册回调前到达的 Intent 只会被补发一次。
- 后端
- 前端
- 企业应用
【免费下载链接】GreaterWMS
This Inventory management system is the currently Ford Asia Pacific after-sales logistics warehousing supply chain process . After I leave Ford , I start this project . You can share your vacant warehouse space, use it for those in need, and generate income
相关推荐
GreaterWMS 移动端启动画面插件深度解析:cordova-plugin-splashscreen 版本演进、配置实战与源码剖析
GreaterWMS 移动端启动画面插件深度解析:cordova plugin splashscreen 版本演进、配置实战与源码剖析 GreaterWMS 的
后端前端企业应用GreaterWMS 移动端屏幕常亮方案:cordova-plugin-insomnia 插件原理与实战配置
GreaterWMS 移动端屏幕常亮方案:cordova plugin insomnia 插件原理与实战配置 导读 本文以 GreaterWMS 开源仓库中内置
后端前端企业应用Android Sunflower深度链接:导航组件与Intent处理
Android Sunflower深度链接:导航组件与Intent处理 项目概述 Android Sunflower是一个园艺应用,展示了将基于视图的应用迁移到
移动开发示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考