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 }  
     
    
    
     
  • 相关阅读:
    sencha touch 入门系列 (四)sencha touch 新建项目目录结构解析
    sencha touch 入门系列 (三)sencha touch 项目创建
    sencha touch 入门系列 (二)sencha touch 开发准备
    sencha touch 入门系列 (一)sencha touch 简介
    Android Weak Handler:可以避免内存泄漏的Handler库
    Android 中的冷启动和热启动
    Android4.0以上版本比较靠谱的获取手机屏幕分辨率的方法
    Android studio 获取每次编译apk时的日期
    android瓦片地图技术研究
    Android中的各种访问权限Permission含义
  • 原文地址:https://www.cnblogs.com/xunzhi/p/6023190.html
Copyright © 2011-2022 走看看