§ Fox3.0 插件开发指南(Android)

§ 介绍

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

关于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插件

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

§ IDevice开发指南

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

§ java代码


     /**
     * Demo OCR
     *
     * create by 江成
     */
    public class DemoOCRDevice implements IDevice {
    
        /**
         * 外设调用
         *
         * @param type     设备类型,以配置文件中的type对应
         * @param action   动作
         * @param param    参数
         * @param callbackContext 回调函数
         * @param cite   引用
         */
        public void call(String type, String action, String param, ICallback callbackContext, Cite cite){
            //根据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

参数说明

  • type:设备类型,以配置文件中的type对应
  • action:设备操作动作
  • callbackContext:回调上下文,通过callback(int code,Object data)函数通知web设备调用情况
  • cite:上下文引用,其方法如下
     /**
         * 获取App名称
         * @return
         */
        public String getAppName()
    
        /**
         * 获取App类型
         * @return
         */
        public FXAppType getAppType()
    
        /**
         * 获取Http requester
         * @return
         */
        public HttpRequester getHttpRequester()
        /**
         * 获取文件访问器
         * @return
         */
        public FileAccessor getFileAccessor()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

§ 配置

    <?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开发指南

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

§ java 代码

    package fox.core.ext.demo;
    
    import fox.core.Cite;
    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  回调函数
         * @param cite      引用
         */
        public void call(String action, String param, ICallback callbackContext, Cite cite){
            //根据动作执行
            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
27
28
29
30

参数说明

  • action:操作动作
  • param:参数
  • callbackContext:回调上下文,通过callback(int code,Object data)函数通知web设备调用情况
  • cite:上下文引用,其方法如下
     /**
         * 获取App名称
         * @return
         */
        public String getAppName()
    
        /**
         * 获取App类型
         * @return
         */
        public FXAppType getAppType()
    
        /**
         * 获取Http requester
         * @return
         */
        public HttpRequester getHttpRequester()
        /**
         * 获取文件访问器
         * @return
         */
        public FileAccessor getFileAccessor()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

§ 配置

    <?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.core.AProxy;
    import fox.ninetales.engine.JavascriptInterface;
    
    /**
    * AProxy测试demo,AProy主要用于为
    * Web提供native能力
    *
    * create by 江成
    */
    public class DemoProxy extends AProxy {

        /**
         * 插件创建时调用,由于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

§ AProxy便利方法

AProxy提供获取http请求器和文件访问器的方法

    /**
         * 获取Http requester
         * @return
         */
        protected HttpRequester getHttpRequester()
    
        /**
         * 获取文件访问器
         * @return
         */
        protected FileAccessor getFileAccessor()
    }
1
2
3
4
5
6
7
8
9
10
11
12

§ 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

§ AProxy&APlugin插件APP生命周期Hook说明

在Aproxy和APlugin中能监听到APP的整个生命周期,我们在对应的回调中,做某些SDK的初始化或销毁工作

        //Activity正在启动,此时Activity已处于可见状态,只是还没有在前台显示,因此无法与用户进行交互
        public void onStart() {
        }
    
        //Activity已在前台可见,可与用户交互时调用
        public void onResume(boolean multitasking) {
        }
    
    
        //此方法被回调时则表示Activity正在停止(Paused形态)
        public void onPause(boolean multitasking) {
        }
    
        // 一般在onPause方法执行完成直接执行,表示Activity即将停止或者完全被覆盖(Stopped形态),此时Activity不可见,仅在后台运行
        public void onStop() {
        }
    
        //此时Activity正在被销毁,也是生命周期最后一个执行的方法
        public void onDestroy() {
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

§ AProxy&APlugin插件webview生命周期Hook说明


      //页面开始加载时调用
       public void onPageStarted(String url){
   
       }
   
       //页面加载结束时调用
       public void onPageFinished(String url){
   
       }
   
   
      //url请求时调用
       public Boolean shouldAllowNavigation(String url) {
           return null;
       }
   
   
   
      //处理https请求错误时调用
       public boolean onReceivedSslError(FXSslErrorHandler handler, FXSslError error){
           return false;
       }
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
最后更新于: 7/5/2022, 5:29:52 PM