需求
最近我需要将蓝牙iBeacon上的数据获取出来,然后将对应的JavaScript所需接口注入到我们应用内的浏览器里,便于合作方在webview页面上调用。Javascript的接口具体细节参考微信上摇一摇业务开发的H5页面获取设备信息 JS API。
实现方式
在Javascript中调用平台接口,一般可以采用COM或ActiveX组件的方式注册到系统中然后在Javascript中调用或者标准接口直接使用HTML5来实现。但我发现在Visual Studio 2017中UWP平台下没有看到生成COM或ActiveX组件的方式,参考了MSDN上这篇How to invoke Javascript (JS) at Native, and invoke native at JS in WebView of Universal Windows Platform (UWP),我们了解到需要先创建一个RuntimeComponent(universal windows)项目,生成.winmd文件,然后在UWP(universal windows)项目中引用这个.winmd文件,添加一个webview控件,然后通过AddWebAllowedObject绑定RuntimeComponent中提供的方法注入到webview中,供javascript调用。
开发环境:
- Windows 10
- Visual Studio 2017
- C#
具体步骤
创建RuntimeComponent项目
在Visual Studio 2017中新建项目,选择“Windows运行时组件(通用Windows)”,用于为通用Windows平台UWP应用创建托管Windows运行时组件.winmd。
AllowForWeb
项目中类SHRFIDBeaconService必须为sealed,并且必须要引用[Windows.Foundation.Metadata]中的[AllowForWeb]属性。定义方法GetPlusResult和GetVersion。
//This project type is RuntimeComponent(Universal Windows)
//Three times for important thing,^_^
namespace SHRFIDBeaconRuntimeComponent
{
//this class must be sealed, and must have attribuite [Windows.Foundation.Metadata.AllowForWeb]
[AllowForWeb]
public sealed class SHRFIDBeaconService
{
private static Logger logger = LogManager.GetLogger("SHRFIDBeaconService");
private static bool _restartingBeaconWatch;
private static string _errBleMessage;
private readonly WindowsBluetoothPacketProvider _provider;
private BeaconManager _beaconManager;
public SHRFIDBeaconService()
{
// Construct the Universal Bluetooth Beacon manager
_provider = new WindowsBluetoothPacketProvider();
_beaconManager = new BeaconManager(_provider);
// Subscribe to status change events of the provider
_provider.WatcherStopped += WatcherOnStopped;
_beaconManager.BeaconAdded += BeaconManagerOnBeaconAdded;
//
......
public int GetPlusResult(int param1, int param2)
{
return param1 + param2;
}
public string GetVersion()
{
return "1.0.0.0";
}
......
创建UWP项目
创建空白UWP项目
WPF中webveiw
空白页面中添加webview,source设置为测试页面
<!-- Source file is on the web. >>>> http://www.microsoft.com-->
<!-- Source file is in local storage. >>>> ms-appdata:///local/intro/welcome.html-->
<!-- Source file is in the app package. >>>> ms-appx-web:///InvokeNativeCode/InvokeNativeCode.html-->
<!-- WebView Grid.Row="1" Name="MainWebView" NavigationStarting="MainWebView_NavigationStarting" Source="http://www.microsoft.com" /-->
<WebView Grid.Row="1" Name="MainWebView" NavigationStarting="MainWebView_NavigationStarting" Source="ms-appx-web:///InvokeNativeCode/InvokeNativeCode.html" />
WPF页面实现MainWebView_NavigationStarting方法
WPF页面,添加MainWebView_NavigationStarting实现,使用AddWebAllowedObject在页面载入时将Runtimecomponent中的服务SHRFIDBeaconService注入到webview中。
namespace SHRFIDBeaconServiceTest
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
//when start Navigation, Add Native object to webview,and then the object can be invoke in html.
private void MainWebView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
//This way can invoke native method and get result from native method.
//But because this native is from orther project in this solution, so when you operator UI, it will be very difficult.
sender.AddWebAllowedObject("SHRFIDBeaconService", new SHRFIDBeaconRuntimeComponent.SHRFIDBeaconService());
}
HTML测试页面
测试页面中引用SHRFIDBeaconService各种方法实现,例如SHRFIDBeaconService.GetPlusResult(),SHRFIDBeaconService.GetVersion()等。
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>SHRFIDBeaconServiceTest</title>
</head>
<body style="background:#007acc">
<h1>Invoke native code in WebView</h1>
<h2>Blue region is WebView</h2>
<h3 id="ifName"></h3>
<h3 id="resultContainer"></h3>
<br />
<br />
<br />
<div>
<button id="btn1" width="200" onclick="GetPlusResult()">GetPlusResult</button>
<button id="btn2" onclick="GetVersion()">GetVersion</button>
</div>
<br />
<div>
<button id="btn4" onclick="StartSearchBeacons()">StartSearchBeacons</button>
<button id="btn6" onclick="OnSearchBeacons()">OnSearchBeacons</button>
<button id="btn5" onclick="StopSearchBeacons()">StopSearchBeacons</button>
</div>
<br />
<div>
</div>
<script>
window.onload = function () {
//Notice that The Native method name is GetPlusResult, but in js it's name is getPlusResult, the first letter had to to lower.
var a = 117;
var b = 140;
var result = SHRFIDBeaconService.getPlusResult(a, b);
document.getElementById("ifName").innerHTML = "getPlusResult: ";
document.getElementById("resultContainer").innerHTML = a + "+" + b + "= " + result;
}
function GetPlusResult() {
var a = 123;
var b = 234;
var sum = SHRFIDBeaconService.getPlusResult(a, b);
document.getElementById("ifName").innerHTML = "getPlusResult: ";
document.getElementById("resultContainer").innerHTML = a + "+" + b + "= " + sum;
}
function GetVersion() {
var version = SHRFIDBeaconService.getVersion();
document.getElementById("ifName").innerHTML = "getVersion: ";
document.getElementById("resultContainer").innerHTML = version;
}
function StartSearchBeacons() {
var startb = SHRFIDBeaconService.startSearchBeacons();
document.getElementById("ifName").innerHTML = "startSearchBeacons: ";
document.getElementById("resultContainer").innerHTML = startb;
}
function StopSearchBeacons() {
var stopb = SHRFIDBeaconService.stopSearchBeacons();
document.getElementById("ifName").innerHTML = "stopSearchBeacons: ";
document.getElementById("resultContainer").innerHTML = stopb;
}
function OnSearchBeacons() {
var searchb = SHRFIDBeaconService.onSearchBeacons();
document.getElementById("ifName").innerHTML = "onSearchBeacons: ";
document.getElementById("resultContainer").innerHTML = searchb;
}
</script>
</body>
</html>
实际测试
生成,部署,启动……