如何使用android與HTML混合開發

來源:互聯網
上載者:User
這次給大家帶來如何使用android與HTML混合開發,使用android與HTML混合開發的注意事項有哪些,下面就是實戰案例,一起來看一下。

現在很多的 APP中會嵌套HTML5的頁面,比如經常變化的等等,有一部分頁面需要原生Java與HTML5中的js進行互動操作,下面介紹一下android中HTML5的使用:

1、關於HTML5種cookie

網頁中可能會用到 使用者資訊等很多參數,可以提前把這些資訊放到cookie中,可以採用以下方法: 

public static void addCookies(Context context, WebView webView, String url) {  String url=“https://www.xxxx.com/xx/xx/”          String protocol = "";          String authority = "";          try {              URL urlObj = new URL(url);              protocol = urlObj.getProtocol();              authority = urlObj.getAuthority();         } catch (Exception e) {             e.printStackTrace();         }          String ua = webView.getSettings().getUserAgentString();         webView.getSettings().setUserAgentString(Constant.PROJECT_NAME + "/" + ParamHandler.getVersion(context) + "(" + ua + "; HFWSH)");          if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(protocol) && !TextUtils.isEmpty(authority)) {             if (protocol.equals("https") && authority.indexOf("liepin.com") > -1) {                 CookieSyncManager.createInstance(context);                 CookieManager cookieManager = CookieManager.getInstance();                 cookieManager.setAcceptCookie(true);                 try {                     List<String> data = getCookiesString();                     if (!ListUtils.isEmpty(data)) {                         for (String value : data) {                             cookieManager.setCookie(url, value);                         }                     }                     cookieManager.setCookie(url, "client_id=" + Constant.CLIENT_ID + ";path=/;domain=.XXXX.com");                     cookieManager.setCookie(url, "appVersion=" + Constant .VERSION + ";path=/;domain=.XXXX.com");              CookieSyncManager.getInstance().sync();          } catch (Exception e) {              LogUtils.e("Exception:" + e.getMessage());          }        }      }    }
public List<String> getCookiesString() {      ArrayList data = new ArrayList();      this.clearExpired();      Collection values = this.mCookies.values();      Iterator var3 = values.iterator();        while(var3.hasNext()) {          SwiftCookie c = (SwiftCookie)var3.next();          data.add(c.toCookieString());     }      return data; }

在 mWebView.loadUrl(Url)之前添加cookie,網頁就可以通過cookie取到相應的參數值了。

2、關於js的安全問題

js在4.2以前有漏洞

通過JavaScript,可以訪問當前裝置的SD卡上面的任何東西,甚至是連絡人資訊,簡訊等。好,我們一起來看看是怎麼出現這樣的錯誤的。

1,WebView添加了JavaScript對象,並且當前應用具有讀寫SDCard的許可權,也就是:android.permission.WRITE_EXTERNAL_STORAGE

2,JS中可以遍曆window對象,找到存在“getClass”方法的對象的對象,然後再通過反射的機制,得到Runtime對象,然後調用靜態方法來執行一些命令,比如訪問檔案的命令.

3,再從執行命令後返回的輸入資料流中得到字串,就可以得到檔案名稱的資訊了。然後想幹什麼就幹什麼,好危險。核心JS代碼如下:

 function execute(cmdArgs)    {        for (var obj in window) {            if ("getClass" in window[obj]) {                alert(obj);                return  window[obj].getClass().forName("java.lang.Runtime")                     .getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);            }        }   }

解決方案:

1,Android 4.2以上的系統

在Android 4.2以上的,google作了修正,通過在Java的遠程方法上面聲明一個@JavascriptInterface,如下面代碼:

class JsObject {      @JavascriptInterface      public String toString() { return "injectedObject"; }   }   webView.addJavascriptInterface(new JsObject(), "injectedObject");   webView.loadData("", "text/html", null);   webView.loadUrl("javascript:alert(injectedObject.toString())");

2,Android 4.2以下的系統

這個問題比較難解決,但也不是不能解決。

首先,我們肯定不能再調用addJavascriptInterface方法了。關於這個問題,最核心的就是要知道JS事件這一個動作,JS與Java進行互動我們知道,有以下幾種,比prompt, alert等,

這樣的動作都會對應到WebChromeClient類中相應的方法,對於prompt,它對應的方法是onJsPrompt方法,這個方法的聲明如下:

public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)

通過這個方法,JS能把資訊(文本)傳遞到Java,而Java也能把資訊(文本)傳遞到JS中,通知這個思路我們能不能找到解決方案呢?

經過一番嘗試與分析,找到一種比較可行的方案,請看下面幾個小點:

【1】讓JS調用一個Javascript方法,這個方法中是調用prompt方法,通過prompt把JS中的資訊傳遞過來,這些資訊應該是我們組合成的一段有意義的文本,可能包含:特定標識,方法名稱,參數等。

在onJsPrompt方法中,我們去解析傳遞過來的文本,得到方法名,參數等,再通過反射機制,調用指定的方法,從而調用到Java對象的方法。

【2】關於傳回值,可以通過prompt返回回去,這樣就可以把Java中方法的處理結果返回到Js中。

【3】我們需要動態產生一段聲明Javascript方法的JS指令碼,通過loadUrl來載入它,從而註冊到html頁面中,具體的代碼如下:

javascript:(function JsAddJavascriptInterface_(){        if (typeof(window.jsInterface)!='undefined') {                console.log('window.jsInterface_js_interface_name is exist!!');}         else {            window.jsInterface = {                        onButtonClick:function(arg0) {                     return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));                },                               onImageClick:function(arg0,arg1,arg2) {                    prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));               },           };       }   }   )()

說明:

1,上面代碼中的jsInterface就是要註冊的對象名,它註冊了兩個方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2),如果有傳回值,就添加上return。

2,prompt中是我們約定的字串,它包含特定的標識符MyApp:,後麵包含了一串JSON字串,它包含了方法名,參數,對象名等。

3,當JS調用onButtonClick或onImageClick時,就會回調到Java層中的onJsPrompt方法,我們再解析出方法名,參數,對象名,再反射調用方法。

4,window.jsInterface這表示在window上聲明了一個Js對象,聲明方法的形式是:方法名:function(參數1,參數2)

3、在html5中進行java和js的互動

1)、方法一:

mWebView.getSettings().setJavaScriptEnabled(true);mWebView.addJavascriptInterface(this, "xxx");

然後在當前類中實現以下方法:

@JavascriptInterface  public void callbackFromH5(final String j) {    //TODO  }

callbackFromH5的名字必須和網頁中的js方法名一樣

Java調用js方法:

mWebView.loadUrl(String.format("javascript:java2js(0)"));//這裡是java端調用webview的JS

js方法名需要和網頁端一直

2)方法二: 

jsbridge方法(https://github.com/lzyzsd/JsBridge)

Android JsBridge 就是用來在 Android app的原生 java 代碼與 javascript 代碼中架設通訊(調用)橋樑的協助工具輔助

1 將jsBridge.jar引入到我們的工程 

Android Studio:

  repositories {  // ...  maven { url "https://jitpack.io" }  }  dependencies {   compile 'com.github.lzyzsd:jsbridge:1.0.4'  }

2、布局檔案

<?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" >            <!-- button 示範Java調用web -->        <Button             android:id="@+id/button"           android:layout_width="match_parent"           android:text="@string/button_name"           android:layout_height="dp"           />              <!-- webview 示範web調用Java -->       <com.github.lzyzsd.jsbridge.BridgeWebView           android:id="@+id/webView"           android:layout_width="match_parent"           android:layout_height="match_parent" >        </com.github.lzyzsd.jsbridge.BridgeWebView>      </LinearLayout>

3、java代碼

//載入伺服器網頁            webView.loadUrl("https://www.baidu.com");                //必須和js同名函數。            webView.registerHandler("submitFromWeb", new BridgeHandler() {                    @Override                public void handler(String data, CallBackFunction function) {                       String str ="html返回給java的資料:" + data;                                  makeText(MainActivity.this, str, LENGTH_SHORT).show();                      Log.i(TAG, "handler = submitFromWeb, data from web = " + data);                   function.onCallBack( str + ",Java經過處理後:"+ str.substring(,));               }              });           //類比使用者擷取本地位置           User user = new User();           Location location = new Location();           location.address = "xxx";           user.location = location;           user.name = "Bruce";              webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {               @Override               public void onCallBack(String data) {                   makeText(MainActivity.this, "網頁在擷取你的資訊", LENGTH_SHORT).show();                  }           });              webView.send("hello");
webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {                      @Override                   public void onCallBack(String data) {                       // TODO Auto-generated method stub                       Log.i(TAG, "reponse data from js " + data);                   }                  });

js調用

 var str1 = document.getElementById("text1").value;             var str2 = document.getElementById("text2").value;                   //調用本地java方法               window.WebViewJavascriptBridge.callHandler(                   'submitFromWeb'                   , {'param': str}                   , function(responseData) {                       document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData                  }              );    //註冊事件監聽   document.addEventListener(                      'WebViewJavascriptBridgeReady'                      , function() {                          callback(WebViewJavascriptBridge)                      },                      false                  );    //註冊回呼函數,第一次串連時調用 初始化函數  connectWebViewJavascriptBridge(function(bridge) {              bridge.init(function(message, responseCallback) {                  console.log('JS got a message', message);                  var data = {                      'Javascript Responds': 'Wee!'                  };                  console.log('JS responding with', data);                  responseCallback(data);              });                 bridge.registerHandler("functionInJs", function(data, responseCallback) {                  document.getElementById("show").innerHTML = ("data from Java: = " + data);                  var responseData = "Javascript Says Right back aka!";                  responseCallback(responseData);              });          })

4、關於webView的最佳化

1、設定WebView 緩衝模式

private void initWebView() {                                mWebView.getSettings().setJavaScriptEnabled(true);                mWebView.getSettings().setRenderPriority(RenderPriority.HIGH);                mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);  //設定 緩衝模式                // 開啟 DOM storage API 功能                mWebView.getSettings().setDomStorageEnabled(true);                //開啟 database storage API 功能                mWebView.getSettings().setDatabaseEnabled(true);                String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME;       //      String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME;               Log.i(TAG, "cacheDirPath="+cacheDirPath);               //設定資料庫緩衝路徑               mWebView.getSettings().setDatabasePath(cacheDirPath);               //設定  Application Caches 緩衝目錄               mWebView.getSettings().setAppCachePath(cacheDirPath);               //開啟 Application Caches 功能               mWebView.getSettings().setAppCacheEnabled(true);

2、清除緩衝

/**            * 清除WebView緩衝            */            public void clearWebViewCache(){                                //清理Webview快取資料庫                try {                    deleteDatabase("webview.db");                     deleteDatabase("webviewCache.db");               } catch (Exception e) {                   e.printStackTrace();               }                              //WebView 快取檔案               File appCacheDir = new File(getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME);               Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath());                              File webviewCacheDir = new File(getCacheDir().getAbsolutePath()+"/webviewCache");               Log.e(TAG, "webviewCacheDir path="+webviewCacheDir.getAbsolutePath());                              //刪除webview 緩衝目錄               if(webviewCacheDir.exists()){                   deleteFile(webviewCacheDir);               }               //刪除webview 緩衝 緩衝目錄               if(appCacheDir.exists()){                   deleteFile(appCacheDir);               }           }

3、在使用WebView載入網頁的時候,有一些固定的資源檔如js/css/圖片等資源會比較大,如果直接從網路載入會導致頁面載入的比較慢,而且會消耗比較多的流量。所以這些檔案應該放在assets裡面同app打包。

解決這個問題用到API 11(HONEYCOMB)提供的shouldInterceptRequest(WebView view, String url) 函數來載入本地資源。

API 21又將這個方法棄用了,是重載一個新的shouldInterceptRequest,需要的參數中將url替換成了成了request。

比如有一個圖片xxxxx.png,這個圖片已經放在了assets中,現在載入了一個外部html,就需要直接把assets裡面的圖片拿出來載入而不需要重新從網路擷取。當然可以在html裡面將圖片連結換成file:///android_asset/xxxxx.png,

但是這樣這個html就不能在Android ,ios,WAP中公用了。

webView.setWebViewClient(new WebViewClient() {                    @Override                public WebResourceResponse shouldInterceptRequest(WebView view, String url) {                    WebResourceResponse response = null;                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){                        response = super.shouldInterceptRequest(view,url);                        if (url.contains("xxxxx.png")){                            try {                               response = new WebResourceResponse("image/png","UTF-8",getAssets().open("xxxxx.png"));                           } catch (IOException e) {                               e.printStackTrace();                           }                       }                   }   //                return super.shouldInterceptRequest(view, url);                   return  response;               }                  @TargetApi(Build.VERSION_CODES.LOLLIPOP)               @Override               public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {                   WebResourceResponse response = null;                   response =  super.shouldInterceptRequest(view, request);                   if (url.contains("xxxxx.png")){                       try {                           response = new WebResourceResponse("image/png","UTF-",getAssets().open("xxxxx.png"));                       } catch (IOException e) {                           e.printStackTrace();                       }                   }                   return response;               }   }

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

CSS實戰項目中書寫規範與順序

CSS層疊機制使用詳解

相關文章

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.