webview與JS的交互


webview與JS的交互

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

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

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

      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上的,不管是升級還是維護在客戶端進行即可,無需提交審核。

Hybird App通常分為三種類型:多view混合型,單View混合型,web主體型。

     1. 多view混合型:

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

     2. 單view混合型:

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

    3. 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開發方式即是匯集了HTML5、CSS3、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+")");
       })
   });  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM