§ Fox2.0 插件开发指南


§ 介绍

移动平台使用osgi机制管理插件,以module为功能单位,每个moduel可包含三类文件,分别为boot插件、plugin插件和配置文件,其中boot插件是在平台启动和关闭是调用,如版本更新,缓存清理等,而plugin类型插件用于给javascript提供native能力,如拍照,文件上传下载等,而配置文件则是插件的注册信息。如下图

module

关于module的命名规范: 1.第三方的外设集成,我们的module的命名一般为device_xxx,例如 集成cfca的签名功能,我们就应该创建模块device_cfca。 2.pdf、图片浏览、im、崩溃日志记录等非设备访问的代码要独立建立插件,插件名规范如下core_ext_xxx, 如core_ext_pdf。 3.原则上尽量一个插件代表一个功能,这样有利于后面我们根据需求进行裁剪。

§ Boot插件开发

主要是用于APP启动过程中调用,我们一般用于做一些初始化工作,如过期文件清理,版本更新等。

§ Java 代码

    package fox.core.ext.demo;
    import android.util.Log;
    import fox.ninetales.FXBoot;
    import fox.ninetales.FXInterface;
    import fox.ninetales.FXProgressContext;
    /**
    * Fox启动插件
    * start在Activity onCreate方法中调用,当所有的boot模块都done完成后,才
    * 加web页面
    * stop在Activity destroy方法中调用,在所以的FXPlugin的destroy方法调用
    * 后才调
    *
    * create by 江成
    */
    public class DemoBoot extends FXBoot {
        /**
         * 启动
         * activity onCreate方法中调用,在所有boot模块done后启动web view
         * 任务完成后必须调用done或cancel方法
         *
         * @param fxInterface
         * @param context
         * @return
         */
        public void start(FXInterface fxInterface, FXProgressContext context){
            Log.i("demo","模块start");
            context.done(FXProgressContext.Status.SUCCESS);
        }
        /**
         * 关闭
         *
         * activity onDestroy方法中调用,在FXPlugin的onDestroy方法后面
         *
         *
         * @param fxInterface
         * @return
         */
        public void stop(FXInterface fxInterface){
            Log.i("demo","模块stop");
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

§ 配置文件

配置文件需要手动在模块所在的assets目录下,新建目录metadata,并在该文件夹下新建xml配置文件,文件名的规则fox_extesion_xxx,例如在模块core_ext_demo中的配置文件的名为fox_extension_core_ext_demo.xml。

    <?xml version="1.0" encoding="UTF-8"?>
    <plugins>
    <!--启动扩展-->
    <extension point="fox.extension.boot">
        <!--启动测试插件-->
        <boot name="DemoBoot" class="fox.core.ext.demo.DemoBoot" text="demo启动测试">
        </boot>
    </extension>
    </plugins>
1
2
3
4
5
6
7
8
9

§ Plugin插件

根据功能和范围插件主要分为FXPlugin,FXProxy,IDevice和INative几类,其中IDevice插件用于调用系统或第三方提供的外设模块,如相机、OCR、二代证等,而INative则用于集成除外设类之外的原生功能,如定位、网络等,而FXProxy则用于功能更为复杂的原生功能调用,FXPlugin功能最强但由于其复杂,也导致其比较少使用,一般用FXProxy代替。我们在原生开发的过程中选择优先顺序是INative&IDevice > FXProxy > FPlugin,也是我们根据功能分类优先选择INative或IDevice,当两个不合适的情况下才选择FXProy,而FXPlugin是最后的选择。下图是他们之间的关系

插件关系图

§ IDevice开发指南

IDevice插件用于集成外设调用,下面是例子

§ java

package fox.core.ext.demo;
import fox.core.ICallback;
import fox.core.plugins.device.IDevice;

/**
 * Demo OCR
 *
 * create by 江成
 */
public class DemoOCRDevice implements IDevice {

    /**
     * 外设调用
     *
     * @param type     设备类型,以配置文件中的type对应
     * @param action   动作
     * @param param    参数
     * @param callbackContext 回调函数
     */
    public void call(String type, String action, String param, ICallback callbackContext){
        //根据js传递过来的动作执行
        if("test".equals(action)){
            callbackContext.callback(ICallback.SUCCESS,"ok");
        }else{
            callbackContext.callback(ICallback.ERROR,"error");
        }
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

§ 配置

    <?xml version="1.0" encoding="UTF-8"?>
    <plugins>
    <!--外设模块扩展-->
    <extension point="fox.extension.device">
        <!--OCR测试-->
        <device class="fox.core.ext.demo.DemoOCRDevice" scope="singleton"
            type="ocr" typeName="OCR" devId="ocr-demo" devName="测试OCR外设"></device>
    </extension>
    </plugins>
1
2
3
4
5
6
7
8
9

配置说明

  • type:设备类型,js就是根据type来调用不同的外设的
  • typeName:设备类型的描述
  • devId:设备的ID必须唯一
  • devName:设备的描述

§ js调用

    //外设测试
    deviceTest:function(){
    //type:配置文件上的type保持一致, action, params, callback
    fox.device.call("ocr","test","外设测试",(code, message ,data)=>{
    //code 0:成功,1:取消,2:失败
    //调用成功后,返回数据在data中,如果失败了则message是失败消息
     if(code == 0){
        fox.layer.open(data);
     }else{
        fox.layer.open("调用失败,"+message);
     }
     });
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

参数说明

fox.device.call(type,action,params,callback)是外设调用的js接口,其参数为

  • type:外设类型和配置文件上的保持一致
  • action:动作,和与原生实现进行约定
  • params:参数
  • callack:回调函数

§ INative开发指南

IDevice插件用于集成简单的原生功能,下面是例子

§ java

    package fox.core.ext.demo;
    import fox.core.ICallback;
    import fox.core.plugins.natives.INative;
    /**
    * Network测试
    *
    * create by 江成
    */
    public class DemoNetworkNative implements INative {
    /**
     * 调用
     *
     * @param action    动作,配置文件&JS上的action必须一致
     * @param param     参数
     * @param callbackContext  回调函数
     */
    public void call(String action, String param, ICallback
    callbackContext){
        //根据动作执行
        if("wifiStatus".equals(action)){
            callbackContext.callback(ICallback.SUCCESS,"ok");
        }else{
            callbackContext.callback(ICallback.ERROR,"unknown action");
        }
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

####配置

    <?xml version="1.0" encoding="UTF-8"?>
    <plugins>
    <!--native接口模块扩展-->
    <extension point="fox.extension.native">
        <native action="wifiStatus" class="fox.core.ext.demo.DemoNetworkNative">
        </native>
    </extension>
    </plugins>
1
2
3
4
5
6
7
8

配置说明

fox.native.call(action,params,callback)是native调用的js接口,其参数为

  • action:动作,和与原生实现进行约定和配置文件保持一致
  • params:参数
  • callack:回调函数

§ js调用

    //native测试
     nativeTest:function(){
    // action:配置文件上的action保持一致, params, callback
    fox.native.call("wifiStatus","native测试",(code, message ,data)=>{
      //code 0:成功,1:取消,2:失败
      //调用成功后,返回数据在data中,如果失败了则message是失败消息
      if(code == 0){
        fox.layer.open(data);
      else{
        fox.layer.open("调用失败,"+message);
      }
      });
      }
1
2
3
4
5
6
7
8
9
10
11
12
13

§ FXProxy开发指南

一个插件的开发主要包括java、配置和js三部分

§ java

插件中暴露给JS调用的方法,必须加上annotation @JavascriptInterface

    package fox.core.ext.demo;
    import android.util.Log;
    import fox.ninetales.FXProxy;
    import fox.ninetales.engine.JavascriptInterface;
    
    /**
    * FXProxy测试demo,FXProy主要用于为
    * Web提供native能力
    *
    * create by 江成
    */
    public class DemoProxy extends FXProxy {

        /**
         * 插件创建时调用,由于Proxy初始化后只在APP关闭时才销毁,
         * 所以该方法只会调用一次
         */
        protected void pluginInitialize() {
            Log.i("demo" ,"插件初始化,仅会执行一次");
        }
        
        /**
         * 同步测试
         * @param message
         * @return
         */
        @JavascriptInterface
        public String syncFn(String message){
            Log.i("demo" ,"同步执行,"+message);
            return "back:"+message;
        }

        /**
         * 异步测试
         * @param message
         * @return
         */
        @JavascriptInterface
        public void asyncFn(String message){
            Log.i("demo" ,"异步执行,"+message);
    
            String retMsg = "back:"+message;
            // 成功回调
            this.success(retMsg);
            //失败回调
            //this.error(retMsg);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

§ 配置

    <?xml version="1.0" encoding="UTF-8"?>
    <plugins>
    <!--启动扩展-->
    <extension point="fox.extension.boot">
        <!--启动测试插件-->
        <boot name="demoBoot" class="fox.core.ext.demo.DemoBoot" text="demo启动测试">
        </boot>
    </extension>

    <!--插件扩展-->
    <extension point="fox.extension.plugin">
        <!--测试插件-->
        <plugin name="demoPlugin" class="fox.core.ext.demo.DemoProxy" text="测试插件">
        </plugin>
    </extension>
</plugins>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

§ js插件

    /**
    * Created by 江成
    */
    (function (fox, window, factory) {
    // 判断是否支持模块定义
    let hasDefine = (typeof define === 'function');
    if (hasDefine) {
        //获取对象
        let exports = factory(fox,window);
        //定义模块
        define(exports);
        //安装插件(兼容非模块的访问方式)
        window.fox.demo = exports;
        } else {
        //获取对象
        let exports = factory(fox,window);
        //安装插件
        window.fox.demo = exports;
    }
    }(fox, window, function (fox,window) {
    //定义Demo对象
    let demo={

        /**
         * 同步测试
         * @param message
         */
        syncFn:function(message) {
            //参数
            let args = {
                service: "demoPlugin", //对应FXProxy注册在配置文件上的服务名
                action: "syncFn", //对应FXProxy中标志了@JavascriptInterface的方法
                data: [message],  //对应方法中的参数
                async: false      //是否异步
            };
            return window.fxBridge.exec(args);
        },

        /**
         * 异步测试
         * @param message
         * @param callback
         */
        asyncFn:function(message,callback) {
            //参数
            let args = {
                service: "demoPlugin",//对应FXProxy注册在配置文件上的服务名
                action: "asyncFn",//对应FXProxy中标志了@JavascriptInterface的方法
                data: [message],  //对应方法中的参数
                async: true,      //是否异步
                callback:callback //异步回调函数
            };
            return window.fxBridge.exec(args);
        }

    };
    return demo;
    }));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

§ js调用

    //同步测试
    demoSyncTest:function(){
        let res = fox.demo.syncFn("同步测试");
        fox.layer.open(res);
    },
    //异步测试
    demoASyncTest:function(){
        fox.demo.asyncFn("异步测试",(status,data)=>{
             //status 0:成功,1:取消,2:失败
             if(status == '0'){
                 fox.layer.open(data);
             }else{
                 fox.layer.open("调用失败");
             }
       });
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
最后更新于: 4/15/2022, 2:41:22 PM