Android-Java和HTML5交互-混合開發-優化

如今不少的 APP中會嵌套HTML5的頁面,好比常常變化的等等,有一部分頁面須要原生Java與HTML5中的js進行交互操做,下面介紹一下android中HTML5的使用:javascript

一、關於HTML5種cookie

  網頁中可能會用到 用戶信息等不少參數,能夠提早把這些信息放到cookie中,能夠採用如下方法: css

 1 public static void addCookies(Context context, WebView webView, String url) {
 2 
 3 String url=「https://www.xxxx.com/xx/xx/」
 4         String protocol = "";
 5         String authority = "";
 6         try {
 7             URL urlObj = new URL(url);
 8             protocol = urlObj.getProtocol();
 9             authority = urlObj.getAuthority();
10         } catch (Exception e1) {
11             e1.printStackTrace();
12         }
13 
14         String ua = webView.getSettings().getUserAgentString();
15         webView.getSettings().setUserAgentString(Constant.PROJECT_NAME + "/" + ParamHandler.getVersion(context) + "(" + ua + "; HFWSH)");
16 
17         if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(protocol) && !TextUtils.isEmpty(authority)) {
18             if (protocol.equals("https") && authority.indexOf("liepin.com") > -1) {
19                 CookieSyncManager.createInstance(context);
20                 CookieManager cookieManager = CookieManager.getInstance();
21                 cookieManager.setAcceptCookie(true);
22                 try {
23                     List<String> data = getCookiesString();
24                     if (!ListUtils.isEmpty(data)) {
25                         for (String value : data) {
26                             cookieManager.setCookie(url, value);
27                         }
28                     }
29                     cookieManager.setCookie(url, "client_id=" + Constant.CLIENT_ID + ";path=/;domain=.XXXX.com");
30                     cookieManager.setCookie(url, "appVersion=" + Constant .VERSION + ";path=/;domain=.XXXX.com"); 
31             CookieSyncManager.getInstance().sync(); 
32         } catch (Exception e) { 
33             LogUtils.e("Exception:" + e.getMessage()); 
34         } 
35       } 
36     } 
37   }

 
 1 public List<String> getCookiesString() {
 2     ArrayList data = new ArrayList();
 3     this.clearExpired();
 4     Collection values = this.mCookies.values();
 5     Iterator var3 = values.iterator();
 6 
 7     while(var3.hasNext()) {
 8         SwiftCookie c = (SwiftCookie)var3.next();
 9         data.add(c.toCookieString());
10     }
11 
12     return data;
13 }

 

  在 mWebView.loadUrl(Url)以前添加cookie,網頁就能夠經過cookie取到相應的參數值了。html

二、關於js的安全問題

  js在4.2之前有漏洞
html5

  經過JavaScript,能夠訪問當前設備的SD卡上面的任何東西,甚至是聯繫人信息,短信等。好,咱們一塊兒來看看是怎麼出現這樣的錯誤的。java

  1,WebView添加了JavaScript對象,而且當前應用具備讀寫SDCard的權限,也就是:android.permission.WRITE_EXTERNAL_STORAGEandroid

  2,JS中能夠遍歷window對象,找到存在「getClass」方法的對象的對象,而後再經過反射的機制,獲得Runtime對象,而後調用靜態方法來執行一些命令,好比訪問文件的命令.ios

  3,再從執行命令後返回的輸入流中獲得字符串,就能夠獲得文件名的信息了。而後想幹什麼就幹什麼,好危險。核心JS代碼以下:git

 
 1 function execute(cmdArgs)  
 2 { 3 for (var obj in window) { 4 if ("getClass" in window[obj]) { 5  alert(obj); 6 return window[obj].getClass().forName("java.lang.Runtime") 7 .getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); 8  } 9  } 10 } 
 

解決方案:

  1,Android 4.2以上的系統github

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

  1. 1 class JsObject {  
    2  @JavascriptInterface 3 public String toString() { return "injectedObject"; } 4 } 5 webView.addJavascriptInterface(new JsObject(), "injectedObject"); 6 webView.loadData("", "text/html", null); 7 webView.loadUrl("javascript:alert(injectedObject.toString())"); 
  2,Android 4.2如下的系統
 

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

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

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

 

  1. public boolean onJsPrompt(WebView view, String url, String message,   
  2.     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頁面中,具體的代碼以下:

 1 javascript:(function JsAddJavascriptInterface_(){  
 2     if (typeof(window.jsInterface)!='undefined') { 3 console.log('window.jsInterface_js_interface_name is exist!!');} 4 else { 5 window.jsInterface = { 6 onButtonClick:function(arg0) { 7 return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]})); 8  }, 9 10 onImageClick:function(arg0,arg1,arg2) { 11 prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]})); 12  }, 13  }; 14  } 15 } 16 )() 

 

  說明:
 

  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) 



三、在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'   }
  二、佈局文件
 1 <?xml version="1.0" encoding="utf-8"?>  
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
 3     android:layout_width="match_parent"  
 4     android:layout_height="match_parent"  
 5     android:orientation="vertical" >  
 6   
 7     <!-- button 演示Java調用web -->  
 8     <Button   
 9         android:id="@+id/button"  
10         android:layout_width="match_parent"  
11         android:text="@string/button_name"  
12         android:layout_height="48dp"  
13         />  
14       
15     <!-- webview 演示web調用Java -->  
16     <com.github.lzyzsd.jsbridge.BridgeWebView  
17         android:id="@+id/webView"  
18         android:layout_width="match_parent"  
19         android:layout_height="match_parent" >  
20      </com.github.lzyzsd.jsbridge.BridgeWebView>  
21   
22 </LinearLayout>  

三、java代碼

 1 //加載服務器網頁  
 2         webView.loadUrl("https://www.baidu.com");  
 3   
 4         //必須和js同名函數。  
 5         webView.registerHandler("submitFromWeb", new BridgeHandler() {  
 6   
 7             @Override  
 8             public void handler(String data, CallBackFunction function) {  
 9   
10                 String str ="html返回給java的數據:" + data;  
11               
12                 makeText(MainActivity.this, str, LENGTH_SHORT).show();  
13   
14                 Log.i(TAG, "handler = submitFromWeb, data from web = " + data);  
15                 function.onCallBack( str + ",Java通過處理後:"+ str.substring(0,5));  
16             }  
17   
18         });  
19         //模擬用戶獲取本地位置  
20         User user = new User();  
21         Location location = new Location();  
22         location.address = "xxx";  
23         user.location = location;  
24         user.name = "Bruce";  
25   
26         webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {  
27             @Override  
28             public void onCallBack(String data) {  
29                 makeText(MainActivity.this, "網頁在獲取你的信息", LENGTH_SHORT).show();  
30   
31             }  
32         });  
33   
34         webView.send("hello");  
1  webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {  
2   
3                 @Override  
4                 public void onCallBack(String data) {  
5                     // TODO Auto-generated method stub  
6                     Log.i(TAG, "reponse data from js " + data);  
7                 }  
8   
9             });  

js調用

 1 var str1 = document.getElementById("text1").value;  
 2            var str2 = document.getElementById("text2").value;  
 3   
 4            //調用本地java方法  
 5            window.WebViewJavascriptBridge.callHandler(  
 6                'submitFromWeb'  
 7                , {'param': str1}  
 8                , function(responseData) {  
 9                    document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData  
10                }  
11            );  
12 
13 //註冊事件監聽 
14  document.addEventListener(  
15                    'WebViewJavascriptBridgeReady'  
16                    , function() {  
17                        callback(WebViewJavascriptBridge)  
18                    },  
19                    false  
20                );  
21 
22 //註冊回調函數,第一次鏈接時調用 初始化函數 
23 connectWebViewJavascriptBridge(function(bridge) {  
24            bridge.init(function(message, responseCallback) {  
25                console.log('JS got a message', message);  
26                var data = {  
27                    'Javascript Responds': 'Wee!'  
28                };  
29                console.log('JS responding with', data);  
30                responseCallback(data);  
31            });  
32   
33            bridge.registerHandler("functionInJs", function(data, responseCallback) {  
34                document.getElementById("show").innerHTML = ("data from Java: = " + data);  
35                var responseData = "Javascript Says Right back aka!";  
36                responseCallback(responseData);  
37            });  
38        })  

 

 



 

四、關於webView的優化

  一、設置WebView 緩存模式

 1     private void initWebView() {  
 2               
 3             mWebView.getSettings().setJavaScriptEnabled(true);  
 4             mWebView.getSettings().setRenderPriority(RenderPriority.HIGH);  
 5             mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);  //設置 緩存模式  
 6             // 開啓 DOM storage API 功能  
 7             mWebView.getSettings().setDomStorageEnabled(true);  
 8             //開啓 database storage API 功能  
 9             mWebView.getSettings().setDatabaseEnabled(true);   
10             String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME;  
11     //      String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME;  
12             Log.i(TAG, "cacheDirPath="+cacheDirPath);  
13             //設置數據庫緩存路徑  
14             mWebView.getSettings().setDatabasePath(cacheDirPath);  
15             //設置  Application Caches 緩存目錄  
16             mWebView.getSettings().setAppCachePath(cacheDirPath);  
17             //開啓 Application Caches 功能  
18             mWebView.getSettings().setAppCacheEnabled(true); 

  二、清除緩存

 1  /** 
 2          * 清除WebView緩存 
 3          */  
 4         public void clearWebViewCache(){  
 5               
 6             //清理Webview緩存數據庫  
 7             try {  
 8                 deleteDatabase("webview.db");   
 9                 deleteDatabase("webviewCache.db");  
10             } catch (Exception e) {  
11                 e.printStackTrace();  
12             }  
13               
14             //WebView 緩存文件  
15             File appCacheDir = new File(getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME);  
16             Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath());  
17               
18             File webviewCacheDir = new File(getCacheDir().getAbsolutePath()+"/webviewCache");  
19             Log.e(TAG, "webviewCacheDir path="+webviewCacheDir.getAbsolutePath());  
20               
21             //刪除webview 緩存目錄  
22             if(webviewCacheDir.exists()){  
23                 deleteFile(webviewCacheDir);  
24             }  
25             //刪除webview 緩存 緩存目錄  
26             if(appCacheDir.exists()){  
27                 deleteFile(appCacheDir);  
28             }  
29         }  

  三、在使用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中公用了。

 1 webView.setWebViewClient(new WebViewClient() {  
 2   
 3             @Override  
 4             public WebResourceResponse shouldInterceptRequest(WebView view, String url) {  
 5                 WebResourceResponse response = null;  
 6                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){  
 7                     response = super.shouldInterceptRequest(view,url);  
 8                     if (url.contains("xxxxx.png")){  
 9                         try {  
10                             response = new WebResourceResponse("image/png","UTF-8",getAssets().open("xxxxx.png"));  
11                         } catch (IOException e) {  
12                             e.printStackTrace();  
13                         }  
14                     }  
15                 }  
16 //                return super.shouldInterceptRequest(view, url);  
17                 return  response;  
18             }  
19   
20             @TargetApi(Build.VERSION_CODES.LOLLIPOP)  
21             @Override  
22             public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {  
23                 WebResourceResponse response = null;  
24                 response =  super.shouldInterceptRequest(view, request);  
25                 if (url.contains("xxxxx.png")){  
26                     try {  
27                         response = new WebResourceResponse("image/png","UTF-8",getAssets().open("xxxxx.png"));  
28                     } catch (IOException e) {  
29                         e.printStackTrace();  
30                     }  
31                 }  
32                 return response;  
33             }  
34 }  
相關文章
相關標籤/搜索