webview與JS的交互

webview與JS的交互javascript

一:hybird app, web app 和 native app 的區別php

  Web App Hybird App 混合Native App
開發成本
維護更新 簡單 簡單 複雜
體驗
跨平臺

 Native App是一種基於智能手機本地操做系統如IOS,Android等並運用原生程序編寫運行的第三方運用程序,也叫本地App。css

      Web App 是針對Iphone,Android優化後的web站點,前端使用的技術是:html5,css,javascript等,服務器端技術是java,php,asp等。須要注意的是web app開發仍是比較有限的。由於Web APP開發不能整合設備的核心功能,好比發文本信息,也不能充分使用APP Store進行更新。可是Web APP開發也有其優點所在。
       首先它解決了iphone APP的可擴展性問題,由於它是能夠跨平臺使用的。好比你開發了一款Web App,那麼它既能夠在手機iphone上使用,也能夠再平板ipad上使用,而不像iphone APP那樣只針對某個平臺。
       其次web APP也繞開了APP store嚴格的提交和更新審查規則。衆所周知,隨着APP store中的APP逐漸增多,APP store也推出了一系列的提交和審查規則,可謂至關之嚴格。而web APP則繞開了這些提交和更新審查規則,從而使得web APP的升級和維護變得更容易。由於它是一個獨立的站點,而不是依附於app store上的,不論是升級仍是維護在客戶端進行便可,無需提交審覈。html

Hybird App一般分爲三種類型:多view混合型,單View混合型,web主體型。前端

     1. 多view混合型:html5

     即Native View和web View獨立展示,交替出現。目前常見的Hybird App是Native View與web View交替出現,這種應用混合邏輯相對簡單,即在須要的時候,將webView當成一個獨立的view(Activity)運行起來,在webview內完成相關的展現操做。這種移動運用主體一般是 Native App,  web技術只起到補充做用。開發難度和Native App至關。java

     2. 單view混合型:android

     即在同一個View內,同時包括Native View和Web View。互相之間是覆蓋(層疊)的關係。這種Hybrid App的開發成本較高,開發難度較大,可是體驗較好。如百度搜索爲表明的單View混合型移動應用,既能夠實現充分的靈活性,又能實現較好的用戶體驗。ios

    3. Web主體型:web

    即移動應用的主體是Web View,主要以網頁語言編寫,穿插Native功能的Hybrid App開發類型。這種類型開發的移動應用體驗相對而言存在缺陷,但總體開發難度大幅下降,而且基本能夠實現跨平臺。Web主體型的移動應用用戶體驗的好壞,主要取決於底層中間件的交互與跨平臺的能力。國外的appMobi、PhoneGap,國內的AppCan和Rexsee都屬於Web主體型移動應用中間件。其中Rexsee不支持跨平臺開發。appMobi和PhoneGap除基礎的底層能力更可能是經過插件(Plugins)擴展的機制實現Hybrid。而AppCan除了插件機制,還提供了大量的單View混合型的接口來完善和彌補Web主體型Hybrid App體驗差的問題,接近Native App的體驗。

以上的知識點是從這邊參考的http://www.gtuanb.com/a/yd/2013/1231/127.html

二:Android webview與js的交互方式

  1. 關於webview。

     咱們知道目前android市場上的一些應用採用的開發方式分爲三種:Native App,web App,Hybird App 。下面介紹Hybird App中實現的主要技術native組件與JS的數據交互的理解。

   Android API中提供了webview組件來實現對html渲染,所謂的HybridApp開發方式便是聚集了HTML五、CSS三、jS的相關開發技術,以及數據交換格式json/XML。  下面是Android_webview 與 JS交互的步驟以下:

1.  新建一個webview的佈局webview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

若是應用中須要用到多個webview的界面,那隻要寫一個佈局就夠了。

2.  在activity中初始化webview

     1.  初始化佈局

setContentView(R.layout.exam);
webView = (WebView) findViewById(R.id.webview);        
webView.setScrollBarStyle(0);//滾動條風格,爲0指滾動條不佔用空間,直接覆蓋在網頁上

      2. 添加設置,使js代碼能運行

WebSettings setting = webView.getSettings();
setting.setJavaScriptEnabled(true);
setting.setJavaScriptCanOpenWindowsAutomatically(true);
setting.setAllowFileAccess(true);// 設置容許訪問文件數據
setting.setSupportZoom(true);
setting.setBuiltInZoomControls(true);
setting.setJavaScriptCanOpenWindowsAutomatically(true);
setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
setting.setDomStorageEnabled(true);
setting.setDatabaseEnabled(true);
setting.setDefaultTextEncodingName("GBK");//設置字符編碼
webView.addJavascriptInterface(new AndroidForJs(this), "JavaScriptInterface");

設置完這些後會發如今調試js代碼時仍是不能彈出alert對話框調式代碼,上網查了以後發現是要添加以下代碼:

webView.setWebChromeClient(new WebChromeClient());

    3.  裝載html5頁面

        本地界面:webView.loadUrl("file:///android_asset/test.html");,其中test.html頁面是放在assets文件夾下。

        在線界面:webView.loadUrl("http://www.baidu.com");

3.  具體的交互以下:

(1)js調用native代碼

上面的代碼中 webView.addJavascriptInterface(new AndroidForJs(this), "JavaScriptInterface");

其中AndroidForJs就是專門用來放js調用native的代碼的,"JavaScriptInterface"就是js調用AndroidForJs時的名稱,先寫一個AndroidForJs.java代碼以下:

public class AndroidForJs {

      private Context mContext;
      private String cmdCode, resultKey;
      private long visitTime;

    public AndroidForJs(Context context) {
        this.mContext = context;
    }

    // 以json實現webview與js之間的數據交互
    public String jsontohtml(String abc) {
        JSONObject map;
        JSONArray array = new JSONArray();
        try {
            map = new JSONObject();
            map.put("name", abc);
            map.put("age", 25);
            map.put("address", abc);
            array.put(map);

            map = new JSONObject();
            map.put("name", "jacky");
            map.put("age", 22);
            map.put("address", "中國北京");
            array.put(map);

            map = new JSONObject();
            map.put("name", "vans");
            map.put("age", 26);
            map.put("address", "中國深圳");
            map.put("phone", "13888888888");
            array.put(map);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return array.toString();
    }
}

上面的都是android(java)方法的配置項,前端不須要作任何事情。下面以下方法是前端調用native方法。

而後在js中能夠用以下方式調用native方法

var result = JavaScriptInterface.jsontohtml("uyiyu");

其中"uyiyu"就是js傳給native的參數,須要的話就把參數傳過去,不須要的話就不傳。看具體的需求。其中jsontohtml是java中的一個方法。也能夠改爲其餘的方法名。

result是native方法返回的值.通常是返回前端json數據,可是使用這種方式有問題,前端拿不到返回的數據,咱們能夠接着往下看。

注意:項目中遇到的問題是若是調用的native方法若是是一個向服務器發起請求的方法,以下:

public String jsontohtml () {
        String result = null;
        cmdCode = "123";
        visitTime = System.currentTimeMillis() / 1000;
        resultKey = Utils.getResultKey(cmdCode, visitTime, mContext);
        String key = Utils.getKey(cmdCode, visitTime, mContext);
        final String url = Utils.getMainUrl(key, cmdCode, visitTime,
                AllServerPort.URL_GET_EQUIPMENT, mContext);
        LogUtil.d(url);
        new HttpGetData(mContext, new CallBack() {

            @Override
            public void handlerData(String result) {
                // TODO Auto-generated method
                // stub
                LogUtil.d("-----RESPONSE------" + result);
                Map<String, String> backMsg = Utils.parseResponseResult(
                        mContext, result, cmdCode, visitTime, resultKey);
                if (backMsg.get(Constant.BACK_FLAG).equals("1")) {
                    String body = backMsg.get(Constant.BACK_BODY);
                    JSONObject jsonBody;
                    JSONArray jsonArray = null;
                    try {
                        jsonBody = new JSONObject(body);
                        jsonArray = jsonBody.getJSONArray("dispatchKitList");
                    } catch (JSONException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    result = jsonArray.toString();
                    LogUtil.d("------------success--------------" + jsonArray.toString());
                }

            }
        }, url).start();
        return result;
    }

這時若是還用var result = JavaScriptInterface.jsontohtml ();這個方法會發現獲取的result是null的,這是由於java中向服務器請求後到服務器返回結果進入回調函數handlerData是須要一段時間的,而js沒有等待這段時間就從java獲取了返回值,這時result還沒被賦值,就爲空了,解決的方法只能是native從服務器端獲取到數據後再調用js的方法把結果傳給js。因此上述方法改成

public String jsontohtml () {
        String result = null;
        cmdCode = "123";
        visitTime = System.currentTimeMillis() / 1000;
        resultKey = Utils.getResultKey(cmdCode, visitTime, mContext);
        String key = Utils.getKey(cmdCode, visitTime, mContext);
        final String url = Utils.getMainUrl(key, cmdCode, visitTime,
                AllServerPort.URL_GET_EQUIPMENT, mContext);
        LogUtil.d(url);
        new HttpGetData(mContext, new CallBack() {

            @Override
            public void handlerData(String result) {
                // TODO Auto-generated method
                // stub
                LogUtil.d("-----RESPONSE------" + result);
                Map<String, String> backMsg = Utils.parseResponseResult(
                        mContext, result, cmdCode, visitTime, resultKey);
                if (backMsg.get(Constant.BACK_FLAG).equals("1")) {
                    String body = backMsg.get(Constant.BACK_BODY);
                    JSONObject jsonBody;
                    JSONArray jsonArray = null;
                    try {
                        jsonBody = new JSONObject(body);
                        jsonArray = jsonBody.getJSONArray("dispatchKitList");
                    } catch (JSONException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    Message message = MyEquipmentActivity.handler.obtainMessage();
                    message.what = Constant.HANDLER_SHOW_EQUIPMENT;

                    message.obj = jsonArray.toString();
                    MyEquipmentActivity.handler.sendMessage(message);
                    LogUtil.d("------------success--------------" + jsonArray.toString());
                }

            }
        }, url).start();
        return result;
    }

Handler中調用js方法

public static Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            String handlerMsg = "";
            if (msg.obj != null) {
                handlerMsg = msg.obj.toString();
            }
            switch (msg.what) {
            case Constant.HANDLER_SHOW_EQUIPMENT:
                webView.loadUrl("javascript:getEquipmentSuccess('" + handlerMsg + "')");
                break;            
            default:
                break;
            }
        }

    };

如上代碼:webView.loadUrl("javascript:jsontohtmlSuccess('" + handlerMsg + "')");

js中調用native方法拿JSON數據最終方案:

前端只須要以下調用便可:

function jsontohtmlSuccess (json) {

    var data = eval("("+json+")");//解析json字符串

    // data 就是咱們從開發那邊拿回來的json數據了。

}

2) native調用js方法

常見的方法是

不帶參數:webView.loadUrl("javascript:submit()");

帶參數:webView.loadUrl("javascript:getListSuccess('" + handlerMsg + "')");

目前程序中使用的方法就是這個,可是由於用這個方法沒法獲取js return的參數,因此須要來回互相調用才能完成一次交互,比較麻煩。

3) 即上面可知:開發有2種(帶參數和不帶參數)方法調用前端代碼,以下:

不帶參數:webView.loadUrl("javascript:submit()");

帶參數:webView.loadUrl("javascript:getListSuccess('" + handlerMsg + "')");

 其中submit是咱們javascript中的一個方法名稱。以下能夠這樣寫代碼:

function submit(){
      var result = {「json」:」」};  // json數據
       JavaScriptInterface.submitResult(result);
 }

Json數據使用result變量保存起來,以後使用JavaScriptInterface.submitResult(result)方法調用便可。submitResult是java方法,不用管!開發經過上面的代碼就能夠拿到咱們前端的返回數據。

綜合所述:

 1.  js中調用native方法拿JSON數據最終方案:

     前端只須要以下調用便可:

function jsontohtmlSuccess(json) {
    var data = eval("("+json+")");//解析json字符串
    // data 就是咱們從開發那邊拿回來的json數據了。
}

服務器端須要有這個方法:

webView.loadUrl("javascript:jsontohtmlSuccess('" + handlerMsg + "')");

2.  JS中的數據返回給native端。以下代碼:

function submit(){
    var result = {「json」:」」};
    JavaScriptInterface.submitResult(result);
}

服務器端 須要有這個調用方法:

webView.loadUrl("javascript:submit()"); 

好比如今html5頁面有一個按鈕btn,點擊按鈕btn後,須要把數據傳遞給native端;代碼以下:

<div id=」 bookidA」> bookidA </div>
Var bookidA = document.getElementById(「bookidA」);
bookidA.addEventListener(‘click’,function(e){
    e.preventDefault();
    submit();
});
function submit(){
    var result = {「json」:」」};
    JavaScriptInterface.submitResult(result);
}

三: JS與iOS Native Code互調方法

    爲你們介紹一個優秀的國人開發開源小項目:WebViewJavascriptBridge。它優雅地實現了在使用UIWebView時JS與ios 的Objective-C nativecode之間的互調,支持消息發送、接收、消息處理器的註冊與調用以及設置消息處理的回調。它是鏈接UIWebView和Javascript的bridge。

   以下JS代碼實現connectWebViewJavascriptBridge

   // 鏈接html
   function connectWebViewJavascriptBridge(callback) {
       if (window.WebViewJavascriptBridge) {
           callback(WebViewJavascriptBridge)
       } else {
           document.addEventListener('WebViewJavascriptBridgeReady', function() {
                callback(WebViewJavascriptBridge)
           }, false)
       }
   }   
   connectWebViewJavascriptBridge (function(bridge) {
        // init方法是把數據傳給開發 data是前端須要傳遞的數據
        // 再調用responseCallback(data)
        bridge.init(function(message, responseCallback) {
             var data = {「json」:」」};
             responseCallback(data);
        });
        // registerHandler 這個方法是從ios拿到數據 給前端。其中testJavascriptHandler要與開發那邊名字對應上
        bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
            var data = eval("("+data+")");
       })
   });  
相關文章
相關標籤/搜索