文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基础巩固(五)Android通过WebView与Js交互

2023-09-02 18:57

关注

文章目录

简介

WebView是一个基于webkit引擎、展现web页面的控件。Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。

webview的作用在于:

WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。

WebView基本使用

常用方法

WebView的生命周期 / 状态切换

//激活WebView为活跃状态,能正常执行网页的响应webView.onResume()//当页面被失去焦点被切换到后台不可见状态,需要执行onPause//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。webView.onPause()//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。webView.pauseTimers()//恢复pauseTimers状态webView.resumeTimers()//销毁Webview//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview//但是注意:webview调用destory时,webview仍绑定在Activity上//这是由于自定义webview构建时传入了该Activity的context对象//因此需要先从父容器中移除webview,然后再销毁webview:rootLayout.removeView(webView); webView.destroy();

关于前进 / 后退网页

//是否可以后退Webview.canGoBack() //后退网页Webview.goBack()//是否可以前进                     Webview.canGoForward()//前进网页Webview.goForward()//以当前的index为起始点前进或者后退到历史记录中指定的steps//如果steps为负数则为后退,正数则为前进Webview.goBackOrForward(intsteps) 

常见用法:Back键控制网页后退

public boolean onKeyDown(int keyCode, KeyEvent event) {if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { mWebView.goBack();return true;}return super.onKeyDown(keyCode, event);}

清除缓存数据

//清除网页访问留下的缓存//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.Webview.clearCache(true);//清除当前webview访问的历史记录//只会webview访问历史记录里的所有记录除了当前访问记录Webview.clearHistory()//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据Webview.clearFormData()

常用类

WebSettings类

作用:对WebView进行配置和管理
配置步骤如下:

  1. 添加网络访问权限
<uses-permission android:name="android.permission.INTERNET"/>
  1. 生成一个WebView组件(有两种方式)
//方式1:直接在在Activity中生成WebView webView = new WebView(this)//方法2:在Activity的layout文件里添加webview控件:WebView webview = (WebView) findViewById(R.id.webView1);
  1. 进行配置-利用WebSetting子类
//声明WebSettings子类WebSettings webSettings = webView.getSettings();//如果访问的页面中要与Javascript交互,则webview必须设置支持JavascriptwebSettings.setJavaScriptEnabled(true);  //支持插件webSettings.setPluginsEnabled(true); //设置自适应屏幕,两者合用webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小 webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小//缩放操作webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件//其他细节操作webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存 webSettings.setAllowFileAccess(true); //设置可以访问文件 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式

常见用法:设置WebView缓存

//优先使用缓存:     WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);         //缓存模式如下:        //LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据        //LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。        //LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.        //LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。//不使用缓存: WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);

结合使用(离线加载)

if (NetStatusUtil.isConnected(getApplicationContext())) {    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。} else {    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载}webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能webSettings.setDatabaseEnabled(true);   //开启 database storage API 功能webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录

注意: 每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()

WebViewClient类

WebViewClient用于处理各种通知事件和请求事件。

1、重载url加载:shouldOverrideUrlLoading()
作用:打开网页时不调用系统浏览器, 而是在本WebView中显示;在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。

//步骤1. 定义Webview组件Webview webview = (WebView) findViewById(R.id.webView1);//步骤2. 选择加载方式  //方式1. 加载一个网页:  webView.loadUrl("http://www.google.com/");  //方式2:加载apk包中的html页面  webView.loadUrl("file:///android_asset/test.html");  //方式3:加载手机本地的html页面   webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");//步骤3. 复写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示    webView.setWebViewClient(new WebViewClient(){      @Override      public boolean shouldOverrideUrlLoading(WebView view, String url) {          view.loadUrl(url);      return true;      }  });

2、当页面开始加载:onPageStarted()

作用:开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。

 webView.setWebViewClient(new WebViewClient(){      @Override      public void  onPageStarted(WebView view, String url, Bitmap favicon) {         //设定加载开始的操作      }  });

3、当页面加载完毕:onPageFinished()
作用:在页面加载结束时调用,我们可以关闭loading条,切换程序动作。

    webView.setWebViewClient(new WebViewClient(){      @Override      public void onPageFinished(WebView view, String url) {         //设定加载结束的操作      }  });

4、当加载资源
作用:在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。

    webView.setWebViewClient(new WebViewClient(){      @Override      public boolean onLoadResource(WebView view, String url) {         //设定加载资源的操作      }  });

5、当接收到错误
作用:当加载页面的服务出现错误时调用,比如404.

App里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面,即webview如何加载一个本地的页面

//步骤1:写一个html文件(error_handle.html),用于出错时展示给用户看的提示页面//步骤2:将该html文件放置到代码根目录的assets文件夹下//步骤3:复写WebViewClient的onRecievedError方法//该方法传回了错误码,根据错误类型可以进行不同的错误分类处理    webView.setWebViewClient(new WebViewClient(){      @Override      public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){switch(errorCode)                {                case HttpStatus.SC_NOT_FOUND:                    view.loadUrl("file:///android_assets/error_handle.html");                    break;                }            }        });

6、当接收到SSL错误:onReceivedSslError()
作用:处理https请求
webView默认是不处理https请求的,页面显示空白,需要进行如下设置:

webView.setWebViewClient(new WebViewClient() {            @Override            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {                handler.proceed();    //表示等待证书响应        // handler.cancel();      //表示挂起连接,为默认方式        // handler.handleMessage(null);    //可做其他处理        }        });    

WebChromeClient类

webview.setWebChromeClient(new WebChromeClient(){      @Override      public void onProgressChanged(WebView view, int newProgress) {          if (newProgress < 100) {              String progress = newProgress + "%";              progress.setText(progress);            } else {        }    });

2、当收到web页面标题:
作用:获取Web页中的标题
每个网页的页面都有一个标题,比如www.baidu.com这个页面的标题即“百度一下,你就知道”,那么如何知道当前webview正在加载的页面的title并进行设置呢?

webview.setWebChromeClient(new WebChromeClient(){    @Override    public void onReceivedTitle(WebView view, String title) {       titleview.setText(title)}

注意事项

如何避免WebView内存泄露

  1. 不在xml中定义 Webview ,而是在需要的时候在Activity中创建,并且Context使用 getApplicationgContext()
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);        mWebView = new WebView(getApplicationContext());        mWebView.setLayoutParams(params);        mLayout.addView(mWebView);
  1. 在 Activity 销毁( WebView )的时候,先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。
@Override    protected void onDestroy() {        if (mWebView != null) {            mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);            mWebView.clearHistory();            ((ViewGroup) mWebView.getParent()).removeView(mWebView);            mWebView.destroy();            mWebView = null;        }        super.onDestroy();    }

使用案例

目标:实现显示“www.baidu.com”、获取其标题、提示加载开始 & 结束和获取加载进度
具体实现:

添加网络访问权限
manifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

通过WebView使得native与Js交互

Android与JS通过WebView互相调用方法,实际上是:

  1. Android去调用JS的代码
  2. JS去调用Android的代码

两者沟通的桥梁是WebView

Android通过WebView调用JS代码

Android调用JS代码的方法有2种:

  1. 通过WebView的loadUrl()
  2. 通过WebView的evaluateJavascript()

在这里插入图片描述

方式1:通过WebView的loadUrl()

实例介绍:点击Android按钮,即调用WebView JS(文本名为javascript)中callJS()

具体实现步骤如下:

  1. 将需要调用的js代码以.html格式放到src/main/

注:此处调用的是本地的js代码,实际使用时更多调用的是远程的js代码,只需要把加载js的路径改为url即可

// 文本名:javascriptDOCTYPE html><html>   <head>      <meta charset="utf-8">      <title>Carson_Hotitle>      // JS代码     <script>// Android需要调用的方法   function callJS(){      alert("Android调用了JS的callJS方法");   }script>   head>html>
  1. 在Android里通过WebView设置调用JS代码

特别注意:js代码调用一定要在onPageFinished()回调之后才能调用,否则不会调用。
onPageFinished()属于WebViewClient类的方法,主要在页面加载结束时调用

 public class MainActivity extends AppCompatActivity {    WebView mWebView;    Button button;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mWebView =(WebView) findViewById(R.id.webview);        WebSettings webSettings = mWebView.getSettings();        // 设置与Js交互的权限        webSettings.setJavaScriptEnabled(true);        // 设置允许JS弹窗        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);        // 先载入JS代码        // 格式规定为:file:///android_asset/文件名.html        mWebView.loadUrl("file:///android_asset/javascript.html");        button = (Button) findViewById(R.id.button);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // 通过Handler发送消息                mWebView.post(new Runnable() {                    @Override                    public void run() {                        // 注意调用的JS方法名要对应上                        // 调用javascript的callJS()方法                        mWebView.loadUrl("javascript:callJS()");                    }                });}        });        // 由于设置了弹窗检验调用结果,所以需要支持js对话框        // webview只是载体,内容的渲染需要使用webviewChromClient类去实现        // 通过设置WebChromeClient对象处理JavaScript的对话框        //设置响应js 的Alert()函数        mWebView.setWebChromeClient(new WebChromeClient() {            @Override            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {                AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);                b.setTitle("Alert");                b.setMessage(message);                b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        result.confirm();                    }                });                b.setCancelable(false);                b.create().show();                return true;            }        });    }}

方式2:通过WebView的evaluateJavascript()

这种方式要比使用loadUrl()更高效,更简洁一点儿,因为该方法的执行不会使得页面刷新,而loadUrl实际上我们调用了HTML文件,使用的是HTML中的js代码操作。

具体使用只需要将第一种方法中的loadUrl()换成下面的方法即可;

// 只需要将第一种方法的loadUrl()换成下面该方法即可    mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {        @Override        public void onReceiveValue(String value) {            //此处为 js 返回的结果        }    });}

JS通过WebView调用 Android 代码

对于JS调用Android代码的方法有3种:

  1. 通过webview的addJavascriptInterface()进行对象映射
  2. 通过WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
  3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

在这里插入图片描述

通过 WebView的addJavascriptInterface()进行对象映射

步骤1:定义一个与JS对象映射关系的Android类:AndroidtoJs

// 继承自Object类public class AndroidtoJs extends Object {    // 定义JS需要调用的方法    // 被JS调用的方法必须加入@JavascriptInterface注解    @JavascriptInterface    public void hello(String msg) {        System.out.println("JS调用了Android的hello方法");    }}

步骤2:将需要调用的JS代码以.html格式放到src/main/assets文件夹里

DOCTYPE html><html>   <head>      <meta charset="utf-8">      <title>Carsontitle>        <script>                          function callAndroid(){        // 由于对象映射,所以调用test对象等于调用Android映射的对象,这里test可能有些突兀,实际上是下边activity中我们声明的AndroidtoJs类型的对象名字            test.hello("js调用了android中的hello方法");         }      script>   head>   <body>      //点击按钮则调用callAndroid函数      <button type="button" id="button1" onclick="callAndroid()">button>   body>html>

步骤3:在Android里通过WebView设置Android类与JS代码的映射addJavascriptInterface()

public class MainActivity extends AppCompatActivity {    WebView mWebView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mWebView = (WebView) findViewById(R.id.webview);        WebSettings webSettings = mWebView.getSettings();        // 设置与Js交互的权限        webSettings.setJavaScriptEnabled(true);        // 通过addJavascriptInterface()将Java对象映射到JS对象        //参数1:Javascript对象名        //参数2:Java对象名        mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS类对象映射到js的test对象        // 加载JS代码        // 格式规定为:file:///android_asset/文件名.html        mWebView.loadUrl("file:///android_asset/javascript.html");

通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url

具体原理:

  1. Android通过 WebViewClient 的回调方法
  2. shouldOverrideUrlLoading ()拦截 url解析该 url 的协议
  3. 如果检测到是预先约定好的协议,就调用相应方法

步骤1:在JS约定所需要的Url协议
将以下的HTML:javascript.html文件放在src/main/assets文件夹里

DOCTYPE html><html>   <head>      <meta charset="utf-8">      <title>Carson_Hotitle>           <script>         function callAndroid(){                        document.location = "js://webview?arg1=111&arg2=222";         }      script>head>   <body>     <button type="button" id="button1" onclick="callAndroid()">点击调用Android代码button>   body>html>

当该JS通过Android的mWebView.loadUrl(“file:///android_asset/javascript.html”)加载后,就会回调shouldOverrideUrlLoading (),接下来继续看步骤2:

步骤2:在Android通过WebViewClient复写shouldOverrideUrlLoading ()

public class MainActivity extends AppCompatActivity {    WebView mWebView;//    Button button;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mWebView = (WebView) findViewById(R.id.webview);        WebSettings webSettings = mWebView.getSettings();        // 设置与Js交互的权限        webSettings.setJavaScriptEnabled(true);        // 设置允许JS弹窗        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);        // 步骤1:加载JS代码        // 格式规定为:file:///android_asset/文件名.html        mWebView.loadUrl("file:///android_asset/javascript.html");// 复写WebViewClient类的shouldOverrideUrlLoading方法mWebView.setWebViewClient(new WebViewClient() {          @Override          public boolean shouldOverrideUrlLoading(WebView view, String url) {              // 步骤2:根据协议的参数,判断是否是所需要的url              // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)              //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)              Uri uri = Uri.parse(url);                   // 如果url的协议 = 预先约定的 js 协议              // 就解析往下解析参数              if ( uri.getScheme().equals("js")) {                  // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议                  // 所以拦截url,下面JS开始调用Android需要的方法                  if (uri.getAuthority().equals("webview")) {                     //  步骤3:                      // 执行JS所需要调用的逻辑                      System.out.println("js调用了Android的方法");                      // 可以在协议上带有参数并传递到Android上                      HashMap<String, String> params = new HashMap<>();                      Set<String> collection = uri.getQueryParameterNames();                  }                  return true;              }              return super.shouldOverrideUrlLoading(view, url);          }      }        );   }        }

这种方式的缺点在于js调用了Android,但是如果想要获取返回值就很复杂。如果JS想要得到Android方法的返回值,只能通过 WebView 的 loadUrl ()去执行 JS 方法把返回值传递回去,相关的代码如下:

// Android:MainActivity.javamWebView.loadUrl("javascript:returnResult(" + result + ")");// JS:javascript.htmlfunction returnResult(result){    alert("result is" + result);}

通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

在JS中,有三个常用的对话框方法:
在这里插入图片描述
方式3的原理:Android通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调分别拦截JS对话框。

以下是具体步骤,以拦截prompt()方法为例:

步骤1:加载js代码。
将javascript.html格式放到src/main/assets文件夹里

DOCTYPE html><html>   <head>      <meta charset="utf-8">      <title>Carson_Hotitle>           <script>        function clickprompt(){    // 调用prompt()    var result=prompt("js://demo?arg1=111&arg2=222");    alert("demo " + result);}      script>head>   <body>     <button type="button" id="button1" onclick="clickprompt()">点击调用Android代码button>   body>html>

当使用mWebView.loadUrl(“file:///android_asset/javascript.html”)加载了上述JS代码后,就会触发回调onJsPrompt()。

步骤2:在Android通过WebChromeClient复写onJsPrompt()

public class MainActivity extends AppCompatActivity {    WebView mWebView;//    Button button;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mWebView = (WebView) findViewById(R.id.webview);        WebSettings webSettings = mWebView.getSettings();        // 设置与Js交互的权限        webSettings.setJavaScriptEnabled(true);        // 设置允许JS弹窗        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 先加载JS代码        // 格式规定为:file:///android_asset/文件名.html        mWebView.loadUrl("file:///android_asset/javascript.html");        mWebView.setWebChromeClient(new WebChromeClient() {            // 拦截输入框(原理同方式2)            // 参数message:代表promt()的内容(不是url)            // 参数result:代表输入框的返回值            @Override            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {                // 根据协议的参数,判断是否是所需要的url(原理同方式2)                // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)                //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)                Uri uri = Uri.parse(message);                // 如果url的协议 = 预先约定的 js 协议                // 就解析往下解析参数                if ( uri.getScheme().equals("js")) {                    // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议                    // 所以拦截url,下面JS开始调用Android需要的方法                    if (uri.getAuthority().equals("webview")) {                        //                        // 执行JS所需要调用的逻辑                        System.out.println("js调用了Android的方法");                        // 可以在协议上带有参数并传递到Android上                        HashMap<String, String> params = new HashMap<>();                        Set<String> collection = uri.getQueryParameterNames();                        //参数result:代表消息框的返回值(输入值)                        result.confirm("js调用了Android的方法成功啦");                    }                    return true;                }                return super.onJsPrompt(view, url, message, defaultValue, result);            }// 通过alert()和confirm()拦截的原理相同,此处不作过多讲述            // 拦截JS的警告框            @Override            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {                return super.onJsAlert(view, url, message, result);            }            // 拦截JS的确认框            @Override            public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {                return super.onJsConfirm(view, url, message, result);            }        }        );            }        }

WebView缓存机制与预加载

WebView存在的性能问题

  1. webview H5页面加载速度比较慢,因为H5页面需要的资源很多,网络请求数据量大。
  2. H5页面渲染速度慢,取决于js解析效率与手机硬件设备的性能。
  3. 耗费流量多,每次使用时都需要重新加载webview里边的H5页面,导致需要发送大量串行的网络请求。

解决方案

webview缓存机制

  1. 浏览器 缓存机制
  2. Application Cache 缓存机制
  3. Dom Storage 缓存机制
  4. Web SQL Database 缓存机制
  5. Indexed Database 缓存机制

浏览器缓存

根据 HTTP 协议头里的 Cache-Control(或 Expires)和 Last-Modified(或 Etag)等字段来控制文件缓存的机制

  1. Cache-Control:用于控制文件在本地缓存有效时长:

如服务器回包:Cache-Control:max-age=600,则表示文件在本地应该缓存,且有效时长是600秒(从发出请求算起)。在接下来600秒内,如果有请求这个资源,浏览器不会发出 HTTP 请求,而是直接使用本地缓存的文件。

  1. Expires:与Cache-Control功能相同,即控制缓存的有效时间

Expires是 HTTP1.0 标准中的字段,Cache-Control 是 HTTP1.1 标准中新加的字段
当这两个字段同时出现时,Cache-Control 优先级较高

  1. Last-Modified:标识文件在服务器上的最新更新时间

下次请求时,如果文件缓存过期,浏览器通过 If-Modified-Since 字段带上这个时间,发送给服务器,由服务器比较时间戳来判断文件是否有修改。如果没有修改,服务器返回304告诉浏览器继续使用缓存;如果有修改,则返回200,同时返回最新的文件。

  1. Etag:功能同Last-Modified ,即标识文件在服务器上的最新更新时间。

不同的是,Etag 的取值是一个对文件进行标识的特征字串。
在向服务器查询文件是否有更新时,浏览器通过If-None-Match 字段把特征字串发送给服务器,由服务器和文件最新特征字串进行匹配,来判断文件是否有更新:没有更新回包304,有更新回包200
Etag 和 Last-Modified 可根据需求使用一个或两个同时使用。两个同时使用时,只要满足基中一个条件,就认为文件没有更新。

常见的用法是:

即一个用于控制缓存有效时间,一个用于在缓存失效后,向服务查询是否有更新。

Cache-Control、 Last-Modified 、 Expires、 Etag都是webview内置自动实现的

Application Cache 缓存机制

DOCTYPE html><html manifest="demo_html.appcache">// HTML 在头中通过 manifest 属性引用 manifest 文件// manifest 文件:就是上面以 appcache 结尾的文件,是一个普通文件文件,列出了需要缓存的文件// 浏览器在首次加载 HTML 文件时,会解析 manifest 属性,并读取 manifest 文件,获取 Section:CACHE MANIFEST 下要缓存的文件列表,再对文件缓存<body>...body>html>

原理说明如下:

具体实现是 通过webview的WebSettings类来实现的:

// 通过设置WebView的settings来实现WebSettings settings = getSettings();String cacheDirPath = context.getFilesDir().getAbsolutePath()+"cache/";settings.setAppCachePath(cacheDirPath);// 1. 设置缓存路径 settings.setAppCacheMaxSize(20*1024*1024);// 2. 设置缓存大小settings.setAppCacheEnabled(true);// 3. 开启Application Cache存储机制// 特别注意// 每个 Application 只调用一次 WebSettings.setAppCachePath() 和WebSettings.setAppCacheMaxSize()

Dom Storage 缓存机制

Dom Storage 机制类似于 Android 的 SharedPreference机制。

DOM Storage 分为 sessionStorage & localStorage; 二者使用方法基本相同,区别在于作用范围不同:

// 通过设置 `WebView`的`Settings`类实现WebSettings settings = getSettings();settings.setDomStorageEnabled(true);// 开启DOM storage

Web SQL Database 缓存机制

基于 SQL 的数据库存储机制。
根据官方说明,Web SQL Database存储机制不再推荐使用(不再维护)
取而代之的是 IndexedDB缓存机制,下面会详细介绍

IndexedDB 缓存机制

属于 NoSQL 数据库,通过存储字符串的 Key - Value 对来提供,类似于Dom Storage 存储机制 的key-value存储方式。

用于存储复杂、数据量大的结构化数据。

// 通过设置WebView的settings实现        WebSettings settings = getSettings();        settings.setJavaScriptEnabled(true);        // 只需设置支持JS就自动打开IndexedDB存储机制        // Android 在4.4开始加入对 IndexedDB 的支持,只需打开允许 JS 执行的开关就好了。

资源预加载

将需使用的H5页面提早加载,即提前构建缓存。
对于Android首页建议使用这种方案:

预加载H5资源:
主要分为以下两个方面:

  1. 首次使用的WebView对象

    • 首次初始化webview会比第2次慢很多,初始化后即便webview被释放,但一些webview共用的全局服务和资源对象仍未释放,使得第2次加载会更快。
    • 基于这种现象,可以在应用启动时就初始化1个全局的webview对象,即在Android的BaseApplication中初始化1个webview对象。
  2. 后续使用的WebView对象

    • 多次创建webview对象会耗费很多时间资源,可以自身构建webview复用池,比如采用2个或者多个webview重复使用,而不需每次打开H5都复用。

自身构建缓存

  1. 事先将更新频率较低、常用 & 固定的H5静态资源 文件(如JS、CSS文件、图片等) 放到本地
  2. 拦截H5页面的资源网络请求 并进行检测
  3. 如果检测到本地具有相同的静态资源 就 直接从本地读取进行替换 而 不发送该资源的网络请求 到 服务器获取

重写WebViewClient 的 shouldInterceptRequest 方法,当向服务器访问这些静态资源时进行拦截,检测到是相同的资源则用本地资源代替:

// 假设现在需要拦截一个图片的资源并用本地资源进行替代        mWebview.setWebViewClient(new WebViewClient() {            // 重写 WebViewClient  的  shouldInterceptRequest ()            // API 21 以下用shouldInterceptRequest(WebView view, String url)            // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)            // 下面会详细说明             // API 21 以下用shouldInterceptRequest(WebView view, String url)            @Override            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {                // 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名                if (url.contains("logo.gif")) {                // 假设网页里该图片资源的地址为:http://abc.com/imgage/logo.gif                // 图片的资源文件名为:logo.gif                    InputStream is = null;                    // 步骤2:创建一个输入流                    try {                        is =getApplicationContext().getAssets().open("images/abc.png");                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)                        // a. 先在app/src/main下创建一个assets文件夹                        // b. 在assets文件夹里再创建一个images文件夹                        // c. 在images文件夹放上需要替换的资源(此处替换的是abc.png图片)                    } catch (IOException e) {                        e.printStackTrace();                    }                    // 步骤4:替换资源                    WebResourceResponse response = new WebResourceResponse("image/png","utf-8", is);                    // 参数1:http请求里该图片的Content-Type,此处图片为image/png                    // 参数2:编码类型                    // 参数3:存放着替换资源的输入流(上面创建的那个)                    return response;                }                return super.shouldInterceptRequest(view, url);            }                       // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)            @TargetApi(Build.VERSION_CODES.LOLLIPOP)            @Override            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {               // 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名                if (request.getUrl().toString().contains("logo.gif")) {                // 假设网页里该图片资源的地址为:http://abc.com/imgage/logo.gif                // 图片的资源文件名为:logo.gif                    InputStream is = null;                    // 步骤2:创建一个输入流                    try {                        is = getApplicationContext().getAssets().open("images/abc.png");                         // 步骤3:获得需要替换的资源(存放在assets文件夹里)                        // a. 先在app/src/main下创建一个assets文件夹                        // b. 在assets文件夹里再创建一个images文件夹                        // c. 在images文件夹放上需要替换的资源(此处替换的是abc.png图片                    } catch (IOException e) {                        e.printStackTrace();                    }                    // 步骤4:替换资源                    WebResourceResponse response = new WebResourceResponse("image/png","utf-8", is);                    // 参数1:http请求里该图片的Content-Type,此处图片为image/png                    // 参数2:编码类型                    // 参数3:存放着替换资源的输入流(上面创建的那个)                    return response;                }                return super.shouldInterceptRequest(view, request);            }    });}

来源地址:https://blog.csdn.net/baiduwaimai/article/details/130992041

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯