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+")"); }) });