Android集成Cordova

导语

Apache Cordova是一个开源的移动开发框架。允许你用标准的web技术-HTML5,CSS3和JavaScript做跨平台开发。 应用在每个平台的具体执行被封装了起来,并依靠符合标准的API绑定去访问每个设备的功能,比如说:传感器、数据、网络状态等。

使用Apache Cordova的人群:

  • 移动应用开发者,想扩展一个应用的使用平台,而不通过每个平台的语言和工具集重新实现。

  • web开发者,想包装部署自己的web App将其分发到各个应用商店门户。

  • 移动应用开发者,有兴趣混合原生应用组建和一个WebView(一个特别的浏览器窗口) 可以接触设备A级PI,或者你想开发一个原生和WebView组件之间的插件接口。

一、安装Cordova环境

1.下载安装Node.js node.js下载地址,这样就可以使用npm命令。

2.在终端中使用以下命令安装cordova。
sudo npm install -g cordova
其中g表示全局安装。

3.安装完成后运行一下命令查看cordova版本,若出现版本号则说明安装成功,我目前的版本号是8.0.0。
cordova -v

二、创建第一个Cordova工程,添加Android平台并运行

1.在创建第一个cordova工程。选择一个目录例如桌面cd desktop,运行以下命令创建一个cordova工程。
cordova create HelloCordova com.xxx
其中HelloCordova是项目名称,com.xxx是包名。

2.运行之后会生成一个空项目,目录如下:


image.png

3.进入工程根目录下,添加平台
Android:cordova platform add android
iOS:cordova platform add ios
添加完成之后会发现在platforms文件夹下生成了一个Android项目:

image.png

本文章主要介绍Android平台,如不需要iOS平台则可以不添加iOS。

4.在编译项目之前应该先检测编译环境是否满足,在cordova项目根目录下运行如下命令:
cordova requirements
运行后若环境满足则会打印如下信息:

image.png

若不满足则会提示缺少SDK配置或者jdk配置。

5.直接编译运行:
cordova build
注意:若使用cordova build则会编译所有平台,例如Android和iOS以及其他平台会同时被编译,若只想编译其中一个平台则运行cordova build android,加上平台后缀即可,单独编译iOS则cordova build ios
编译Android项目时用到gradle编译命令,所以运行前请保证环境变量已经配置完毕。
编译后将生成一个apk,可直接传到手机上运行:

image.png

运行截图:
image.png

这样第一个Android的Cordova项目就运行起来了。

6.用Android Studio打开该项目跟打开普通Android项目一样的:


image.png

三、Cordova插件调用

cordova官网提供了很多原生插件以供开发者使用,具体插件命令和作用可以查看这两篇文章:

1.在cordova工程根目录或者Android目录下运行一下命令
cordova plugin add cordova-plugin-camera
其中cordova plugin add 为添加插件操作,cordova-plugin-camera为插件名称。
卸载插件命令为:
cordova plugin remove cordova-plugin-camera
等待控制台打印

image.png

这样一个相机插件就添加完成了,查看文件目录可以发现添加该插件的同时都添加了哪些代码文件:


image.png

image.png

image.png

image.png

image.png

2.在用cordova添加Android平台时会在assets文件夹下创建www目录来管理插件和js代码:


image.png

首页面的页面编辑在index.html中:


index.html

逻辑编写在js文件夹下的index.js文件中:
index.js

3.我们可以分别编辑这两个文件,首先在index.html中编写两个按钮

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
    <meta name="format-detection" content="telephone=no">
    <meta name="msapplication-tap-highlight" content="no">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    <!--<link rel="stylesheet" type="text/css" href="css/index.css">-->
    <title>Hello World</title>
</head>
<body>
     <div class="app">
        
        <div class="line"><button id="take_picture">拍照</button></div>
        <div class="line"><img id="picture" style="height: 200px;"></img></div>

        <div class="line"><button id="open_photo">相册选择图片</button></div>
        <div class="line"><img id="photo" style="height: 200px;"></img></div>

    </div>
    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
</body>
</html>

注意cordova.js不能省略,且需放在其他js文件引用的第一行


image.png

4.编写index.js

首先简化代码:

var app = {
initialize: function() {
    document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
},
onDeviceReady: function() {
},
};
app.initialize();

document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
这一行是在初始化js文件的时候初始化cordova,不做改动。

初始化完成后会自动调用onDeviceReady: function() {},我们的点击事件就在这里面写:

var app = {
// Application Constructor
initialize: function() {
    document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
},

onDeviceReady: function() {
   //调用原生拍照
    document.getElementById("take_picture")
            .onclick = function(){

            navigator.camera.getPicture(onSuccess, onFail, {quality: 50,destinationType: Camera.DestinationType.DATA_URL});

            function onSuccess(imageData) {
              document.getElementById('picture')
              .src = "data:image/jpeg;base64," + imageData;
            }

            function onFail(message) {
            alert('Failed because: ' + message);
            }

    };

     //调用原生选择图片
     document.getElementById("open_photo")
             .onclick = function(){

             navigator.camera.getPicture(onSuccess, onFail, {quality: 50,destinationType: Camera.DestinationType.DATA_URL,
                            sourceType: Camera.PictureSourceType.PHOTOLIBRARY
                        });

             function onSuccess(imageData) {
                document.getElementById('photo')
                .src = "data:image/jpeg;base64," + imageData;
                };

              function onFail(message) {
                alert('Failed because: ' + message);
              };

            };

},
};

app.initialize();

需要说明的是:调用原生方法时需要传入一个调用成功onSuccess()和一个调用失败的方法onFail(),
Android返回结果时就会通过这两个方法,这里返回的是base64格式图片。

5.两个文件编写完成,运行查看结果:


image.png

关于相机动态权限问题该插件已经自动处理好了,cordova拥有一套完整的权限处理机制,主要通过PermissionHelper类来完成处理,在之后的自定义插件中如用到动态权限则可以参照相机插件的源码完成,具体可以查看该插件源码,这里就先不贴了。

四、创建自定义插件及调用

很多时候官方的插件并不能满足我们的开发需求,此时需要自定义插件以供开发调用,下面以创建一个能弹Toast的插件为例进行讲解。
1.插件的创建我们依赖一个plugman插件,该插件可以为我们自动创建一个cordova插件所需要的文件。
首先打开终端,运行以下命令安装plugman:
npm install -g plugman
安装完成后可通过plugman -v查看版本信息:

image.png

2.随便选择一个目录在该目录下打开终端创建自定义插件:
命令:plugman create --name [插件名] --plugin_id [插件ID] --plugin_version [插件版本号]
例子:plugman create --name toast-plugin --plugin_id toast-plugin --plugin_version 1.0.0
创建完成后可以看到该插件目录文件结构如下:

image.png

需要在src下手动创建一个android空目录,里面存放代码和资源文件,如果没有android目录则将该插件添加到项目中时会报找不到资源的错误。
image.png

3.根据功能先编写java文件,该java文件要继承cordova的CordovaPlugin.java,如我们要创建的插件功能为弹出一条Toast,为了结构规范以及编写方便,我们在Android studio中的app-src-java-org-apache下创建一个toa包,在包中创建ToaPlugin.java文件继承自CordovaPlugin.java,并编写Toast代码:
image.png

重写三个execute方法,这三个方法的功能是一样的,按照个人喜好重写其中一个即可,这里我们使用第三个方法

  • 参数action:js调用原生的方法名
  • 参数CordovaArgs:js传过来的参数,cordova做了封装
  • 参数CallbackContext:该对象很重要,要靠它实现js与原生的回调及数据交互
  • 另外需要返回一个boolean值来告诉cordova是否调用成功,若不写则会直接调用失败。
  • 上下文可以使用cordova.getActivity()或者cordova.getContext();

4.编写完成后将该插件文件剪切到plugman创建的插件文件夹toast-plugin中的src-android目录下:


image.png

注意:是剪切,如果复制的话记得将toa包和其中的ToaPlugin.java文件删掉,因为后面添加插件的时候cordova会自动将toa包和java文件复制进来。若项目中已经有了则会在添加插件的时候报错”已存在“。
5.编辑plugin.xml文件,该文件是各个平台对插件的各项配置,包括插件名称、id、调用方法名、代码文件及资源文件配置等。
初始只有插件名称和js配置:


image.png

在plugin节点下添加我们的插件配置,完整代码:
<?xml version='1.0' encoding='utf-8'?>
<plugin id="toast-plugin" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" 
xmlns:android="http://schemas.android.com/apk/res/android">
<name>toast-plugin</name>
<js-module name="toast-plugin" src="www/toast-plugin.js">
    <clobbers target="cordova.plugins.toast-plugin" />
</js-module>

<!--添加Android平台  -->
<platform name="android">
    <config-file target="res/xml/config.xml" parent="/*">  
         <!-- JS调用时的前缀名字 -->
        <feature name="ToaPlugin"> 
            <!-- 插件类名全路径 -->
            <param name="android-package" value="org.apache.cordova.toa.ToaPlugin"/>  
        </feature>  
    </config-file>  
    <!-- 意思从Android目录下找到ToaPlugin.java文件,拷贝到工程项目中的toa包下,包名要与上面的插件类名全路径对应上 -->
    <source-file src="src/android/ToaPlugin.java" target-dir="src/org/apache/cordova/toa" />  
</platform> 
</plugin>

注意:

  • 之所以要在toa包下编写ToaPlugin.java文件是为了包名匹配,若在别的包下编写则需要在复制java文件的时候记得把包名手动改成package org.apache.cordova.toa;
  • toast-plugin.js文件暂时不用编写
  • 这个xml文件不仅仅可以添加Android插件配置,同时也可以在plugin节点下继续添加iOS插件配置。

6.在插件目录下通过npm注册该插件:
npm init
弹出的提示依次回车:

image.png

完成后会在插件目录下生成一个package.json文件:
image.png

image.png

里面的内容是插件的名称、版本、描述、作者等信息,我们可以暂时不去管它,以后需要编辑的话重新在npm init弹出的提示中填入即可。

7.这样一个插件就编写完成了,复制插件路径,在as控制台或者终端通过以下命令将该插件添加到项目中去:
cordova plugin add /Users/mac/Desktop/toast-plugin
添加成功会打印如下信息:

image.png

可以看到插件文件已经被自动添加进来了:
image.png

另外插件会被注册到项目中:
image.png

image.png

8.编写index.html添加一个按钮

<div class="line"><button id="show_toa">弹出Toast</button></div>
image.png

编写index.js代码调用ToaPlugin插件:

      //调用原生选择图片
      document.getElementById("show_toa")
      .onclick = function(){
      cordova.exec(onSuccess,onFailed,"ToaPlugin","showToa",["这是从js传过来的消息"]);
      function onSuccess(deviceInfo) {}
      function onFailed(message) {alert('Failed : ' + message);}
      };
image.png

9.运行截图:


image.png

五、管理自定义插件中使用到的各种资源文件

以上只是创建了一个简单的自定义插件,没有使用其他资源文件,那么如要在自定义插件使用资源文件如图片,style,string,lib库等资源则需要将该插件写成lib库的形式导入工程中。下面使用加载图片资源的Activity作为插件来创建一个插件lib。
1.建立一个新项目或者在原项目上新建一个lib module,起名叫photolib
2.在res-drawbale中插入一张图片,创建Activity,编写布局代码,界面如下:


image.png

3.按照上面创建自定义插件的流程在桌面创建一个photo-plugin插件
4.将该lib库整体复制到插件src-android目录下:


image.png

5.在app项目中编写一个用于启动PhotoActivity的插件:
image.png

6.将插件文件同样复制到桌面创建的photo-plugin插件-src-android中:
image.png

7.编辑plugin.xml文件:

    <?xml version='1.0' encoding='utf-8'?>
    <plugin id="photo-plugin" version="1.0.0" 
    xmlns="http://apache.org/cordova/ns/plugins/1.0"           
    xmlns:android="http://schemas.android.com/apk/res/android">
    <name>photo-plugin</name>
    <js-module name="photo-plugin" src="www/photo-plugin.js">
    <clobbers target="cordova.plugins.photo-plugin" />
    </js-module>

<!--添加Android平台  -->
<platform name="android">
    <config-file target="res/xml/config.xml" parent="/*">  
         <!-- JS调用时的前缀名字 -->
        <feature name="ToaPlugin"> 
            <!-- 插件类名全路径 -->
            <param name="android-package" value="org.apache.cordova.photo.PhotoPlugin"/>  
        </feature>  
    </config-file>  
    <!-- 意思从Android目录下找到ToaPlugin.java文件,拷贝到工程项目中的toa包下,包名要与上面的插件类名全路径对应上 -->
    <source-file src="src/android/PhotoPlugin.java" target-dir="src/org/apache/cordova/photo" />  
    <framework src="src/android/photolib" custom="true"/>
</platform> 
</plugin>

请注意<framework src="src/android/photolib" custom="true"/>这一句,
framework标签代表引用内部或外部库资源,如要使用gson.jar包则这样写:
<framework src="src/android/gson.jar" custom="true"
关于plugin.xml文件中各个标签节点的含义可以参考cordova Plugin.xml 详解

8.编辑完成,在插件目录下打开终端运行 npm init,生成对应package.json文件。

9.复制插件路径,将插件添加到项目中,在as项目中运行命令:
cordova plugin add /Users/guibo/Desktop/photo-plugin
会发现photo-plugin插件以lib库的形式添加了进来:

image.png

同时项目自动依赖了该插件库:
image.png

10.编辑index.html再添加一个按钮,其余不变:

  <div class="line"><button id="jump_photo">跳转到图片界面</button></div>
image.png

index.js中:

      //跳转到图片界面
     document.getElementById("jump_photo")
             .onclick = function(){
             cordova.exec(onSuccess,onFailed,"PhotoPlugin","jumpPhoto");
             function onSuccess() {}
             function onFailed(message) {alert('Failed : ' + message);}
             };
image.png

运行测试即可。

10.若引用的资源文件不多也可以不使用lib库,可直接将图片文件复制到PhotoPlugin.java同级目录下:

image.png

plugin.xml中添加
<source-file src="src/android/photo.jpeg" target-dir="app/src/main/res/drawable" />
image.png

这里需留意一下target-dir="app/src/main/res/drawable这个目录,表示将图片复制到app项目中的drawable中,若资源文件不是图片而是.xml文件,则指定的目录可以省略app/src/main,例如:

    <!-- drawable -->
    <source-file       
    src="src/android/Library/res/drawable/back_button_selector.xml" target- 
    dir="res/drawable" />

    <!-- drawable-hdpi -->
    <source-file src="src/android/Library/res/drawable-hdpi/icon_back_press.png" target-dir="app/src/main/res/drawable-hdpi" />

原因是添加插件时由cordova中的js代码控制文件拷贝,查看该源码可以发现在cordova在处理图片、.xml文件和.java文件时的逻辑并不一样:

image.png

可以看到处理.xml和.java文件时会给目录前自动加上app/src/main,但并没有处理图片路径,所以我们在plugin.xml中的目标路径要写全路径:
target-dir="app/src/main/res/drawable
路径不正确会导致添加插件时指定的图片资源无法复制到项目中去。

六、补充操作

1.想要在AndroidManifest.xml文件中添加权限或者四大组件,platform标签下添加:

     <config-file parent="/manifest" target="AndroidManifest.xml">
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.VIBRATE" />
     </config-file>

    <config-file parent="/manifest/application" target="AndroidManifest.xml">
       <activity
        android:name="org.apache.cordova.qrscanner.CaptureActivity"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
    </activity>
    </config-file>
  1. string.xml:

     <config-file parent="/resources" target="res/values/strings.xml">
    
        <string name="cancel_label">"取消"</string>
        <string name="scan">请扫描</string>
        <string name="OK">确定</string>
        <string name="complete">完成</string>
       
     </config-file>
    

3.lib库:

    <source-file src="src/android/Library/libs/core-3.3.0.jar" target-        
    dir="app/src/main/libs" framework="true"/>

七、常见问题

1.经过实测,res/values文件夹下除了string.xml能添加资源以外,该文件夹下其他资源均添加不进去,如attr.xml、ids.xml、color、style、diments等。因此在使用的资源文件很多的情况下建议将插件写成lib库的形式添加,也方便管理。

八、感谢提供参考的宝贵文章:

Android使用Cordova进行混合开发
Android+Cordova混合开发以及Cordova自定义插件
cordova 插件之资源文件处理
cordova Plugin.xml 详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容