写在前面

话说HTML5和原生代码这个口水仗打了很久了。打口水仗的人多半都是喷口水而已。这其中的奥妙真正干活的人才知道。如果说HTML5能完全代替原生,那要原生干什么?如果说HTML5完全不可用,那这么多牛逼的网站是打脸的么?所以说呢,webview在android和ios就是个控件,你就好好的当他控件来用。webview是一个有完整UI系统和生态系统的控件。用好了那是好处多多的。

好,那么问题来了。作为一个控件,是少不了和原生代码交互的。但是常见的用法似乎只是用来打开一个网页。这个勉强算是 "原生代码 call js", 还是一次性的。但是"js call 原生"要怎么玩呢?我曾经研究过一番,在前人的基础上写了个工具https://github.com/fangj/WebViewJavascriptBridge 但是不太完善。那么有现成的cordova 是要用起来的。

在说cordova之前。我大概说一下webview和原生怎么交互的:

  • Android

Android->JS : loadUrl
JS->Android: JavascriptInterface

  • IOS IOS->JS: loadUrl
    JS->IOS: loadUrl, IOS拦截自定义schema

下面正式开始讲:
主要内容都在文档里
http://cordova.apache.org/docs/en/4.0.0/guide_cli_index.md.html#The%20Command-Line%20Interface
http://cordova.apache.org/docs/en/4.0.0/guide_hybrid_plugins_index.md.html#Plugin%20Development%20Guide
http://cordova.apache.org/docs/en/4.0.0/guide_platforms_android_plugin.md.html
http://cordova.apache.org/docs/en/4.0.0/guide_platforms_ios_plugin.md.html

  • 用命令行工具生成cordova工程

    sudo npm install -g cordova
    cordova create hello com.example.hello HelloWorld
    cd hello
    cordova platform add ios
    cordova platform add android
    cordova build
    

此时android和ios的工程已经生成,可以用你的IDE打开运行了。

  • 开始写插件。先看JS里怎么调用:

    cordova.exec(function(winParam) {},
                 function(error) {},
                 "service",
                 "action",
                 ["firstArgument", "secondArgument", 42, false]);
    

几个参数分别是:成功回调。失败回调。要调用的对象(service)。要调用的方法(action)。方法的参数[...]。

  • 在IOS里怎么写插件

Echo.h

    #import "CDVPlugin.h"

    @interface Echo : CDVPlugin
    - (void)echo:(CDVInvokedUrlCommand*)command;
    @end

Echo.m

    #import "Echo.h"

    @implementation Echo
    - (void)echo:(CDVInvokedUrlCommand*)command
    {
        CDVPluginResult* pluginResult = nil;
        NSString* echo = [command.arguments objectAtIndex:0];

        if (echo != nil && [echo length] > 0) {
            NSLog(@"ios received %@",echo);
            NSLog(@"ios send OK");
            pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
        } else {
            NSLog(@"ios send fail");
            pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
        }

        [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
    }
    @end

还有很关键的。在ios工程目录下找到config.xml,加上这么一句

 <feature name="Echo">
    <param name="ios-package" value="Echo" />
    <param name="onload" value="true" />
</feature>

feature name是JS中调用的插件名,ios-package的value是IOS中对应的类名。

  • Android 中插件怎么玩
    Echo.java

    package org.apache.cordova.plugin;
    
    
    import android.util.Log;
    import org.apache.cordova.CallbackContext;
    import org.apache.cordova.CordovaPlugin;
    import org.json.JSONArray;
    import org.json.JSONException;
    
    
    /**
     * Created by fangjian on 14-11-28.
     */
    public class Echo extends CordovaPlugin {
        @Override
        public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
            if (action.equals("echo")) {
                String message = args.getString(0);
                this.echo(message, callbackContext);
                return true;
            }
            return false;
        }
    
    
    
    private void echo(String message, CallbackContext callbackContext) {
        if (message != null &amp;&amp; message.length() &gt; 0) {
            Log.d("cordova plugin", "android sent OK");
            callbackContext.success(message);
        } else {
            Log.d("cordova plugin", "android sent Fail");
            callbackContext.error("Expected one non-empty string argument.");
        }
    }
    
    }

同样,需要找到Android工程目录下的config.xml,加上这么一句

<feature name="Echo">
    <param name="android-package" value="org.apache.cordova.plugin.Echo" />
    <param name="onload" value="true" />
</feature>

android-package是android对应的类名。