webview加载h5

loadUrl
           //加载assets目录下的test.html文件 webView.loadUrl("file:///android_asset/test.html"); 
      //加载网络资源(注意要加上网络权限)
webView.loadUrl("http://blog.csdn.net");

setWebViewClient  
  1)默认 跳转手机浏览器加载h5页面
  2)设置WebViewClient在本WebView中显示。注意:并不需要覆盖 shouldOverrideUrlLoading 方法,设置了WebViewClient,则就不会调用系统浏览
 3)实现对网页中超链接的拦截 
webView.setWebViewClient(new WebViewClient(){ @Override public boolean   shouldOverrideUrlLoading(WebView view, String url) { if("http://www.jikedaohang.com/".equals(url))                   { view.loadUrl("https://www.baidu.com/"); } return true; } });
shouldOverrideUrlLoading返回值 
true:则在打开新的url时WebView就不会再加载url
false:则系统就认为上层没有做处理,接下来还是会继续加载url
 4)加载网页时替换某个资源
在shouldInterceptRequest中进行图片替换
shouldInterceptRequest有两个重载: 
       ①public WebResourceResponse shouldInterceptRequest (WebView view, String url) 【已过时】 
        ②public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request) 

两种方法主要是第二个参数的不同,WebResourceRequest 将能够获取更多的信息,提供了getUrl(),getMethod,getRequestHeaders等方法第一种回调:

  
mWebView.setWebViewClient(new WebViewClient(){
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
WebResourceResponse response = null ;
if (url.contains( "logo" )) {
try {
InputStream logo = getAssets().open( "logo.png" );
response = new WebResourceResponse( "image/png" , "UTF-8" , logo);
} catch (IOException e) {
e.printStackTrace();
} }
return response;

}

});
     5)设置开始加载网页、加载完成、加载错误时处理
         webView.setWebViewClient(new WebViewClient() {
@Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); // 开始加载网页时处理 如:显示"加载提示" 的加载对话框 ... } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 网页加载完成时处理 如:让 加载对话框 消失 ... } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); // 加载网页失败时处理 如:提示失败,或显示新的界面 ... } });  

      5)处理https请求,为WebView处理ssl证书设置WebView默认是不处理https请求的,需要在WebViewClient子类中重写父类的onReceivedSslError函数

    webView.setWebViewClient(new WebViewClient() {    
    
        @Override  
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {  
            handler.proceed();  // 接受信任所有网站的证书  
            // handler.cancel();   // 默认操作 不处理  
            // handler.handleMessage(null);  // 可做其他处理  
        }   
    });   
    setWebChromeClient
    用来处理Javascript的对话框、网站图标、网站标题以及网页加载进度等

    1)显示页面加载进度在WebChromeClient子类中重写父类的onProgressChanged

    函数,progress表示当前页面加载的进度,为1至100的整数

    webView.setWebChromeClient(new WebChromeClient() {    
    
        public void onProgressChanged(WebView view, int progress) {    
            setTitle("页面加载中,请稍候..." + progress + "%");    
            setProgress(progress * 100);    
    
            if (progress == 100) {    
                //... 
            }    
        }    
    }); 
      2)加快HTML网页加载完成速度(默认情况html代码下载到WebView后,webkit
    开始解析网页各个节点,发现有外部样式文件或者外部脚本文件时,会异步发起网络
    请求下载文件,但如果在这之前也有解析到image节点,那势必也会发起网络请求下
    载相应的图片。在网络情况较差的情况下,过多的网络请求就会造成带宽紧张,影响
    到css或js文件加载完成的时间,造成页面空白loading过久。解决的方法就是告诉
    WebView先不要自动加载图片,等页面finish后再发起图片加载。)
    //1.首先在WebView初始化时添加如下代码
    if(Build.VERSION.SDK_INT >= 19) {  
    /*对系统API在19以上的版本作了兼容。因为4.4以上系统在onPageFinished时再恢复图片加载时,如果存在多张图片引用的是相同的src时,会只有一个image标签得到加载,因而对于这样的系统我们就先直接加载。*/        webView.getSettings().setLoadsImagesAutomatically(true);  
        } else {  
            webView.getSettings().setLoadsImagesAutomatically(false);  
        }  
    
    //2.在WebView的WebViewClient子类中重写onPageFinished()方法添加如下代码: 
     @Override  
    public void onPageFinished(WebView view, String url) {  
        if(!webView.getSettings().getLoadsImagesAutomatically()) {  
            webView.getSettings().setLoadsImagesAutomatically(true);  
        }  
    }  

    setDownloadListener 

    通常webview渲染的界面中含有可以下载文件的链接,点击该链接后,应该开始执行下载的操作并保存文件到本地中。

    1 ) 创建DownloadListener

    class MyDownloadListenter implements DownloadListener{
          @Override
          public void onDownloadStart(String url, String userAgent,String contentDisposition, String mimetype, long contentLength) {
              //下载任务...,主要有两种方式
              //(1)自定义下载任务
              //(2)调用系统的download的模块
              Uri uri = Uri.parse(url);
              Intent intent = new Intent(Intent.ACTION_VIEW, uri);
              startActivity(intent);
          }
    }
         2)给webview加入监听
    webview.setDownloadListener(new MyDownloadListenter());
    goBack()

    返回上一浏览页面,通过重写onKeyDown方法实现点击返回键返回上一浏览页面而非退出程序

    public boolean onKeyDown(int keyCode, KeyEvent event) {  
    //其中webView.canGoBack()在webView含有一个可后退的浏览记录时返回true
    
            if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {       
                webView.goBack();       
                return true;       
            }       
            return super.onKeyDown(keyCode, event);       
        }
    }

    WebSettings配置
    (1)支持js

    settings.setJavaScriptEnabled(true);

    (2)设置缓存方式,主要有以下几种: 
    LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据。 
    LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。 
    LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式。 
    LOAD_NO_CACHE: 不使用缓存,只从网络获取数据。 
    LOAD_CACHE_ELSE_NETWORK:只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。

    settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

    (3)开启DOM storage API功能(HTML5 提供的一种标准的接口,主要将键值对存储在本地,在页面加载完毕后可以通过 JavaScript 来操作这些数据。)

    settings.setDomStorageEnabled(true);

    (4)设置数据库缓存路径

    settings.setDatabasePath(cacheDirPath);

    (5)设置Application Caches缓存目录

    settings.setAppCachePath(cacheDirPath);

    (6)设置默认编码

    settings.setDefaultTextEncodingName(“utf-8”);

    (7)将图片调整到适合webview的大小

    settings.setUseWideViewPort(false);

    (8)支持缩放

    settings.setSupportZoom(true);

    (9)支持内容重新布局

    settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);

    (10)多窗口

    settings.supportMultipleWindows();

    (11)设置可以访问文件

    settings.setAllowFileAccess(true);

    (12)当webview调用requestFocus时为webview设置节点

    settings.setNeedInitialFocus(true);

    (13)设置支持缩放

    settings.setBuiltInZoomControls(true);

    (14)支持通过JS打开新窗口

    settings.setJavaScriptCanOpenWindowsAutomatically(true);

    (15)缩放至屏幕的大小

    settings.setLoadWithOverviewMode(true);

    (16)支持自动加载图片

    settings.setLoadsImagesAutomatically(true);

    WebChoromeClient的回调方法列表

    辅助WebView处理Javascript的对话框、网站图标、网站标题以及网页加载进度等

    (1)监听网页加载进度

    onProgressChanged(WebView view, int newProgress)

    (2)监听网页标题 : 比如百度页面的标题是“百度一下,你就知道”

    onReceivedTitle(WebView view, String title)

    (3)监听网页图标

    onReceivedIcon(WebView view, Bitmap icon)

    Java和JavaScript互调

    addJavascriptInterface方式实现与本地js交互(存在漏洞)
    • 首先是JS的一段代码:
    function javaCallJs(arg){
             document.getElementById("content").innerHTML =
                 ("欢迎:"+arg );
        }

    • 然后是在java中调用JS中的方法
    webView.loadUrl("javascript:javaCallJs("+"'"+name+"'"+")");

    JS调java

    • 配置Javascript接口
    webView.addJavascriptInterface(new JSInterface (),"Android");
    
    • 实现Javascript接口类
    class JSInterface {
        @JavascriptInterface
         public void showToast(String arg){
                       Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show();
         }
    }

    • JS中调用java代码
    <input type="button" value="点击Android被调用" onclick="window.Android.showToast('JS中传来的参数')"/>

    window.Android.showToast(‘JS中传来的参数’)”中的”Android”即
    addJavascriptInterface()中指定的,并且JS向java传递了参数,类型
    为String。而showToast(String arg)会以Toast的形式弹出此参数。


    安全
        
    • 任意代码执行漏洞
    • 密码明文存储漏洞
    • 域控制不严格漏洞

    漏洞产生原因

    JS调用Android的其中一个方式是通过addJavascriptInterface接口进行对象映射:
    java反射拿到sd卡里的东西
    解决方案
    Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击
    在Android 4.2版本之前采用拦截prompt()进行漏洞修复。
    • 继承 WebView ,重写 addJavascriptInterface 方法,然后在内部自己维护一个对象映射关系的 Map;将需要添加的 JS 接口放入该Map中
    • 每次当 WebView 加载页面前加载一段本地的 JS 代码,原理是:
    让JS调用一Javascript方法:该方法是通过调用prompt()把JS中的信息(含特定标识,方法名称等)传递到Android端;
    在Android的onJsPrompt()中 ,解析传递过来的信息,再通过反射机制调用Java对象的方法,这样实现安全的JS调用Android代码。
    关于Android返回给JS的值:可通过prompt()把Java中方法的处理结果返回到Js中 代码如下
        
    javascript:(function JsAddJavascriptInterface_(){ // window.jsInterface 表示在window上声明了一个Js对象 // jsInterface = 注册的对象名 // 它注册了两个方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2) // 如果有返回值,就添加上return if (typeof(window.jsInterface)!='undefined') { console.log('window.jsInterface_js_interface_name is exist!!');} else { window.jsInterface = { // 声明方法形式:方法名: function(参数) onButtonClick:function(arg0) { // prompt()返回约定的字符串 // 该字符串可自己定义 // 包含特定的标识符MyApp和 JSON 字符串(方法名,参数,对象名等) return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]})); }, onImageClick:function(arg0,arg1,arg2) { return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]})); }, }; } } )() // 当JS调用 onButtonClick() 或 onImageClick() 时,就会回调到Android中的 onJsPrompt () // 我们解析出方法名,参数,对象名 // 再通过反射机制调用Java对象的方法

         

    searchBoxJavaBridge_接口引起远程代码执行漏洞

    漏洞产生原因

    • 在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_的Js接口给 WebView 添加一个JS映射对象:searchBoxJavaBridge_对象
    • 该接口可能被利用,实现远程任意代码。

    解决方案

    删除searchBoxJavaBridge_接口

    // 通过调用该方法删除接口
    removeJavascriptInterface();

    密码明文存储漏洞

          

    问题分析

    WebView默认开启密码保存功能 :

    mWebView.setSavePassword(true)
    • 开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码;
    • 如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险
    解决方案

    关闭密码保存提醒

    WebSettings.setSavePassword(false)

    域控制不严格漏洞

    setAllowFileAccess问题
    // 设置是否允许 WebView 使用 File 协议 webView.getSettings().setAllowFileAccess();     
    // 默认设置为true,即允许在 File 域下执行任意 JavaScript 代码

    使用 file 域加载的 js代码能够使用进行同源策略跨域访问,从而导致隐私信息泄露

    如果不允许使用 file 协议,则不会存在上述的威胁;

    webView.getSettings().setAllowFileAccess(true);     
    

    但同时也限制了 WebView 的功能,使其不能加载本地的 html 文件,移动版的 Chrome 默认禁止加载 file 协议的文件

          

    解决方案

    对于不需要使用 file 协议的应用,禁用 file 协议;
    setAllowFileAccess(false); 
    对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。setAllowFileAccess(true);
    // 禁止 file 协议加载 JavaScript
    if (url.startsWith("file://") {
        setJavaScriptEnabled(false);
    } else {
        setJavaScriptEnabled(true);
    }
    问题:setAllowFileAccessFromFileURLs()
        
    // 设置是否允许通过 file url 加载的 Js代码读取其他的本地文件
    webView.getSettings().setAllowFileAccessFromFileURLs(true);
    // 在Android 4.1前默认允许
    // 在Android 4.1后默认禁止
    

    AllowFileAccessFromFileURLs()设置为 true 时,攻击者的JS代码为:

    <script>
    function loadXMLDoc()
    {
        var arm = "file:///etc/hosts";
        var xmlhttp;
        if (window.XMLHttpRequest)
        {
            xmlhttp=new XMLHttpRequest();
        }
        xmlhttp.onreadystatechange=function()
        {
            //alert("status is"+xmlhttp.status);
            if (xmlhttp.readyState==4)
            {
                  console.log(xmlhttp.responseText);
            }
        }
        xmlhttp.open("GET",arm);
        xmlhttp.send(null);
    }
    loadXMLDoc();
    </script>
    
    // 通过该代码可成功读取 /etc/hosts 的内容数据
    

    解决方案设置setAllowFileAccessFromFileURLs(false);

    当设置成为 false 时,上述JS的攻击代码执行会导致错误,表示浏览器禁止从 file url 中的 javascript 读取其它本地文件。

    问题:setJavaScriptEnabled()
        
    // 设置是否允许 WebView 使用 JavaScript(默认是不允许)
    webView.getSettings().setJavaScriptEnabled(true);  
    
    // 但很多应用(包括移动浏览器)为了让 WebView 执行 http 协议中的 JavaScript,都会主动设置为true,不区别对待是非常危险的。
    

    即使把setAllowFileAccessFromFileURLs()setAllowUniversalAccessFromFileURLs()都设置为 false,通过 file URL 加载的 javascript 仍然有方法访问其他的本地文件:符号链接跨源攻击

        
    // 设置是否允许 WebView 使用 JavaScript(默认是不允许)
    webView.getSettings().setJavaScriptEnabled(true);  
    
    // 但很多应用(包括移动浏览器)为了让 WebView 执行 http 协议中的 JavaScript,都会主动设置为true,不区别对待是非常危险的。
    

    即使把setAllowFileAccessFromFileURLs()setAllowUniversalAccessFromFileURLs()都设置为 false,通过 file URL 加载的 javascript 仍然有方法访问其他的本地文件:符号链接跨源攻击

    解决方案

    对于不需要使用 file 协议的应用,禁用 file 协议;
    // 禁用 file 协议;
    setAllowFileAccess(false);
    setAllowFileAccessFromFileURLs(false);
    setAllowUniversalAccessFromFileURLs(false);
    对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。
    // 需要使用 file 协议
    setAllowFileAccess(true);
    setAllowFileAccessFromFileURLs(false);
    setAllowUniversalAccessFromFileURLs(false);

    // 禁止 file 协议加载 JavaScript
    if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
    } else {
    setJavaScriptEnabled(true);
    }


    优化加载速度
    腾讯出优化加载速度h5  https://github.com/Tencent/VasSonic
        
    1.提高渲染的优先级

    // webSettings.setRenderPriority(RenderPriority.HIGH);

    2、使把图片加载放在最后来加载渲染 

     

    webSettings.setBlockNetworkImage(true); 

    3,使用硬件加速,该功能在Android 3.0 (API level 11)才加入。

    参考:http://developer.android.com/guide/topics/graphics/hardware-accel.html 

    4、开启缓存

    // 开启H5(APPCache)缓存功能

    webSettings.setAppCacheEnabled(true);

    // webView数据缓存分为两种:AppCache和DOM Storage(Web Storage)。

    // 开启 DOM storage 功能

    webSettings.setDomStorageEnabled(true);

    // 应用可以有数据库

    webSettings.setDatabaseEnabled(true);

    // 根据网络连接情况,设置缓存模式,

    if (UtilTools.isConnected()) {

    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);// 根据cache-control决定是否从网络上取数据

    } else {

    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);// 先查找缓存,没有的情况下从网络获取。

    }

    // 可以读取文件缓存(manifest生效)

    webSettings.setAllowFileAccess(true);


    2.换内核  
    1).腾讯浏览服务基于腾讯X5内核解决方案(包括内核和云服务),能够有效解决传统移动web技术面临的普遍问题,同时能极大扩展应用(Hybrid App)内浏览场景的服务能力。SDK提供的JAR包约250K 速度快、省流量等 方便快捷的替换原生的。
    app启动后需要初始化,有时候X5内核没有初始化完成就需要打开H5页面就会造成页面打不开。某些机型又出现H5页面无法打开
    WebVie具体可以参考:http://x5.tencent.com/tbs/guide/sdkInit.html
    2).SuperWebview
    SuperWebview是APICloud官方推出的另一项重量级API生态产品
    SuperWebview查看官方文档,更倾向于web开发,不能直接替换WebView
    3).Crosswalk
    一款开源的Web引擎,其基于 Chromium/Blink
    的应用运行环境,对于混合开发的轻量级应用尤为受欢迎。 它允许Web开发者将他们的应用打包成系统的应用安装包,获得与本地应用一致的体验。
    同时也支持多个应用同时使用一个Crosswalk库的共享模式,仅当应用第一次启动并且发现系统还没有相应的Crosswalk库时才提示用户下载安装
    目前是大多数情况下开发者将Crosswalk直接嵌入到应用本身。在这种嵌入模式下Web应用开发者可以完全控制Crosswalk的更新。
    缺点:apk激增20~50M体积 
    官网:https://crosswalk-project.org/
    接入遇见的坑:
    *1.最低版本要求

    根据http://blog.csdn.net/sslinp/article/details/51607237文章介绍,最低版本是14,我的项目中就是14,于是就按照文章介绍集成,结果一build,报如下错误:

    Error:Execution failed for task:app:processArmv7DebugManifest.> Manifest merger failed with multiple errors, see logs

    查看log发现是我集成的版本要求最低版本是16。
    点击查看最新Crosswalk版本。如下图我集成的是目前最新的版本

         *2.适配不同手机处理器版本 

    不同用户手机处理器下载不同版本。即在build.gradle中的android闭包中声明如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    productFlavors {
     
        armv7 {
     
               ndk {
     
                      abiFilters "armeabi-v7a"
     
               }
     
          }
     
       x86 {
     
            ndk {
     
               abiFilters "x86"
     
            }
     
          }
     
       }


    深度优化
    http://wonko.com/post/painless_javascript_lazy_loading_with_lazyload

    ©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页