自定义Cordova插件

从0开始自定义一个Cordova插件,之后有需求可以参考本篇进行新插件的封装以供JavaScript调用

Posted by catface on January 1, 2015

Cordova概括

  • 通过 Cordova 官方提供的插件,我们可以轻松的在 H5 上调用 Android 封装好的调用手机硬件的方法

  • 个人理解,Cordova 插件就是 java 开发中的 jar 工具包、H5 开发中的 Ionic/AngularJs 等封装好的框架,方便我们一行代码或者一个标签就能实现一个完整的效果

  • Cordova 自定义插件的官方文档

Cordova作用及插件结构简洁

  • 能让 H5 调用任何原生封装好的功能(照相、对话框、数据库、二维码扫描、指纹、NFC)

  • 可以用一个 js 文件指向多个平台下封装好的原生方法

  • 最后,想要开发自己的插件,还是参考官方已有的插件结构来最好

    • 举例:官方 Vibration 插件结构(仅列出重要文件)

      1
      2
      3
      4
      5
      6
      7
      
        |cordova-plugin-vibration
        ----|src
        ---------|android
        --------------Vibration.java(①)
        ----|www
        ---------|vibration.js(②)
        ----plugin.xml(③)
      
    • java 代码:继承 CordovaPlugin,复写 execute() 方法,在方法中接收 js 传来的 action,匹配相应的java方法

    • js 中间件:调用 java 的方法暴露方法给 H5 调用

    • 配置文件:重要性参考 Android 的 AndroidManifest.xml、Servlet 的 Web.xml

自定义Cordova插件

照猫画虎,仿照官方明确插件结构

1
2
3
4
5
6
7
|catface.toast
----|src
---------|android
--------------MyToast.java
----|www
---------|toast.js
----plugin.xml

plugin.xml – 配置清单

  • | 结尾为相对不重要或为固定写法

  • ** 结尾为重要内容,需仔细核对

    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
    
      // 清单模板
      plugin
        
          · id:插件唯一标识|
          · version:版本号|
            
          ··· name:插件名称|
          ··· description:插件描述|
          ··· license:证书|
          ··· keywords:关键字|
          ··· repo:仓库地址|
          ··· issue:问题地址| 
        
        
      js-module
        
          · src:js中间件相对目录地址**
          · name://|
            
          ··· clobbers/merges
              ·target:H5通过它调用js中间件方法,可以配置多个**
        
        
      platform
        
          · name:对应平台"android"|
            
          ··· source-file
              · src:"src/android/java类名"**
              · tartget-dir:"src/包类"**会复制进项目包中
                
          ··· config-file
              · target:"res/xml/config.xml"|
              · parent:"/*"|
                
              ··· feature
                  · name:"js中间件通过它调用java方法"**
                    
                  ··· param
                      · name:"android-package"|
                      · value:原生插件类的包类路径**
        
          ··· config-file
              · target:"AndroidManifest.xml"|
              · parent:"/manifest"|
        
              ··· uses-permission:相关权限|
                
    

    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
    
      <!-- 本案例完整清单 -->
      <?xml version="1.0" encoding="UTF-8"?>
      <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
          id="catface.javat"
          version="0.0.2">
          <name>Catface Native Toast</name>
          <description>this is...</description>
        
          <js-module src="www/toast.js" name="toast">
              <clobbers target="catface"/>
          </js-module>
        
          <platform name="android">
              <source-file src="src/android/MyToast.java" target-dir="src/catface"/>
        
              <config-file target="res/xml/config.xml" parent="/*">
                  <feature name="Toast">
                      <param name="android-package" value="catface.MyToast"/>
                  </feature>
              </config-file>
        
              <config-file target="AndroidManifest.xml" parent="/*">  
                  <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
              </config-file> 
          </platform>
        
      </plugin>
    

toast.js – 中间件

  • prototype. 跟上暴露给 H5 的接口方法,可带参数及回调函数

  • exec 通过传入配置文件中的 js-module/clobbers/target 的属性值 、传给 java 类的 action 参数来调用 java 方法

    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
    
      var exec = require('cordova/exec');
      var myFunc = function(){};
        
      // arg1:成功回调
      // arg2:失败回调
      // arg3:将要调用类配置的标识
      // arg4:调用的原生方法名
      // arg5:参数,json格式
      myFunc.prototype.javaShow=function() {
          exec(null, null, "Toast", "javaShow", []);
      };
        
      myFunc.prototype.javaShowJs=function(text, lenth) {
          exec(null, null, "Toast", "javaShowJs", [text, lenth]);
      }
        
      myFunc.prototype.jsShowJava=function(success, error) {
          exec(success, error, "Toast", "jsShowJava", []);
      }
        
      myFunc.prototype.jsShowJs=function(text, success, error) {
          exec(success, error, "Toast", "jsShowJs", [text]);
      }
        
      myFunc.prototype.openVideo=function(content){
              exec(null, null, "Toast", "openVideo", [content]);
      };
        
      var showt = new myFunc();
      module.exports = showt;
    

MyToast.java – 原生 java 类

  • 继承 CordovaPlugin 接口,复写 execute 方法.

  • arg1:js 传来的 action 参数,通过匹配执行相应方法.

  • arg2:js 传来的参数,以 JSONArray 格式,通过 get 方法获取值.

  • arg3:java 提供的接口供回调给 H5.

    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
    59
    60
    61
    62
    63
    64
    
      public class MyToast extends CordovaPlugin {  
            
          private static final String TAG = "Toast";
    
          @Override public void initialize(CordovaInterface cordova, CordovaWebView webView) {
                 
              super.initialize(cordova, webView);
              Context context = this.cordova.getActivity().getApplicationContext();
                 
          }
            
          @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {  
                
              Activity activity = cordova.getActivity();
                
              if("javaShow".equals(action)) {
                  Toast.makeText(activity, "java show...", Toast.LENGTH_SHORT).show();
                
              } else if("javaShowJs".equals(action)) {
                  String str = args.getString(0);
                  int lenth = args.getInt(1);
                    
                  if (lenth == 1) {
                      Toast.makeText(activity, "::" + str, Toast.LENGTH_LONG).show();
                  } else {
                      Toast.makeText(activity, "::" + str, Toast.LENGTH_SHORT).show();
                  }
                
              } else if("jsShowJava".equals(action)) {
                  int a = 4, b = 5;
                  if (a > b) {
                      callbackContext.success("sucsucsuc" + a);
                  } else {
                      callbackContext.error("errerrerr" + b);
                  }
                  return true;
        
              } else if("jsShowJs".equals(action)) {
                  String text = args.getString(0);
                  if (!(text.equals(""))) {
                      callbackContext.success("js'text: " + text);
                  } else {
                      callbackContext.error("errjsjs");
                  }
                  return true;
        
              } else if("openVideo".equals(action)) {
                  openVideo(args.getString(0));
              }
             
              callbackContext.success();  
              return true;  
          }
          
          private void openVideo(String text){
              String url = text;
              String extension = MimeTypeMap.getFileExtensionFromUrl(url);  
              String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);  
              Intent mediaIntent = new Intent(Intent.ACTION_VIEW);  
              mediaIntent.setDataAndType(Uri.parse(url), mimeType);  
              //startActivity(mediaIntent); 
              cordova.startActivityForResult((CordovaPlugin) this, mediaIntent, 200);
          }   
      }
    

H5 调用演示

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
<script type="text/javascript">
        
        function bt1() {
            catface.javaShow();
        }
    
        function bt2() {
            catface.javaShowJs('fuck me', 1);
        }
    
        function bt3() {
            catface.jsShowJava(function(msg) {
                alert(msg);
            }, function(msg) {
                alert(msg);
            });
        }
    
        function bt4() {
            catface.jsShowJs("from js", function(msg) {
                alert(msg);
            }, function(msg) {
                alert(msg);
            });
        }
    
        function bt5() {
            catface.openVideo('video net src');
        }

</script>

补充说明

本案例介绍了四种调用方法,以供参考

  1. js ------> java

  2. js ---args---> java:js带参给java处理

  3. js ------> java ---args---> js:js得到java返回的参数

  4. js ---args---> java ---args---> js