隨着App的設計、開發和維護成本的水漲船高以及H5+JS製做UI的日益成熟,hybrid的App愈來愈成爲不少開發者的選項之一。天然的,Apache的Cordova成爲大部分開發者的首選。對於開發一個App來講,一般沒法僅僅經過HTML+CSS+JS來實現諸如訪問本地的照片、獲取定位信息等還須要使用原生API來實現;這時插件便有了用武之地,經過它,Javascript代碼可以調用原生API,同時,原生API中的事件也能夠經過插件來傳遞到Web控件中,這樣就大大hybrid應用的功能。javascript
因爲本人使用Mac開發,故僅貼上Mac上開發環境的創建過程css
1 安裝nodejs和npmhtml
本人使用Homebrew(http://brew.sh/)安裝,讀者亦可到nodejs官網(https://nodejs.org)上下載安裝包直接下載java
brew install nodejs npm
2 安裝命令行工具node
sudo npm install -g cordova sudo npm install -g ios-sim #用於從命令行啓動iOS模擬器 sudo npm install -g plugman #插件管理工具
3 設置PATH以及ANDROID_HOME環境變量(用於編譯Android應用)
android
PATH=$PATH:/path/to/sdk/tools export PATH ANDROID_HOME=/path/to/sdk export ANDROID_HOME
4 安裝XCode的Command Line Tools(僅針對Mac上開發iOS應用)ios
到XCode->Preferences中相應的設置頁中安裝git
首先,使用plugman來建立插件工程github
plugman create -name MyPlugin --plugin_id com.jxjx.www.plugins --plugin_version 0.1 cd MyPlugin/ plugman platform add --platform_name ios plugman platform add --platform_name android
會產生兩個插件源代碼,分別爲MyPlugin/src/android/MyPlugin.java和MyPlugin/src/ios/MyPlugin.mshell
再建立Cordova應用
cordova create PluginDemo "com.jxjx.www.myplugindemos" 「MyPluginDemos" cd PluginDemo/ cordova platform add ios cordova platform add android
咱們html頁面以下:
<!DOCTYPE html> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <html> <head> <!-- Customize this policy to fit your own app's needs. For more guidance, see: https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy Some notes: * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this: * Enable inline JS: add 'unsafe-inline' to default-src --> <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 *"> <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"> <h1>Apache Cordova</h1> <div id="deviceready" class="blink"> <p class="event listening">Connecting to Device</p> <p class="event received">Device is Ready</p> </div> </div> <br /> <button id="btnEcho">echo</button> <button id="btnDelayedEcho">delayed echo</button> <script type="text/javascript" src="cordova.js"></script> <script type="text/javascript" src="js/index.js"></script> </body> </html>
咱們的index.js以下:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ var app = { // Application Constructor initialize: function() { this.bindEvents(); }, // Bind Event Listeners // // Bind any events that are required on startup. Common events are: // 'load', 'deviceready', 'offline', and 'online'. bindEvents: function() { document.addEventListener('deviceready', this.onDeviceReady, false); document.getElementById("btnEcho").onclick = function() { cordova.exec(function(data){ alert(data); }, function(err) { alert("error: " + err); }, "MyPlugin", "echo", ["hiiii"] ); }; document.getElementById("btnDelayedEcho").onclick = function() { cordova.exec(function(data){ alert(data); }, function(err) { alert("error: " + err); }, "MyPlugin", "delayedEcho", ["hiiii"] ); }; }, // deviceready Event Handler // // The scope of 'this' is the event. In order to call the 'receivedEvent' // function, we must explicitly call 'app.receivedEvent(...);' onDeviceReady: function() { app.receivedEvent('deviceready'); }, // Update DOM on a Received Event receivedEvent: function(id) { var parentElement = document.getElementById(id); var listeningElement = parentElement.querySelector('.listening'); var receivedElement = parentElement.querySelector('.received'); listeningElement.setAttribute('style', 'display:none;'); receivedElement.setAttribute('style', 'display:block;'); console.log('Received Event: ' + id); } }; app.initialize();
插件是Cordova跟原生接口之間的橋樑,js經過以下接口調用原生接口:
cordova.exec(function(successParam) {}, function(err) {}, "service", "action", ["firstArgument", "secondArgument", 42, false] );
咱們的示例插件提供了兩個接口,一個爲echo,直接返回傳入的參數,用以演示當即返回參數值的狀況;另外一個爲delayedEcho,調用後通過3秒後延時返回,用以演示原生程序經過事件方式通知js層的方法。
對於Android系統,其對應於從org.apache.cordova.CordovaPlugin繼承的execute方法,咱們的代碼(MyPlugin.java)以下:
package com.jxjx.www.plugins; import java.util.Timer; import java.util.TimerTask; import android.os.Handler; import android.os.Message; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CallbackContext; import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * This class echoes a string called from JavaScript. */ public class MyPlugin extends CordovaPlugin { private CallbackContext cbCtx; private Timer mTimer; private String paramSaved; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { mTimer.cancel(); mTimer = null; PluginResult result = new PluginResult(PluginResult.Status.OK, "delayed echo: "+ paramSaved); result.setKeepCallback(false); if (cbCtx != null) { cbCtx.sendPluginResult(result); cbCtx = null; } } }; private void setTimerTask() { mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { Message message = new Message(); message.what = 1; mHandler.sendMessage(message); } }, 3000, 3000); } @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { if (action.equals("echo")) { String message = args.getString(0); this.echoMethod(message, callbackContext); return true; } else if (action.equals("delayedEcho")) { this.cbCtx = callbackContext; paramSaved = args.getString(0); PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); setTimerTask(); callbackContext.sendPluginResult(result); return true; } return false; } private void echoMethod(String message, CallbackContext callbackContext) { if (message != null && message.length() > 0) { callbackContext.success(message); } else { callbackContext.error("Expected one non-empty string argument."); } } }
若是execute方法返回true,則exec輸入的第一個回調函數被調用,返回參數經過一個org.apache.cordova.PluginResult參數傳遞;若是execute方法返回false,則exec輸入的第二個回調函數被調用;
service參數爲插件名;
exec的action對應於execute方法的action;
exec的第四個參數必須爲一個數組,對應於execute的args參數;
execute方法的callbackContext參數爲上下文;若是要將事件返回至js層,須要保存起來供後續調用,而且添加以下一行代碼:
result.setKeepCallback(true);
對於iOS系統,其對應於CDVPlugin類的相應方法,咱們的代碼以下:
/********* MyPlugin.m Cordova Plugin Implementation *******/ #import <Cordova/CDV.h> @interface MyPlugin : CDVPlugin { // Member variables go here. } @property (nonatomic, strong) NSString *callbackId; @property (nonatomic, strong) NSString *echoStr; - (void)echo:(CDVInvokedUrlCommand*)command; - (void)delayedEcho:(CDVInvokedUrlCommand*)command; @end @implementation MyPlugin - (void)timeoutCb:(id)arg { NSTimer *timer = (NSTimer *)arg; NSString *echoStr = [NSString stringWithFormat:@"delayed echo: %@", timer.userInfo]; CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echoStr]; [result setKeepCallbackAsBool:NO]; [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; } - (void)echo:(CDVInvokedUrlCommand*)command { CDVPluginResult *pluginResult = nil; NSString *echo = [command.arguments objectAtIndex:0]; if (echo != nil && [echo length] > 0) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo]; } else { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; } [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } - (void)delayedEcho:(CDVInvokedUrlCommand*)command { CDVPluginResult* pluginResult = nil; NSString *echo = [command.arguments objectAtIndex:0]; if (echo != nil && [echo length] > 0) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT]; [pluginResult setKeepCallbackAsBool:YES]; self.callbackId = command.callbackId; self.echoStr = echo; [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:@selector(timeoutCb:) userInfo:echo repeats:NO]; } else { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; } [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } @end
與Android平臺不一樣的是,action對應的是Objective-C類的方法名,其他徹底相似。
在iOS平臺中若是要傳遞異步消息至js層,須要保存的是callbackId;
插件編寫完成後,須要在App的根目錄中使用plugman命令來添加插件
#若是修改了插件源碼須要先移除原插件:cordova plugin remove com.jxjx.www.plugins cordova plugin plugin add ../MyPlugin/ cordova run android cordova run ios
界面的效果以下
最後,附上源碼連接:http://pan.baidu.com/s/1hq0h0X2