Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發中java與javascript交互:PhoneGap插件vs addJavascriptInterface

Android開發中java與javascript交互:PhoneGap插件vs addJavascriptInterface

編輯:關於Android編程

1.        前言

在《用PhoneGap+jQueryMobile開發Android應用實例》中,我們講到PhoneGap(以下稱Cordova)開發環境的搭建,以及如何整合出一個基本的Android應用框架(並給出了范例代碼)。於是乎,我們便開始日夜兼程,披星戴月的炮制我們的第一個手機應用了。

但實際上,除了常見的API調用規范(有且僅有自查手冊一途)引起的問題之外,我們仍然會遇到其他形形色色的各種問題。那麼在這篇文章中,我們談談java與js之間的交互問題(哦,目前僅關注Android,所以只能談java了)。當然,二者之間的交互目的,原因會有種種不同,但應該還是以發揮語言各自的優勢,提供接口給對方調用的意圖居多。

我們知道,在Android平台下,Cordova是通過內置WebKit內核的方式來實現界面容器的(事實上,在其他平台也是如此)。我們也同樣知道,Cordova是一個橋接框架,其目的就是為原生API和js建立橋接,互通有無的。為了便於我們擴展自己的應用,Cordova還提供了插件(PlugIn)機制(當然,我們還可以直接修改Cordova開源代碼)。只要遵循一定的規則(恩恩,事實上這個規則很簡單),就可以擴展出豐富的功能特效來。

 

2.        Cordova插件與 WebView.addJavascriptInterface

恩恩,本文的主題是java與js的交互(差點跑了)。剛提到的,Cordova有插件機制,可以通過插件的形式,實現java與js交互,為什麼還要提到addJavascriptInterface?

Cordova插件確實可以實現二者的交互,而且是異步的,非常方便。但基於一些特殊的原因,例如:一個回調需要被多次調用(啊哈,或許是我太菜?使用PlugIn注冊的回調都只能被調用一次)。又或者不想寫插件,想直接點。

總之,插件也並不是時時處處都符合我們的需求(我們的欲望無窮大啊),總是要找辦法解決,尋點不同的路出來(個人不是特別認同Cordova的Hack方式,遑論其插件;而且Cordova目前的狀態有點怪異,版本更新是很快,但文檔更新不同步)。要真正成熟,還是有一段路要走的。

addJavascriptInterface則是WebKit的原生API,屬於WebView對象的公共方法,用於暴露一個java對象給js,使得js可以直接調用java方法。當然,我們要實現java與js的雙向交互,還需要另一個方法loadUrl(同屬於WebView對象,Cordova也是采用的這個方法調用js的)的配合。

當然,這兩種方式互有優劣(只有實踐時,才會明白啊)。Cordova插件的不足剛才已經提過;而addJavascriptInterface也有些問題,一是Android平台封裝WebKit內核時,不同的版本中有些許不一致;其次,直接使用loadUrl加載js實在是讓人頭疼。

其實應該有更好的方法,比如擴展js引擎(我更喜歡這種方式),但這種方式相對而言,涉及的內容繁雜,暫時不納入這次的話題。

 

3.        Cordova插件的實現

Cordova插件分為兩個部分(額,Cordova本身也是分為兩個部分的,別扭不?),一部分由java實現,另一部分由js實現。

1)        java部分

Cordova插件的java部分很簡單,繼承Cordova.Plugin,實現execute方法就可以了:

public classNotificationClient extends Plugin {

         private static final String TAG ="NotificationClient";

         private String callbackId ="";

         public PluginResult execute(Stringaction, JSONArray args, String callbackId) {

                   PluginResult.Status status =PluginResult.Status.OK;

                   if(action.equals("register")){

                            try {

                                     register(args.getString(0),args.getString(1));

                            } catch(JSONException e) {

                                     status =PluginResult.Status.JSON_EXCEPTION;

                            }

                   } elseif(action.equals("watch")) {

                            this.callbackId =callbackId;

                            PluginResult r = newPluginResult(PluginResult.Status.NO_RESULT);

                            r.setKeepCallback(true);

                            return r;

                   } else {

                            status =PluginResult.Status.INVALID_ACTION;

                   }

                   return newPluginResult(status);

         }

         public Object onMessage(String id,Object data) {

                   Log.d(TAG,"onMessage(" + id + ").");

                   if(id.equals("onClientNotification")){

                            if(!callbackId.equals("")){

                                     this.success("true",callbackId);

                            }

                   }

                   return data;

         }

         private void register(String username,String phone) {

                   Log.d(TAG,"register(" + username + ", " + phone + ").");

         }

}

嗯,就這樣,作為一個Cordova插件java部分的范例,他已經完成了使命(原諒我為了節省篇幅,刪掉了注釋和空行;不必太多介懷,參考資源裡有很多范例工程可以學習)。

不得不說,Cordova還是做了很多工作的,為了減輕插件開發的工作量,對js的調用進行了很多的包裝(回頭看看loadUrl是多麼的貧瘠的時候,才會有如此感慨吧)。

 

2)        js部分

唉,讓我渾身別扭的部分來了。說到js部分,我接觸過的版本裡(當然,我也僅僅接觸過3個版本而已:1.0、2.0、2.1)已經有兩種寫法。嗯,從執行效果上來說,2.0是兼容1.0的寫法的(哦哦,前提是我做了一些改動,雖然改動很小);美中不足的是,跟蹤腳本時還是會報錯,雖然不影響腳本的繼續加載。

先來看看第一種寫法吧(1.0的寫法):

functionNotificationClient() { }

NotificationClient.prototype.register= function(userName, phone) {

         PhoneGap.exec(null, null,"NotificationClient", "register", [ userName, phone ]);

};

NotificationClient.prototype.watch= function(fn) {

         PhoneGap.exec(fn, null,"NotificationClient", "watch", []);

};

PhoneGap.addConstructor(function(){

         if(typeof navigator.notificationClient== "undefined")

                   navigator.notificationClient= new NotificationClient();

});

網上的教程都是這麼弄的,事實上運行時會報錯:找不到PhoneGap對象;更嚴重的是navigator.notificationClient在運行時根本無法訪問。

當然,如果你改成這樣:

// PhoneGap.addConstructor(function(){

         if(typeof navigator.notificationClient== "undefined")

                   navigator.notificationClient= new NotificationClient();

// });

程序是可以正常運行的,雖然仍然會報錯。

 

OK,再來看看第二種(2.0的寫法):

cordova.define("cordova/plugin/notificationClient",function(require, exports, module){

         var exec = require('cordova/exec');

         var NotificationClient = function() {};

         NotificationClient.prototype.register =function(userName, phone) {

                   exec(null, null,"NotificationClient", "register", [ userName, phone ]);

         };

         NotificationClient.prototype.watch =function(fn) {

                   exec(fn, null,"NotificationClient", "watch", []);

         };

         var notificationClient = newNotificationClient();

         module.exports = notificationClient;

});

if(!window.plugins) {

         window.plugins = { };

}

if(!window.plugins.notificationClient) {

         window.plugins.notificationClient =cordova.require("cordova/plugin/notificationClient");

}

恩,這種寫法沒有任何錯誤了,而且能正常運行,開心。

 

3)        注冊插件

把插件寫完之後,還需要注冊,插件才能在Cordova下使用。找到工程目錄下的res\xml目錄,1.0打開plugins.xml文件,2.0打開config.xml文件,在plugins節點下加入:

<pluginname="NotificationClient"value="cn.yofang.mobile.NotificationClient"/>

 

至此,NotificationClient插件就可以在js中調用了。

1.0的用法:

navigator.notificationClient.register("azhi","15810108888");

2.0的用法:

window.plugins.notificationClient.register("azhi","15810108888");

 

4.        WebView.addJavascriptInterface實現

啊,終於到addJavascriptInterface了,每次文檔寫到一半左右都手酸吶(看文檔的人是不是也暗歎了一聲:終於來了)。

addJavascriptInterface比起Cordova插件來更加的簡單,首先我們來定義一個類:

public classNotificationClient {

         private static final String TAG ="NotificationClient";

         private Context context = null;

         private CordovaWebView view = null;

         private String callback = "";

         public NotificationClient(Contextcontext, CordovaWebView view) {

                   this.context = context;

                   this.view = view;

         }

         public void register(String user,String mobile, String callback) {

                   Log.d(TAG, "register(user: " + user + ", mobile: " + mobile + ", callback:" + callback + " )");

                   this.callback = callback;

                   checkMessage();

         }

         public void checkMessage() {

                   SharedPreferences sp =context.getSharedPreferences("NotificationClient", 0);

                   int message =Integer.valueOf(sp.getInt("Message", 0));

                   Log.d(TAG,"checkMessage(): " + message);

                   if(message > 0) {

                            Editor editor =sp.edit();

                            editor.putInt("Message",0);

                            editor.commit();

                            newHandler().post(new Runnable() {

                                     public voidrun() {

                                               view.sendJavascript(callback);

                                     }

                            });

                   }

         }

};

這個類的意圖很簡單(嗯,跟上面Cordova插件的NotificationClient插件很相似對不對?):提供一個register方法供js調用,傳入相應的參數增加了一個callback,在合適的時機(通過SharedPreferences檢查Message標志,大於0則認為是合適的時機了),從java端調用這個callback(當然,代碼裡使用了SharedPreferences、Handler等其他的Android原生對象,大家暫時忽略就是)。

嚯,我是不是沒有用loadUrl,而是用的sendJavascript?sendJavascript是Cordova對WebView封裝後提供的方法,其實把那一句改成:

view.loadUrl("javascript:"+ this.callback);

效果是一樣的(當然,如果你不是用Cordova,而是自己寫的Activity,那麼你就必須得這麼寫了)。

好了,類寫完了,下面就應該把這個類暴露給js了:

appView.getSettings().setJavaScriptEnabled(true);     // 暴露之前,先開啟javascript

appView.addJavascriptInterface(newNotificationClient(this, appView), "notificationClient");

嗯,這裡用到了appView(DroidGap的成員變量),我們使用的Cordova嘛,所以用這個沒有罪過的。如果是直接實現的Activity,就要自己內嵌WebView了,把appView改成自己的WebView對象即可。

         再就是js裡的用法了:

window.notificationClient.register("azhi","15810108888","OnMessage();");

大家看到了,在js中調用時,還是比較方便的,不需要預先建立js類對象,通過addJavascriptInterface添加的對象直接就附加在window對象上了。但弊端也是很明顯,看看我們的callback,是以代碼形式傳入的(當然了,其實是可以改良的,但今天就不聊這個了)。

         呵呵,稍微來點結束語:就這樣吧,希望大家都有所收獲。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved