zoukankan      html  css  js  c++  java
  • Android-Java和HTML5交互-混合开发-优化

    现在很多的 APP中会嵌套HTML5的页面,比如经常变化的等等,有一部分页面需要原生Java与HTML5中的js进行交互操作,下面介绍一下android中HTML5的使用:

    1、关于HTML5种cookie

      网页中可能会用到 用户信息等很多参数,可以提前把这些信息放到cookie中,可以采用以下方法: 

     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取到相应的参数值了。

    2、关于js的安全问题

      js在4.2以前有漏洞

      通过JavaScript,可以访问当前设备的SD卡上面的任何东西,甚至是联系人信息,短信等。好,我们一起来看看是怎么出现这样的错误的。

      1,WebView添加了JavaScript对象,并且当前应用具有读写SDCard的权限,也就是:android.permission.WRITE_EXTERNAL_STORAGE

      2,JS中可以遍历window对象,找到存在“getClass”方法的对象的对象,然后再通过反射的机制,得到Runtime对象,然后调用静态方法来执行一些命令,比如访问文件的命令.

      3,再从执行命令后返回的输入流中得到字符串,就可以得到文件名的信息了。然后想干什么就干什么,好危险。核心JS代码如下:

    
    
     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以上的系统

      在Android 4.2以上的,google作了修正,通过在Java的远程方法上面声明一个@JavascriptInterface,如下面代码:

    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) 



    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、布局文件
     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>  

    3、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        })  
     



    
    

    4、关于webView的优化

      1、设置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); 

      2、清除缓存

     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         }  

      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中公用了。

     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 }  
     
    
    
     
  • 相关阅读:
    HDU 4348 To the moon(可持久化线段树)
    HDU 5875 Function 大连网络赛 线段树
    HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)
    HDU 5876 大连网络赛 Sparse Graph
    HDU 5701 中位数计数 百度之星初赛
    CodeForces 708B Recover the String
    Java实现 蓝桥杯 算法提高 套正方形(暴力)
    ASP.NET生成验证码
    ASP.NET生成验证码
    ASP.NET生成验证码
  • 原文地址:https://www.cnblogs.com/xunzhi/p/6023190.html
Copyright © 2011-2022 走看看