导语
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.运行之后会生成一个空项目,目录如下:
3.进入工程根目录下,添加平台
Android:cordova platform add android
iOS:cordova platform add ios
添加完成之后会发现在platforms文件夹下生成了一个Android项目:
本文章主要介绍Android平台,如不需要iOS平台则可以不添加iOS。
4.在编译项目之前应该先检测编译环境是否满足,在cordova项目根目录下运行如下命令:
cordova requirements
运行后若环境满足则会打印如下信息:
若不满足则会提示缺少SDK配置或者jdk配置。
5.直接编译运行:
cordova build
注意:若使用cordova build
则会编译所有平台,例如Android和iOS以及其他平台会同时被编译,若只想编译其中一个平台则运行cordova build android
,加上平台后缀即可,单独编译iOS则cordova build ios
编译Android项目时用到gradle编译命令,所以运行前请保证环境变量已经配置完毕。
编译后将生成一个apk,可直接传到手机上运行:
运行截图:
这样第一个Android的Cordova项目就运行起来了。
6.用Android Studio打开该项目跟打开普通Android项目一样的:
三、Cordova插件调用
cordova官网提供了很多原生插件以供开发者使用,具体插件命令和作用可以查看这两篇文章:
- https://cordova.apache.org/plugins/
-
cordova插件大全
下面使用相机插件为例演示调用官方插件完成拍照及选择图片:
1.在cordova工程根目录或者Android目录下运行一下命令
cordova plugin add cordova-plugin-camera
其中cordova plugin add 为添加插件操作,cordova-plugin-camera为插件名称。
卸载插件命令为:
cordova plugin remove cordova-plugin-camera
等待控制台打印
这样一个相机插件就添加完成了,查看文件目录可以发现添加该插件的同时都添加了哪些代码文件:
2.在用cordova添加Android平台时会在assets文件夹下创建www目录来管理插件和js代码:
首页面的页面编辑在index.html中:
逻辑编写在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文件引用的第一行
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.两个文件编写完成,运行查看结果:
关于相机动态权限问题该插件已经自动处理好了,cordova拥有一套完整的权限处理机制,主要通过PermissionHelper类来完成处理,在之后的自定义插件中如用到动态权限则可以参照相机插件的源码完成,具体可以查看该插件源码,这里就先不贴了。
四、创建自定义插件及调用
很多时候官方的插件并不能满足我们的开发需求,此时需要自定义插件以供开发调用,下面以创建一个能弹Toast的插件为例进行讲解。
1.插件的创建我们依赖一个plugman插件,该插件可以为我们自动创建一个cordova插件所需要的文件。
首先打开终端,运行以下命令安装plugman:
npm install -g plugman
安装完成后可通过plugman -v
查看版本信息:
2.随便选择一个目录在该目录下打开终端创建自定义插件:
命令:plugman create --name [插件名] --plugin_id [插件ID] --plugin_version [插件版本号]
例子:plugman create --name toast-plugin --plugin_id toast-plugin --plugin_version 1.0.0
创建完成后可以看到该插件目录文件结构如下:
需要在src下手动创建一个android空目录,里面存放代码和资源文件,如果没有android目录则将该插件添加到项目中时会报找不到资源的错误。
3.根据功能先编写java文件,该java文件要继承cordova的CordovaPlugin.java,如我们要创建的插件功能为弹出一条Toast,为了结构规范以及编写方便,我们在Android studio中的app-src-java-org-apache下创建一个toa包,在包中创建ToaPlugin.java文件继承自CordovaPlugin.java,并编写Toast代码:
重写三个execute方法,这三个方法的功能是一样的,按照个人喜好重写其中一个即可,这里我们使用第三个方法
- 参数action:js调用原生的方法名
- 参数CordovaArgs:js传过来的参数,cordova做了封装
- 参数CallbackContext:该对象很重要,要靠它实现js与原生的回调及数据交互
- 另外需要返回一个boolean值来告诉cordova是否调用成功,若不写则会直接调用失败。
- 上下文可以使用cordova.getActivity()或者cordova.getContext();
4.编写完成后将该插件文件剪切到plugman创建的插件文件夹toast-plugin中的src-android目录下:
注意:是剪切,如果复制的话记得将toa包和其中的ToaPlugin.java文件删掉,因为后面添加插件的时候cordova会自动将toa包和java文件复制进来。若项目中已经有了则会在添加插件的时候报错”已存在“。
5.编辑plugin.xml文件,该文件是各个平台对插件的各项配置,包括插件名称、id、调用方法名、代码文件及资源文件配置等。
初始只有插件名称和js配置:
在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
弹出的提示依次回车:
完成后会在插件目录下生成一个package.json文件:
里面的内容是插件的名称、版本、描述、作者等信息,我们可以暂时不去管它,以后需要编辑的话重新在
npm init
弹出的提示中填入即可。
7.这样一个插件就编写完成了,复制插件路径,在as控制台或者终端通过以下命令将该插件添加到项目中去:
cordova plugin add /Users/mac/Desktop/toast-plugin
添加成功会打印如下信息:
可以看到插件文件已经被自动添加进来了:
另外插件会被注册到项目中:
8.编写index.html添加一个按钮
<div class="line"><button id="show_toa">弹出Toast</button></div>
编写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);}
};
9.运行截图:
五、管理自定义插件中使用到的各种资源文件
以上只是创建了一个简单的自定义插件,没有使用其他资源文件,那么如要在自定义插件使用资源文件如图片,style,string,lib库等资源则需要将该插件写成lib库的形式导入工程中。下面使用加载图片资源的Activity作为插件来创建一个插件lib。
1.建立一个新项目或者在原项目上新建一个lib module,起名叫photolib
2.在res-drawbale中插入一张图片,创建Activity,编写布局代码,界面如下:
3.按照上面创建自定义插件的流程在桌面创建一个photo-plugin插件
4.将该lib库整体复制到插件src-android目录下:
5.在app项目中编写一个用于启动PhotoActivity的插件:
6.将插件文件同样复制到桌面创建的photo-plugin插件-src-android中:
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库的形式添加了进来:
同时项目自动依赖了该插件库:
10.编辑index.html再添加一个按钮,其余不变:
<div class="line"><button id="jump_photo">跳转到图片界面</button></div>
index.js中:
//跳转到图片界面
document.getElementById("jump_photo")
.onclick = function(){
cordova.exec(onSuccess,onFailed,"PhotoPlugin","jumpPhoto");
function onSuccess() {}
function onFailed(message) {alert('Failed : ' + message);}
};
运行测试即可。
10.若引用的资源文件不多也可以不使用lib库,可直接将图片文件复制到PhotoPlugin.java同级目录下:
plugin.xml中添加
<source-file src="src/android/photo.jpeg" target-dir="app/src/main/res/drawable" />
这里需留意一下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文件时的逻辑并不一样:
可以看到处理.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>
-
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 详解