国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android的WebView與H5前端JS代碼交互的實例代碼

2019-10-22 18:31:24
字體:
來源:轉載
供稿:網友

前段時間項目有深度和前端對接過,也是碰了一些坑,現在有時間就拿出來分享下

JS調用原生不外乎就兩種,一種是傳假的url,也就是url攔截的方式,類似于下面這種:

//js代碼 function sendCommand(param){  var url="js-call://"+param;  document.location = url; } sendCommand("PlaySnake");
//Java代碼 mWebView.setWebViewClient(new WebViewClient() {   @Override   public boolean shouldOverrideUrlLoading(WebView view, String url) {     if (url.contains("js-call:")) {       if (url.contains("PlaySnake")) {        Log.d("X5WebViewActivity", "玩蛇");       } else if (url.contains("WhatDoesTheFoxSay")) {        Log.d("X5WebViewActivity", "叮鈴鈴鈴叮鈴鈴");       } else {        showInfoAsToast("龜兒娃,你調得不對");       }       return false;     }     view.loadUrl(url);     return true;       } });

這種方法來調用原生,好處就是集成比較迅速,約定一個標識,類似于示例中的“js-call”,再約定一波Type,比如“玩蛇”之類的,代碼很簡單,畢竟大家都很忙。

但是如果你打算長期把這個項目做下去的話,這種方式還是不要了吧,缺點太明顯了。首先是給原生傳數據,只能是字符串;然后業務擴展起來,你的else if越寫越多,里面再加一大把switch,代碼越來臃腫,維護起來那感覺真的酸爽。

另一種就是通過谷歌提供的JS與Java綁定的接口,約定好要交互的對象名,類似于下面的“App”

//通過WebView提供的addJavascriptInterface這行代碼,我們在瀏覽器的JS環境中創建了一個"App"對象//這個對象下的函數就是自定義接口類里面通過 @JavascriptInterface注解的Java方法轉換而來的mWebView.addJavascriptInterface(new JavaFuckJSInterface(this), "App");/** * 自定義的交互接口類 */public class JavaFuckJSInterface{  private WeakReference<X5WebViewActivity> x5WebViewActivity;  public JavaFuckJSInterface(X5WebViewActivity context) {    x5WebViewActivity = new WeakReference<>(context);  }  //通過這個@JavascriptInterface轉化成綁定的“App”對象下的同名函數,js代碼可以直接調用  @JavascriptInterface  public void presentCamera(String data) {    //拍照上傳    x5WebViewActivity.get().presentCamera(data);  }}
//js代碼 var parameter = {}; parameter.size = "1024*768"; parameter.format = "JPEG"; var parameterStr = JSON.stringify(parameter); App.presentCamera(parameterStr);

這樣寫的話,規范了不少,即使函數再多,這個接口里面也是一目了然,調函數就是調函數,傳參數就是傳參數,相比于之前那個方法,可讀性高了不少

不過上面寫的這些破玩意網上資料一大把,我特么是吃多了么,再寫一遍?

NoNoNo,這些東西確實足夠我們與JS交互了,但是前端不想搞JSON.stringify(parameter)這種操作啊,他要直接傳對象過來。為什么別人IOS都可以拿到我的對象,你拿的就是undefined?為什么別人IOS能給我對象,你就不給我對象,偏要給我字符串?憑什么別人IOS能拿到我的匿名回調函數來調用,你偏偏讓我寫一個回調函數給你調?

ok,也不是不能做到,不過這就需要通過注入JS代碼來完成了

talk is cheap , show me the code

下面這個微型的SDK能夠實現互調傳JSON對象,調用js傳入的匿名函數

 //需要注入的js代碼,加//"是因為簡書會忽略/"這個回引號,不加的話后面的代碼都是字符串的顏色了 //原理是通過這個SDKNativeEvents來保存傳入的匿名函數callback,等原生做完該做的操作之后 //接著去調用sdk_nativeCallback這個函數來運行存進去的callback var SDKNativeEvents = {} function sdk_launchFunc(funcName,data,callback){   if(!data){     alert(/"必須傳入data/");//"     return;   }   if(!callback){     alert(/"必須傳入回調function/");//"     return;   }   SDKNativeEvents[funcName] = callback;   var jsObj={};   jsObj.funcName=funcName;   jsObj.data=JSON.stringify(data);   var str = JSON.stringify(jsObj);   App.native_launchFunc(str) //這個函數要在JavascriptInterface里申明 } function sdk_nativeCallback(funcName,data){   var obj= JSON.parse(data);   if(SDKNativeEvents[funcName]){     SDKNativeEvents[funcName](obj);     if(funcName != /"updateLocation/"){//定位回調會不定時去重復觸發,不做置空操作"       SDKNativeEvents[funcName] = null;     }   } } //下面實現的功能和通過@JavascriptInterface注解的Java方法是一樣的,App為約定好的注入對象名 //App.xxx為暴露給前端的js函數 App.login = function(data,callback){   sdk_launchFunc(/"login/",data,callback);//" } App.xxxxxxxxxxxxx = function(data,callback){   sdk_launchFunc(/"xxxxxxxxxxxxx/",data,callback);//" } ...

上面那些App.xxx的函數其實也可以不用注入,實現起來就是把 sdk_launchFunc這個函數注入到App對象下面,讓前端直接調用,這樣不用增加一個調用就多注入一個函數,前端只用改funcName就能實現所有的調用。但是我覺得,調函數就是調函數,傳參數就是傳參數,將每個功能拆成function可以提高代碼的可讀性

注入JS代碼也很簡單,把上面那些js代碼都粘貼到string這個資源文件里面,再通過mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code1))來注入就行,其中js_sdk_code1就是js代碼的字符串

示例代碼:

//在網頁加載時提前注入,可以保證頁面一旦加載完畢前端就能立即調到函數 mWebView.setWebChromeClient(new WebChromeClient() {      @Override      public void onProgressChanged(WebView webView, int i) {        super.onProgressChanged(webView, i);        if (i >= 10 && canInject) {          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code1));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code2));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code3));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code4));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code5));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code6));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code7));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code8));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code9));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code10));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code11));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code12));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code13));          mWebView.loadUrl("javascript:" + getString(R.string.js_sdk_code14));          canInject = false;        }        if (i == 100) {          canInject = true;        }     }});

這個時候有人就要問了,怎么注入這么多次,我也不想啊,這里有個坑的,一次注入的代碼超過三行左右(分號結束為一行)吧,就會有幾率出現注入失敗,會造成所有js代碼都沒法注入進去,我就干脆直接一次注入一行代碼來跳出這個坑,比如下面的js_sdk_code3就可以注入,雖然這個function內部有好幾行代碼,但是整體來說也算一行代碼,這行代碼定義了這個function。然而我又試了,在這個function里面再多加一行代碼就會注入失敗,搞得現在我也不確定他失敗的零界點在哪里,反正盡量拆開注入吧。

android,webview,h5,Android與H5交互,與js交互

將要注入的js代碼拆開注入

細心的同學已經發現了,搞了這么多花里胡哨的,最關鍵的原生怎么來響應js的調用還沒說明,別急,下面上代碼

  //@JavascriptInterface的代碼應該放在哪里不用我講了吧  //通過與js交互的接口類來拿到做什么事,以及傳過來的JSON對象轉成的字符串  @JavascriptInterface  public void native_launchFunc(String data) {    try {      JSONObject jsonObject = new JSONObject(data);      String funcName = jsonObject.getString("funcName");      String dataStr = jsonObject.getString("data");      switchName(funcName, dataStr);    } catch (JSONException e) {      e.printStackTrace();    }  }    private void switchName(String funcName, String dataStr) {    if (funcName == null) {      return;    }    switch (funcName) {      case "login":        x5WebViewActivity.get().login(data);        break;      case "xxx":        x5WebViewActivity.get().xxx(data);        break;    }  }   //這里演示調用了login讓原生來登陸,等登陸成功之后,我們去調用js的匿名回調,并傳入token   JsonObject jsonObject = new JsonObject();   jsonObject.addProperty("token", PreferencesHelper.getInstance().getToken());   String js = "javascript:sdk_nativeCallback(/'login/',/'" + jsonObject + "/')";   mWebView.loadUrl(js);

Android原生調用JS代碼也有兩種,一種是通過上面的loadUrl,一種是下面這種:

  String script = "sdk_nativeCallback(/'login/',/'" + jsonObject + "/')";  mWebView.evaluateJavascript(script, responseJson -> {     if (!TextUtils.isEmpty(responseJson)) {       //拿到js函數的返回值     } });

區別就是一個能拿到js函數的返回值,一個拿不到,這個根據自己的需求來選用

前端js調用原生傳入匿名回調的示例代碼:

//js代碼 var fucker = {}; fucker.name = "pdd"; fucker.age = 18; App.login(fucker, function (data) {    if (data.err) {     alert(data.err);    }    alert(data.token); });

我們可以看到,前端給我們傳入的是對象和匿名回調函數,匿名回調需要的參數依然是個對象,我們通過注入的SDK保存了這個回調函數,并自己做了對象和字符串轉換,實際上Java代碼最終拿到和傳出去還都是字符串,我們通過這個sdk統一的進行了轉換,前端js代碼那邊不用判斷手機是iPhone或者是Android,統一發出和接受對象,傳入回調函數,能夠減少他們很多工作量。

對了,因為Android版本不一致,android/274707.html">webview的兼容性參差不齊,選用了騰訊的X5內核瀏覽器來加載,其中有個坑就是全屏播放視頻會有qq瀏覽器的廣告,這個可以通過代碼去掉,也拿出來分享下吧:

 //去掉QQ瀏覽器廣告 private void removeTbsAd() {  getWindow().getDecorView().addOnLayoutChangeListener      ((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {        ArrayList<View> outView = new ArrayList<>();        View decorView = getWindow().getDecorView();        decorView.findViewsWithText(outView, "相關視頻", View.FIND_VIEWS_WITH_TEXT);        decorView.findViewsWithText(outView, "QQ瀏覽器", View.FIND_VIEWS_WITH_TEXT);        if (outView.size() > 0) {          outView.get(0).setVisibility(View.GONE);        }      }); }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 康保县| 磐安县| 大荔县| 丹东市| 兰考县| 准格尔旗| 黔南| 墨脱县| 南开区| 札达县| 巴里| 白银市| 龙山县| 沐川县| 明溪县| 绩溪县| 萝北县| 翼城县| 林周县| 鄂托克旗| 醴陵市| 镇康县| 阜城县| 年辖:市辖区| 柘城县| 皋兰县| 鸡泽县| 托克托县| 十堰市| 县级市| 从化市| 太湖县| 卢氏县| 岢岚县| 威信县| 临安市| 南澳县| 嘉鱼县| 广西| 鹤山市| 渝北区|