技术架构

HTML5+ 基座扩展采用三层结构,JS层、PluginBridge层和Native层。 三层功能分别是:

JS层: 在Webview页面调用,触发Native层代码,获取执行结果。

PluginBridge层: 将JS层请求进行处理,触发Native层扩展插件代码。

Native层: 插件扩展的平台原生代码,负责执行业务逻辑并执行结果返回到请求页面。

技术架构 - 图1

开发者在开发扩展插件时需要为扩展插件编写对应的JS API,JS API将在HTML页面中触发调用对应Native扩展方法的请求,并负责接收运行结果。

开发者在编写扩展插件的JS时需要调用Javascript Plugin Bridge的API用来完成对Native层代码的调用和运行结果的返回。在实际应用中,开发者可以根据扩展方法的实际需求不同,将插件的扩展方法设置为同步执行或异步执行。

同步执行的扩展方法会阻塞当前JS代码的执行,直到Native层插件扩展方法执行完毕。异步扩展方法不会阻塞当前JS代码的执行,使用者需要设置回调方法接收Native层返回的执行结果,开发者需要在插件中调用 Native plugin brigde的方法将执行结果返回到请求页面。

工程示例请参考SDK内包含的HBuilder-Integrate工程,工程里已经整合了插件开发和集成方式的示例。

扩展插件工作流程

异步方法的执行不会阻塞当前JS的运行,方法的执行结果会通过回调的方式返回。

插件异步扩展方法:

技术架构 - 图2

同步方法的执行会阻塞当前JS的运行,方法执行完成后可直接获取执行的结果。

插件同步扩展方法:

技术架构 - 图3

扩展插件JS API 编写

开发者在实现JS层API时首先要定义一个插件类的别名,并需要在Android工程的assets\data\properties.xml文件中声明插件类别名和Native层扩展插件类的对应关系

  1. <properties>
  2. <features>
  3. <feature name="plugintest" value="com.example.H5PlusPlugin.PGPlugintest"></feature>
  4. </properties>

示例中为方便查看删除了其他插件的代码,开发者在可根据5+应用使用插件的情况进行增删修改。

Feature节点下声明的插件将会在调用时创建相应的对象。

JS扩展方法的实现

同步返回JS扩展方法实现

在实现同步扩展方法时,开发者需要调用JS Plugin Bridge的window.plus.bridge.execSync() 方法,该方法可同步获取Native插件返回的运行结果。

  1. void plus.bridge.execSync( String service, String action, Array<String> args );

service: 插件类别名

action: 调用Native层插件方法名称。

args: 参数列表。

异步返回JS扩展方法实现

在实现异步扩展方法时,开发者需要调用JS Plugin Bridge的plus.bridge.exec()方法,该方法会通知Native层插件执行指定方法,运行结果会通过回调的方式通知JS层

  1. void plus.bridge.exec( String service, String action, Array<String> args );

service: 插件类别名

action: 调用Native层插件方法名称。

args: 参数列表。

扩展插件JS API代码示例:

  1. ! function(root, factory) {
  2. if (typeof exports == 'object' && typeof module != 'undefined') {
  3. module.exports = factory()
  4. } else if (typeof define == 'function' && define.amd) {
  5. define(factory)
  6. } else {
  7. /**
  8. * 插件对象直接扩展到 window 对象上
  9. * 这个对象的名字,需要自己填写一下。例如:plugintest
  10. */
  11. var moduleName = 'plugintest';
  12. root[moduleName] = factory()
  13. }
  14. }(this, function() {
  15. var _BARCODE = 'plugintest';
  16. var plugintest = {
  17. PluginTestFunction: function(Argus1, Argus2, Argus3, Argus4, successCallback, errorCallback) {
  18. var success = typeof successCallback !== 'function' ? null : function(args) {
  19. successCallback(args);
  20. },
  21. fail = typeof errorCallback !== 'function' ? null : function(code) {
  22. errorCallback(code);
  23. };
  24. callbackID = plus.bridge.callbackId(success, fail);
  25. return plus.bridge.exec(_BARCODE, "PluginTestFunction", [callbackID, Argus1, Argus2, Argus3, Argus4]);
  26. },
  27. PluginTestFunctionArrayArgu: function(Argus, successCallback, errorCallback) {
  28. var success = typeof successCallback !== 'function' ? null : function(args) {
  29. successCallback(args);
  30. },
  31. fail = typeof errorCallback !== 'function' ? null : function(code) {
  32. errorCallback(code);
  33. };
  34. callbackID = plus.bridge.callbackId(success, fail);
  35. return plus.bridge.exec(_BARCODE, "PluginTestFunctionArrayArgu", [callbackID, Argus]);
  36. },
  37. PluginTestFunctionSync: function(Argus1, Argus2, Argus3, Argus4) {
  38. return plus.bridge.execSync(_BARCODE, "PluginTestFunctionSync", [Argus1, Argus2, Argus3, Argus4]);
  39. },
  40. PluginTestFunctionSyncArrayArgu: function(Argus) {
  41. return plus.bridge.execSync(_BARCODE, "PluginTestFunctionSyncArrayArgu", [Argus]);
  42. }
  43. };
  44. return plugintest;
  45. });

Html使用示例

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
  6. <meta name="HandheldFriendly" content="true"/>
  7. <meta name="MobileOptimized" content="320"/>
  8. <title>H5Plugin</title>
  9. <script type="text/javascript" src="./js/common.js"></script>
  10. <script type="text/javascript" src="./js/test.js"></script>
  11. <script type="text/javascript">
  12. function pluginShow() {
  13. plus.plugintest.PluginTestFunction("Html5","Plus","AsyncFunction","MultiArgument!", function( result ) {alert( result[0] + "_" + result[1] + "_" + result[2] + "_" + result[3] );},function(result){alert(result)});
  14. }
  15. function pluginShowArrayArgu() {
  16. plus.plugintest.PluginTestFunctionArrayArgu( ["Html5","Plus","AsyncFunction","ArrayArgument!"], function( result ) {alert( result );},function(result){alert(result)});
  17. }
  18. function pluginGetString()
  19. {
  20. alert(plus.plugintest.PluginTestFunctionSync("Html5","Plus","SyncFunction","MultiArgument!"));
  21. }
  22. function pluginGetStringArrayArgu()
  23. {
  24. var Argus = plus.plugintest.PluginTestFunctionSyncArrayArgu(["Html5","Plus","SyncFunction","ArrayArgument!"]);
  25. alert( Argus.RetArgu1 + "_" + Argus.RetArgu2 + "_" + Argus.RetArgu3 + "_" + Argus.RetArgu4 );
  26. }
  27. </script>
  28. <link rel="stylesheet" href="./css/common.css" type="text/css" charset="utf-8"/>
  29. </head>
  30. <body>
  31. <header>
  32. <div class="nvbt" onclick="back();"><div class="iback"></div></div>
  33. <div class="nvtt">PluginTest</div>
  34. </header>
  35. <div id="dcontent" class="dcontent">
  36. <br/>
  37. <div class="button" onclick="pluginShow()">PluginTestFunction()</div>
  38. <div class="button" onclick="pluginShowArrayArgu()">PluginTestFunctionArrayArgu()</div>
  39. <div class="button" onclick="pluginGetString()">PluginTestFunctionSync()</div>
  40. <div class="button" onclick="pluginGetStringArrayArgu()">PluginTestFunctionSyncArrayArgu()</div>
  41. <br/>
  42. </div>
  43. </body>
  44. </html>

uni-app 使用示例

uni-app 中无需主动监听 plusready,可以直接调用 plus 相关的 API。

  1. <template>
  2. <view>
  3. <button @click="pluginShow">pluginShow</button>
  4. </view>
  5. </template>
  6. <script>
  7. // 扩展的 js 文件的位置:common/plugins.js
  8. var plugins = require('../../common/plugins.js')
  9. export default {
  10. data() {
  11. return {
  12. plugins: plugins
  13. };
  14. },
  15. methods: {
  16. pluginShow() {
  17. this.plugins.PluginTestFunction("Html5", "Plus", "AsyncFunction", "MultiArgument!", function(result) {
  18. console.log(result[0] + "_" + result[1] + "_" + result[2] + "_" + result[3]);
  19. }, function(result) {
  20. console.log(result)
  21. });
  22. }
  23. }
  24. }
  25. </script>
  26. <style>
  27. </style>

Android第三方插件开发指导

准备

首先要下载HTML5+基座的Android版SDK点击下载解压后将HBuilder-Integrate工程导入到ADT中。

uni-app插件开发

android平台:uni-app插件开发

接口实现方式

创建插件类

创建一个继承自StandardFeature的类,实现第三方插件扩展。

创建插件类需要引入的包

import io.dcloud.DHInterface.IWebview;

import io.dcloud.DHInterface.StandardFeature;

import io.dcloud.util.JSUtil;

实现扩展方法

Native层扩展插件的方法名需要和JS Plugin Bridge里windows.plus.bridge.exec()或windows.plus.bridge.execSync()方法的第二个传入参数相同,否则无法调用到指定的方法。

  1. public void PluginTestFunction(IWebview pWebview, JSONArray array)

扩展方法有两个传入参数

IWebview pWebview 发起请求的webview,

JSONArray array JS请求传入的参数

开发者在实现同步和异步接口运行结果返回时调用的API是不同的

同步执行方法:

同步执行方法在返回结果时可以直接将结果以return的形式返回给js层,返回的结果需要调用

  1. JSUtil.wrapJsVar("Html5 Plus Plugin Hello1!",true);

处理要返回的字符串,也可调用其他方法处理其他返回值类型(参考io.dclod.util.JSUtil的返回方法)。

异步执行方法:

异步方法开发者需要调用

  1. JSUtil.execCallback(pWebview, cbId, (which==AlertDialog.BUTTON_POSITIVE)?"ok":"cancel", JSUtil.OK, false, false);

代码示例

  1. package com.example.H5PlusPlugin;
  2. import io.dcloud.common.DHInterface.IWebview;
  3. import io.dcloud.common.DHInterface.StandardFeature;
  4. import io.dcloud.common.util.JSUtil;
  5. import org.json.JSONArray;
  6. import org.json.JSONException;
  7. import org.json.JSONObject;
  8. public class PGPlugintest extends StandardFeature
  9. {
  10. public void PluginTestFunction(IWebview pWebview, JSONArray array)
  11. {
  12. String CallBackID = array.optString(0);
  13. JSONArray newArray = new JSONArray();
  14. newArray.put(array.optString(1));
  15. newArray.put(array.optString(2));
  16. newArray.put(array.optString(3));
  17. newArray.put(array.optString(4));
  18. JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);
  19. }
  20. public void PluginTestFunctionArrayArgu(IWebview pWebview, JSONArray array)
  21. {
  22. String ReturnString = null;
  23. String CallBackID = array.optString(0);
  24. JSONArray newArray = null;
  25. try {
  26. newArray = new JSONArray( array.optString(1));
  27. String inValue1 = newArray.getString(0);
  28. String inValue2 = newArray.getString(1);
  29. String inValue3 = newArray.getString(2);
  30. String inValue4 = newArray.getString(3);
  31. ReturnString = inValue1 + "-" + inValue2 + "-" + inValue3 + "-" + inValue4;
  32. } catch (JSONException e) {
  33. // TODO Auto-generated catch block
  34. e.printStackTrace();
  35. }
  36. JSUtil.execCallback(pWebview, CallBackID, ReturnString, JSUtil.OK, false);
  37. }
  38. public String PluginTestFunctionSyncArrayArgu(IWebview pWebview, JSONArray array)
  39. {
  40. JSONArray newArray = null;
  41. JSONObject retJSONObj = null;
  42. try {
  43. newArray = array.optJSONArray(0);
  44. String inValue1 = newArray.getString(0);
  45. String inValue2 = newArray.getString(1);
  46. String inValue3 = newArray.getString(2);
  47. String inValue4 = newArray.getString(3);
  48. retJSONObj = new JSONObject();
  49. retJSONObj.putOpt("RetArgu1", inValue1);
  50. retJSONObj.putOpt("RetArgu2", inValue2);
  51. retJSONObj.putOpt("RetArgu3", inValue3);
  52. retJSONObj.putOpt("RetArgu4", inValue4);
  53. } catch (JSONException e1) {
  54. // TODO Auto-generated catch block
  55. e1.printStackTrace();
  56. }
  57. return JSUtil.wrapJsVar(retJSONObj);
  58. }
  59. public String PluginTestFunctionSync(IWebview pWebview, JSONArray array)
  60. {
  61. String inValue1 = array.optString(0);
  62. String inValue2 = array.optString(1);
  63. String inValue3 = array.optString(2);
  64. String inValue4 = array.optString(3);
  65. String ReturnValue = inValue1 + "-" + inValue2 + "-" + inValue3 + "-" + inValue4;
  66. return JSUtil.wrapJsVar(ReturnValue,true);
  67. }
  68. }

三 关联JS插件名和原生类

在编写扩展插件时需要修改“/assets/data”中properties.xml文件,在其中添加JS对象名称和Android包的类名对应关系,SDK会根据对应的类名查找并生成相应的对象并执行对应的逻辑。

技术架构 - 图4

在应用的manifest.json文件中还需要添加扩展插件的应用使用权限

技术架构 - 图5

Android平台插件开发API

  • io.dcloud.util.JSUtil

    常用方法说明:

    1. String wrapJsVar(String value, boolean isString);

    转换JS层的返回值,也用于异步接口中回调函数的参数。

    参数说明:

    value: 要返回到JS层的值

    isString:返回值类型是否为原始字符串

    返回方法:

    boolea类型: wrapJSVar( “true”, false );

    Number类型: wrapJsVar( “99”, false );

    String类型: wrapJsVar( “hello”, true );

    数组类型: wrapJsVar( “[1,2,3,4,5]”, false );

    JSON类型: wrapJsVar( “{‘name’:’value’}”, false );

    1. String execCallback(IWebview pWebView, String pCallbackId, String pMessage, int pStatus, boolean isJson, boolean pKeepCallback);

    触发扩展插件中的回调方法。

    参数说明:

    pWebView:扩展插件方法运行的窗口

    pCallbackId:回调函数的唯一标识

    pMessage:回调函数的参数

    pStatus:操作是否成功,成功则使用JSUtil.OK,否则使用错误代码

    isJson:回调函数参数是否为JSON数据

    pKeepCallback:是否可多次触发回调函数

术语字典

JS Plugin Bridge: H5+ Plugin Bridge层JS部分API,用户通过调用相应的API触发Native层扩展插件相应方法的调用。

Native Plugin Bridge: H5+ Plugin Bridge层Native部分API,开发者通过实现接口类方法,实现由JS层触发Native层扩展插件逻辑。开发者调用API实现Native扩展方法运行结果得返回。

Native层插件:开发者使用原生语言实现的5+扩展插件,可被JS层通知调用。

插件类别名:JS层字符串,用来声明JS方法和Native层插件的对应关系