用户登录
用户注册

分享至

详解android与HTML混合开发总结

  • 作者: 风采依依
  • 来源: 51数据库
  • 2021-09-04

现在很多的 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;  
             }  
 }  

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

软件
前端设计
程序设计
Java相关