混合手机应用是捆绑了浏览器的网页应用,可以在任何手机平台(Android, iOS, Windows等)上运行。混合应用的最大优势在于它拥有单一的代码基础,而原生应用则必须针对每个平台重新编写。
Apache科尔多瓦是一个用于开发混合应用程序的开源框架。要使用本机功能(除了通过hmtl5提供的功能外),应用程序可以使用插件。这些插件处理特定于平台的代码,可用于单个或多个平台。例如,插件可用于访问GPS、加速计或摄像头。
SAP已经编写了他们自己的插件,命名为Kapsel,以处理登录和离线OData之类的事情。这给混合应用带来了企业风格。
直到最近,SAP推荐的方法是使用混合应用程序工具包(HAT)来构建混合应用程序。应用程序是在开发者的PC或Mac上构建的。HAT因难以设置而获得了声誉,这阻碍了SAP生态系统中混合应用的使用。
Fiori Mobile的核心是云构建服务,不需要安装HAT。我们可以从Web IDE触发构建。我们指定(已经部署的)Fiori应用程序,我们想打包,几分钟后,我们可以下载准备安装在移动设备上的。apk (Android)和。ipa (iOS)文件。
云构建服务运行良好。它通常在8分钟内构建两个平台,不会出现问题。只有几次由于技术问题导致构建失败。
这项技术的一些方面还不成熟。一个领域是离线OData特性。当使用Fiori Mobile时,东西的工作方式与更传统的混合和本地应用程序不同。一个例子就是目的地,因为Fiori Mobile应用使用的是引用SAP云平台(CP)门户服务的生成目的地。
我的观点并不是说离线的OData看起来不成熟,而是说在使用Fiori Mobile应用时,离线特性可能很棘手。我们仍在接受SAP的帮助,以优化我们的应用程序的离线功能。这是一件令人遗憾的事情,因为共享数据和处理增量请求是SAP采用的离线“中间件”方法的两个特点。
身份验证也是一个挑战。在之前的项目中,我使用了菲奥里客户与SAP云身份(现在正式)结合SAP云平台身份认证).Fiori客户端将用户名和密码存储在设备上,这样用户就不需要在每次会话超时时输入它们。我们的Fiori Mobile应用还没有做到这一点。
要创建Fiori Application,我们需要创建并发布将部署应用程序的Site。
去门户服务并单击Go to Service。
在您的浏览器将打开一个新标签。在这个新窗口中,转到网站目录选项卡,然后单击创建站点。
将显示一个弹出窗口。输入您的网站的名字,选择SAP菲奥里发射台,单击“创建”
接下来,你将被重定向到SAP Fiori配置驾驶舱。关闭选项卡并返回Portal服务的管理空间。
现在我们需要发布此站点并将其设置为默认值。
一旦我们创建并发布站点,我们就可以创建Fiori项目了。
再次转到服务选项卡,打开SAP Web IDE服务,然后单击转到服务
将打开一个新标签。从这里,我们将开始创建我们的Fiori项目。
在SAP Web IDE工作区中,我们可以看到Fiori项目包含的所有文件。
开放Component.js文件。
Component.js是我们应用程序的第一点,我们可以说它作为索引,封装了我们应用程序的所有细节,即视图名称,路由细节,主视图,应用程序类型(全屏或SplitApp),应用程序服务配置等。
在这里初始化函数,我们将配置插件。设置我们需要的属性,设置回调函数,调用一些方法,等等。
定义([“sap/ui/core/UIComponent”、“sap/ui/Device”、“MyFristFioriApp/model/models”]、函数(UIComponent、Device、models){“严格使用”;var oEventBus=sap.ui.getCore().getEventBus();var scannerIsInitialized=false;返回UIComponent.extend(“MyFristFioriApp.Component”,{元数据:{manifest:{manifest:“json”}),//***组件在应用程序启动期间由UI5自动初始化,并调用init方法一次。*@public*@override*/init:function(){//调用基础组件的init函数UIComponent.prototype.init.apply(this,arguments);//将设备模型设置为this.setModel(models.createDeviceModel(),“device”);如果(!scannerIsInitialized){scannerIsInitialized=true;window.readerConnected=0;window.scannerActive=false;cmbScanner.addOnResume(函数(结果){cmbScanner.setAvailabilityCallback((readerAvailability)=>{if(readerAvailability==cmbScanner.CONSTANTS.AVAILABILITY){oEventBus.publish(“ScanView”,“SetConnectionStatus”,{statusText:“可用”});cmbScanner.connect((结果)=>{};}else{oEventBus.publish(“扫描视图”,“设置连接状态”,{statusText:“不可用”};}}});if(Device.os.android){cmbScanner.connect((结果)=>{};});cmbScanner.addOnPause(函数(结果){if(Device.os.android){cmbScanner.disconnectcmbScanner.setAvailabilityCallback();});cmbScanner.setPreviewContainerPositionAndSize(0,0,100,50);cmbScanner.setConnectionStateDidChangeOfReaderCallback((connectionState)=>{if(connectionState==cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTED){oEventBus.publish(“扫描视图”,“SetConnectionStatus”,“状态文本:{Status:“CONNECTED”});如果(window.readerConnected!=connectionState){cmbScanner.setSymbologyEnabled(“SYMBOL.DATAMATRIX”,true);cmbScanner.setSymbologyEnabled(“SYMBOL.C128”,true);cmbScanner.sendCommand(“SET TRIGGER.TYPE 2”);}var sdkKEY=“”;if(Device.os.android)sdkKEY=this.getModel(“i18n”).getProperty”(“MX_MOBILE_LICENSE_android”);els sdkKEY=this.getModel(“i18n”).getProperty(“MX_MOBILE_LICENSE_iOS”);cmbScanner.registerSDK(sdkKEY,(res)=>{switch(res){case cmbScanner.CONSTANTS.REGISTER_RESULTS.REGISTER_OK:中断;case cmbScanner.CONSTANTS.REGISTER_RESULTS.REGISTER_无效_键:中断;case cmbScanner.CONSTANTS.REGISTER_RESU无效_应用程序:中断;case cmbScanner.CONSTANTS.REGISTER_RESULTS.REGISTRATION\u INVALID\u SDK\u VERSION:break;case cmbScanner.CONSTANTS.REGISTER\u RESULTS.REGISTRATION\u INVALID\u KEY\u VERSION:break;case cmbScanner.CONSTANTS.REGISTER\u RESULTS.REGISTER\u KEY\u过期:break;默认值:break;});}否则如果(connectionState==cmbScanner.CONSTANTS.CONNECTION_STATE_connected){oEventBus.publish(“扫描视图”,“设置连接状态”,“断开连接”})}如果(connectionState==cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTING){oEventBus.publish(“扫描视图”,“设置连接状态”,“状态文本:”连接”})}如果(connectionState==cmbScanner.CONSTANTS.CONNECTION\u STATE\u DISCONNECTING){oEventBus.publish(“扫描视图”、“设置连接状态”、“断开连接”);}window.readerConnected=connectionState;});cmbScanner.setAvailabilityCallback((readerAvailability)=>{if(readerAvailability==cmbScanner.CONSTANTS.AVAILABILITY\u可用){oEventBus.publish(“ScanView”、“SetConnectionStatus”、{statusText:“AVAILABLE”});cmbScanner.connect((结果)=>{});else{oEventBus.publish(“ScanView”、“SetConnectionStatus”、{statusText:“NotAvailable”});cmbScanner.setActiveStartScanningCallback((结果)=>{window.Neractive=result;});cmbScanner.setPreviewOptions(cmbScanner.CONSTANTS.PREVIEW_OPTIONS.DEFAULTS | cmbScanner.CONSTANTS.PREVIEW_OPTIONS.HARDWARE_触发器);cmbScanner.setCameraMode(cmbScanner.CONSTANTS.CAMERA_MODES.NO_AIMER);cmbScanner.loadScanner(0,(result)=>{cmbScanner.connect((result)=>{});oEventBus.subscribe(“组件”,“loadScanner”,this.loadScanner,this)},loadScanner:function(sChanel,sEvent,oData){cmbScanner.disconnect((结果)=>{cmbScanner.loadScanner(oData.selectedDevice,(结果)=>{cmbScanner.connect((结果)=>{};});}),onExit:function(){oEventBus.unsubscribe(“组件”,“loadScanner”,this.loadScanner,this);cmbScanner.disconnect();cmbScanner.setAvailabilityCallback(); } }); });
cmbScanner是一个代表插件的对象。通过这个对象,我们可以访问所有的对象API方法和常量从我们的插件。
这里我们只配置读卡器设备、处理连接、可用性等。我们将在其他视图中进行扫描并获取结果。如果我们想通知用户每个连接状态的更改或我们将使用的读卡器设备的其他信息EventBussap对象。通过这个对象,我们可以发布函数,并从应用程序的任何视图中调用它们。在这个例子中,我们调用的EventBus改变了连接状态SetConnectionStatus在视图中执行扫描并设置标签文本以显示用户当前连接状态的函数。
现在打开ScanView.view.xml并添加以下代码:
< mvc视图xmlns: mvc = " sap.ui.core。mvc“xmlns: html = " http://www.w3.org/1999/xhtml " xmlns:核心= " sap.ui。核心”controllerName = " MyFristFioriApp.controller。ScanView" displayBlock="true">
您可以使用CodeEditor或LayoutEditor设计视图。
在那之后ScanView.controller.js然后添加以下代码
sap.ui.define(["sap/ui/core/mvc/Controller"], function(Controller) {"use strict";var oEventBus = sap.ui.getCore().getEventBus();var model = new sap.ui.model.json ();oModel。setData({Devices: [{key: "0", text: "MX Device"}, {key: "1", text: "Mobile Camera"}]});返回Controller.extend(“MyFristFioriApp.controller。ScanView", {onInit: function() {this.getView().setModel(model);}, onAfterRendering: function(){窗口。scannerActive = false;cmbScanner.CONSTANTS. switch (window.readerConnected) {case cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTED: this.getView () .byId(“lblStatus”). settext(“连接”); break; case cmbScanner.CONSTANTS.CONNECTION_STATE_DISCONNECTED: this.getView().byId("lblStatus").setText("DISCONNECTED"); break; case cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTING: this.getView().byId("lblStatus").setText("CONNECTING"); break; case cmbScanner.CONSTANTS.CONNECTION_STATE_DISCONNECTING: this.getView().byId("lblStatus").setText("DISCONNECTING"); break default: this.getView().byId("lblStatus").setText("UNKNOWN"); break; } oEventBus.subscribe("ScanView", "SetConnectionStatus", this.setConnectionStatus, this); cmbScanner.setResultCallback((result) => { if (result && result.readString) { var verticalLayoutContainer = new sap.ui.layout.VerticalLayout(null, { width: "100%" }).addStyleClass("sapUiSmallMarginTop"); verticalLayoutContainer.addContent(new sap.m.Label({ text: result.symbologyString + ":", textAlign: "Begin", design: "Bold" }).addStyleClass("sapUiSmallMarginBegin")); verticalLayoutContainer.addContent(new sap.m.Text({ text: result.readString, textAlign: "Begin" }).addStyleClass("sapUiSmallMarginBegin")); this.getView().byId("flexBoxContainer").addItem(verticalLayoutContainer); } }); }, btnScanPress: function() { if (window.readerConnected === cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTED) { if (window.scannerActive === true) { cmbScanner.stopScanning(); } else { cmbScanner.startScanning(); } } }, setConnectionStatus: function(sChanel, sEvent, oData) { this.getView().byId("lblStatus").setText(oData.statusText); }, activeDeviceChanged: function() { cmbScanner.disconnect((result) => { oEventBus.publish("Component", "LoadScanner", { selectedDevice: parseInt(this.getView().byId("selectActiveDevice").getSelectedKey()) }); }); }, onExit: function() { oEventBus.unsubscribe("ScanView", "SetConnectionStatus", this.setConnectionStatus, this); if (window.readerConnected === cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTED) { if (window.scannerActive === true) { cmbScanner.stopScanning(); } } cmbScanner.setResultCallback((result) => { return false; }); } }); });
在这个视图中,我们有一个Scan按钮开始扫描()/停止扫描()和cmbScanner.setResultCallback处理成功的扫描结果。
在我们完成创建和开发我们的Fiori项目后,我们需要准备移动部署。
首先我们需要部署我们的项目SAP HANA云平台.
一个
如果你打算使用cmbSDK要使用智能手机或平板电脑(没有MX移动终端)进行移动扫描,SDK需要安装许可密钥。没有许可密钥,SDK仍然可以运行,但扫描结果会被模糊化(SDK会用星号字符随机替换扫描结果中的字符)。
请与您的康耐视销售代表联系,了解如何获取许可密钥,包括可用于评估SDK 30天的试用许可。
获得许可密钥后,打开i18n。属性文件,并设置获得的密钥。
然后回到Component.js文件并检查以下代码:
cmbScanner.setConnectionStateDidChangeOfReaderCallback((connectionState)=>{if(connectionState==cmbScanner.CONSTANTS.CONNECTION_STATE_CONNECTED){oEventBus.publish(“ScanView”,“SetConnectionStatus”,“status:”CONNECTED“});if(window.readerConnected!=connectionState){cmbScanner.setSymbologyEnabled(“SYMBOL.DATAMATRIX”,true);cmbScanner.setSymbologyEnabled(“SYMBOL.C128”,true);cmbScanner.sendCommand(“SET TRIGGER.TYPE 2”);var sdkKEY=“”;if(Device.os.android)sdkKEY=this.getModel(“i18n”).getProperty(“MX_MOBILE_LICENSE_android”);else sdkKEY=this.getModel(“i18n”).getProperty”(“MX_MOBILE_LICENSE_iOS”);cmbScanner.registersdkk(res)=>(res)开关(res){case cmbScanner.CONSTANTS.REGISTER_RESULTS.REGISTER_OK:中断;case cmbScanner.CONSTANTS.REGISTER_RESULTS.REGISTER_无效_键:中断;case cmbScanner.CONSTANTS.REGISTER_RESU无效_应用程序:中断;case cmbScanner.CONSTANTS.REGISTER_RESULTS.REGISTRATION\u INVALID\u SDK\u VERSION:break;case cmbScanner.CONSTANTS.REGISTER\u RESULTS.REGISTRATION\u INVALID\u KEY\u VERSION:break;case cmbScanner.CONSTANTS.REGISTER\u RESULTS.REGISTER\u KEY\u EXPIRED:break;默认值:break;})。。。。。。。。。。。
您可以看到,在成功连接之后,您从i18n读取了这个键。属性和调用cmbScanner.registerSDK方法以使用许可密钥注册SDK。
检查在回调函数中返回的结果对象可以告诉我们注册是否成功或问题是什么。