Android開發中java與javascript互動:PhoneGap外掛程式vs addJavascriptInterf

來源:互聯網
上載者:User

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,是以代碼形式傳入的(當然了,其實是可以改良的,但今天就不聊這個了)。

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

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.