澳门新葡萄京官网注册 6

澳门新葡萄京官网注册:Android中WebView使用解析

对于webview的简单使用在这里不做过多的说明,使用webview加载网页的核心方法是

澳门新葡萄京官网注册 1

原文链接:

public void loadUrl(String url) {}

老婆保佑,代码无BUG

为什么要学习Android与H5互调?

微信,QQ空间等大量软件都内嵌了H5,不得不说是一种趋势。Android与H5互调可以让我们的实现混合开发,至于混合开发就是在一个App中内嵌一个轻量级的浏览器,一部分原生的功能改为Html
5来开发。
优势:使用H5实现的功能能够在不升级App的情况下动态更新,而且可以在Android或iOS的App上同时运行,节约了成本,提高了开发效率。
原理:其实就是Java代码和JavaScript之间的调用。

开局插入一张文章的目录结构:
澳门新葡萄京官网注册 2

下面就是介绍围绕webview进行的一系列的配置。


WebView简介

要实现Android与H5互调,WebView是一个很重要的控件,WebView可以很好地帮助我们展示html页面,所以有必要先了解一下WebView。

首先如果你调用了下面代码:

前言

权当给自己的记录,而且WebView 坑很多


一丶WebView常用方法

(1)loadUrl
加载界面,其次还有LoadData和LoadDataWithBase方法

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

(2)setWebViewClient(如果用户设置了WebViewClient,则在点击新的链接以后就不会跳转到系统浏览器了,而是在本WebView中显示。注意:并不需要覆盖
shouldOverrideUrlLoading 方法,同样可以实现所有的链接都在 WebView
中打开。)

WebViewClient主要用来辅助WebView处理各种通知、请求等事件,通过setWebViewClient方法设置。以下是它的几种常见用法:

1>
实现对网页中超链接的拦截(比如如果是极客导航的主页,则直接拦截转到百度主页):
当点击页面中的链接后,会在WebView加载URL前回调shouldOverrideUrlLoading(WebView
view, String url)方法,一般点击一个链接此方法调用一次。

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返回值的误区:网上很多解释是return
true代表在本WebView中打开链接,return
false代表调用系统浏览器打开链接。其实只要设置了WebViewClient,则就不会调用系统浏览器。
那么shouldOverrideUrlLoading的返回值到底代表什么呢?return
true,则在打开新的url时WebView就不会再加载这个url了,所有处理都需要在WebView中操作,包含加载;return
false,则系统就认为上层没有做处理,接下来还是会继续加载这个url的;默认return
false。具体的区别展示如下:
加载百度主页,设置WebViewClient后,重写shouldOverrideUrlLoading(WebView
view, String
url)方法,第一张是返回false的截图(点击后正常跳转),第二章是返回true的截图(点击无反应,如果希望能够跳转,则需要我们自己进行处理):
澳门新葡萄京官网注册 3

澳门新葡萄京官网注册 4

还有一点需要注意的是,如果我们拦截了某个url,那么return false 和 return
true区别不大,所以一般建议 return false。

2>
加载网页时替换某个资源(比如在加载一个网页时,需要加载一个logo图片,而我们想要替换这个logo图片,用我们assets目录下的一张图片替代)
我们知道我们在加载一个网页的同时也会加载js,css,图片等资源,所以会多次调用shouldInterceptRequest方法,我们可以在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;
            }
        });

3> 设置开始加载网页、加载完成、加载错误时处理

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);  
        // 加载网页失败时处理 如:提示失败,或显示新的界面
        ...
    }    
});  

4>
处理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);  // 可做其他处理  
    }   
});   

(3)setWebChromeClient
WebChromeClient主要用来辅助WebView处理Javascript的对话框、网站图标、网站标题以及网页加载进度等。通过WebView的setWebChromeClient()方法设置。

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);  
    }  
}  

(4)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());

(5)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);       
    }
}
webView = (WebView) findViewById(R.id.webview);
webView.loadUrl("http://www.baidu.com");

目录

  • 一:生命周期
  • 二:WebView 主要的3个类

      1. WebSettings
      1. WebViewClient
      1. WebChromeClient
  • 三: WebView缓存实现

  • 四: Cookies
  • 五: 关于前进 / 后退网页
  • 六:常见问题
  • 参考

添加网络权限

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

二、WebSettings配置

1.获取WebSettings对象

WebSettings webSettings = webView.getSettings();

2.常用设置方法

(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);

你会发现你的app会自动打开手机系统自带的默认浏览器,这时候我们需要加一行:

一:生命周期

三丶WebViewClient 的回调方法列表

WebViewClient主要用来辅助WebView处理各种通知、请求等事件,通过setWebViewClient方法设置。

(1)更新历史记录

doUpdateVisitedHistory(WebView view, String url, boolean isReload)

(2)应用程序重新请求网页数据

onFormResubmission(WebView view, Message dontResend, Message resend)

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

onLoadResource(WebView view, String url)

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

onPageStarted(WebView view, String url, Bitmap favicon)

(5)在页面加载结束时调用。同样道理,我们知道一个页面载入完成,于是我们可以关闭loading
条,切换程序动作。

onPageFinished(WebView view, String url)

(6)报告错误信息

onReceivedError(WebView view, int errorCode, String description,
String failingUrl)

(7)获取返回信息授权请求

onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
String host,String realm)

(8)重写此方法可以让webview处理https请求。

onReceivedSslError(WebView view, SslErrorHandler handler, SslError
error)

(9)WebView发生改变时调用

onScaleChanged(WebView view, float oldScale, float newScale)

(10)Key事件未被加载时调用

onUnhandledKeyEvent(WebView view, KeyEvent event)

(11)重写此方法才能够处理在浏览器中的按键事件。

shouldOverrideKeyEvent(WebView view, KeyEvent event)

(12)在网页跳转时调用,这个函数我们可以做很多操作,比如我们读取到某些特殊的URL,于是就可以不打开地址,取消这个操作,进行预先定义的其他操作,这对一个程序是非常必要的。

shouldOverrideUrlLoading(WebView view, String url)

(13)在加载某个网页的资源的时候多次调用(已过时)

shouldInterceptRequest(WebView view, String url)

(14)在加载某个网页的资源的时候多次调用

shouldInterceptRequest(WebView view, WebResourceRequest request)

注意:

shouldOverrideUrlLoading在网页跳转的时候调用,且一般每跳转一次只调用一次。
shouldInterceptRequest只要是网页加载的过程中均会调用,资源加载的时候都会回调该方法,会多次调用。

webView.setWebViewClient(new WebViewClient()}

onResume()

WebView为活跃状态时回调,可以正常执行网页的响应。

四丶WebChoromeClient的回调方法列表

WebChromeClient主要用来辅助WebView处理Javascript的对话框、网站图标、网站标题以及网页加载进度等。通过WebView的setWebChromeClient()方法设置。

(1)监听网页加载进度

onProgressChanged(WebView view, int newProgress)

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

onReceivedTitle(WebView view, String title)

(3)监听网页图标

onReceivedIcon(WebView view, Bitmap icon)

然后再运行发现就不调用默认浏览器了,下面为大家讲讲WebViewClient这个类:WebViewClient类中的几个方法在我们平时开发中大量的运用,首先是

onPause()

WebView被切换到后台时回调, 页面被失去焦点,
变成不可见状态,onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。

Java和JavaScript互调

为方便展示,使用addJavascriptInterface方式实现与本地js交互(存在漏洞)。也可通过其他方式实现,比如拦截ur进行参数解析l等。

public boolean shouldOverrideUrlLoading(WebView view, String url) {
    return false;
}

pauseTimers()

当应用程序被切换到后台时回调,该方法针对全应用程序的WebView,它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。

Java调JS

首先是JS的一段代码:

function javaCallJs(arg){
         document.getElementById("content").innerHTML =
             ("欢迎:"+arg );
    }

然后是在java中调用JS中的方法

webView.loadUrl("javascript:javaCallJs("+"'"+name+"'"+")");

以上代码就是调用了JS中一个叫javaCallJs(arg)的方法,并传入了一个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的形式弹出此参数。

一般情况下我们不需要重写,这个函数有一个返回值,当为false时意思是我们不用管,当前的webview自已加载这个url,当返回为ture时,就让我们自己操作。

resumeTimers()

恢复pauseTimers时的动作。

java与JS互调代码示例

先看效果图:
澳门新葡萄京官网注册 5

不好意思,传错了,是这张:
澳门新葡萄京官网注册 6

代码非常简单,并且加了注释,直接看代码就可以了。

首先是本地的JavaAndJavaScriptCall.html文件,放在asstes目录下

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <script type="text/javascript">

    function javaCallJs(arg){
         document.getElementById("content").innerHTML =
             ("欢迎:"+arg );
    }

    </script>
</head>
<body>
    <div id="content"> 请在上方输入您的用户名</div>
    <input type="button" value="点击Android被调用" onclick="window.Android.showToast('JS中传来的参数')"/>
</body>
</html>

javaCallJs是java调用JS的方法,showToast方法是JS调用java的方法

接下来是布局文件,activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ll_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="20dp"
        android:background="#000088">
        <EditText
            android:id="@+id/et_user"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:hint="输入WebView中要显示的用户名"
            android:background="#008800"
            android:textSize="16sp"
            android:layout_weight="1"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="40dp"
            android:layout_marginRight="20dp"
            android:textSize="16sp"
            android:text="确定"
            android:onClick="click"/>
    </LinearLayout>

</LinearLayout>

很简单,就是一个输入框和一个确定按钮,点击按钮会调用JS中的方法。

MainActivity

package com.wangjian.webviewdemo;

import android.annotation.SuppressLint;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private WebView webView;
    private LinearLayout ll_root;
    private EditText et_user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ll_root = (LinearLayout) findViewById(R.id.ll_root);
        et_user = (EditText) findViewById(R.id.et_user);
        initWebView();
    }

    //初始化WebView

    private void initWebView() {
        //动态创建一个WebView对象并添加到LinearLayout中
        webView = new WebView(getApplication());
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        webView.setLayoutParams(params);
        ll_root.addView(webView);
        //不跳转到其他浏览器
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        WebSettings settings = webView.getSettings();
        //支持JS
        settings.setJavaScriptEnabled(true);
        //加载本地html文件
        webView.loadUrl("file:///android_asset/JavaAndJavaScriptCall.html");
        webView.addJavascriptInterface(new JSInterface(),"Android");
    }

    //按钮的点击事件
    public void click(View view){
        //java调用JS方法
        webView.loadUrl("javascript:javaCallJs(" + "'" + et_user.getText().toString()+"'"+")");
    }

    //在页面销毁的时候将webView移除
    @Override
    protected void onDestroy() {
        super.onDestroy();
        ll_root.removeView(webView);
        webView.stopLoading();
        webView.removeAllViews();
        webView.destroy();
        webView = null;
    }

    private class JSInterface {
        //JS需要调用的方法
        @JavascriptInterface
        public void showToast(String arg){
            Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show();
        }
    }
}
public void onPageFinished(WebView view, String url) {
}

destroy()

关闭了Activity时回调, WebView调用destory时,
WebView仍绑定在Activity上.这是由于自定义WebView构建时传入了该Activity的context对象,
因此需要先从父
容器中移除WebView, 然后再销毁webview。

mRootLayout.removeView(webView);  
mWebView.destroy();

需要注意的地方

参考链接:安卓webview的一些坑

  1. webView.addJavascriptInterface()方法在API
    17之前有一些漏洞(有兴趣的可以参考本篇文章,WebView
    远程代码执行漏洞浅析),所以在API
    17以后,需要在JavaScript接口类的方法加上@JavascriptInterface注解。

  2. 仔细看的话你会发现我们上面的WebView对象并不是直接写在布局文件中的,而是通过一个LinearLayout容器,使用addview(webview)动态向里面添加的。另外需要注意创建webview需要使用applicationContext而不是activity的context,销毁时不再占有activity对象,最后离开的时候需要及时销毁webview,onDestory()中应该先从LinearLayout中remove掉webview,再调用webview.removeAllViews();webview.destory();

  3. 如果想要webView在产生OOM的时候不影响主进程,可以另开一个进程,在androidmanifest.xml的activity标签里加上Android:process属性就可以了。

  4. 在activity被杀死之后,依然保持webView的状态,方便用户下次打开的时候可以回到之前的状态。webview支持saveState(bundle)和restoreState(bundle)方法。

保存状态

@Override  
protected void onSaveInstanceState(Bundle outState) {  
    super.onSaveInstanceState(outState);  
    wv.saveState(outState);  
    Log.e(TAG, "save state...");  
}  

恢复状态(在activity的onCreate(bundle savedInstanceState)里)

if(null!=savedInstanceState){  
    wv.restoreState(savedInstanceState);  
    Log.i(TAG, "restore state");  
}else{  
    wv.loadUrl("http://3g.cn");  
}  

其他一些常见问题:

1. WebViewClient.onPageFinished()。
你永远无法确定当WebView调用这个方法的时候,网页内容是否真的加载完毕了。当前正在加载的网页产生跳转的时候这个方法可能会被多次调用,StackOverflow上有比较具体的解释(How
to listen for a Webview finishing loading a URL in Android?),
但其中列举的解决方法并不完美。所以当你的WebView需要加载各种各样的网页并且需要在页面加载完成时采取一些操作的话,可能WebChromeClient.onProgressChanged()比WebViewClient.onPageFinished()都要靠谱一些。
2. WebView后台耗电问题。
当你的程序调用了WebView加载网页,WebView会自己开启一些线程(?),如果你没有正确地将WebView销毁的话,这些残余的线程(?)会一直在后台运行,由此导致你的应用程序耗电量居高不下。对此我采用的处理方式比较偷懒,简单又粗暴(不建议),即在Activity.onDestroy()中直接调用System.exit(0),使得应用程序完全被移出虚拟机,这样就不会有任何问题了。
3. 切换WebView闪屏问题。
如果你需要在同一个ViewGroup中来回切换不同的WebView(包含了不同的网页内容)的话,你就会发现闪屏是不可避免的。这应该是Android硬件加速的Bug,如果关闭硬件加速这种情况会好很多,但无法获得很好的浏览体验,你会感觉网页滑动的时候一卡一卡的,不跟手
4.
在某些手机上,Webview有视频时,activity销毁后,视频资源没有被销毁,甚至还能听到在后台播放。即便是像刚才那样各种销毁webview也无济于事,解决办法:在onDestory之前修改url为空地址。

5.WebView硬件加速导致页面渲染闪烁问题
关于Android硬件加速 开始于Android 3.0 (API level
11),开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是容易会出现页面加载白块同时界面闪烁现象。解决这个问题的方法是设置WebView暂时关闭硬件加速
代码如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

当页面加载完成时调用,但需要注意的是

二:WebView 主要的3个类

/**
 * When onPageFinished() is called, the
 * rendering picture may not be updated yet. To get the notification for the
 * new Picture, use {@link WebView.PictureListener#onNewPicture}.
 */

1. WebSettings

WebSettings用来对WebView做各种设置, 你可以这样获取WebSettings

WebSettings webSettings = mWebView .getSettings();

也就是渲染图片有可能没有加载完成。

JS处理

setJavaScriptEnabled(true); //支持js
setPluginsEnabled(true); //支持插件
setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
/**
 * Notify the host application that the WebView will load the resource
 * specified by the given url.
 *
 * @param view The WebView that is initiating the callback.
 * @param url The url of the resource the WebView will load.
 */
public void onLoadResource(WebView view, String url) {
}

缩放处理

setUseWideViewPort(true); //将图片调整到适合webview的大小
setLoadWithOverviewMode(true); // 缩放至屏幕的大小
setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
setBuiltInZoomControls(true); //设置内置的缩放控件。 这个取决于setSupportZoom(), 若setSupportZoom(false),则该WebView不可缩放,这个不管设置什么都不能缩放。
setDisplayZoomControls(false); //隐藏原生的缩放控件

这个函数是这要加载资源就会被调用。然后说一下错误处理的函数:

内容布局

setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); //支持内容重新布局
supportMultipleWindows(); //多窗口
/**
 * Report an error to the host application. These errors are unrecoverable
 * (i.e. the main resource is unavailable). The errorCode parameter
 * corresponds to one of the ERROR_* constants.
 * @param view The WebView that is initiating the callback.
 * @param errorCode The error code corresponding to an ERROR_* value.
 * @param description A String describing the error.
 * @param failingUrl The url that failed to load.
 * @deprecated Use {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError)
 *             onReceivedError(WebView, WebResourceRequest, WebResourceError)} instead.
 */
@Deprecated
public void onReceivedError(WebView view, int errorCode,
        String description, String failingUrl) {
}

文件缓存

setAllowFileAccess(true); //设置可以访问文件
setDomStorageEnabled(true); // 开启 DOM storage API 功能
setDatabaseEnabled(true);   //开启 database storage API 功能
setAppCacheEnabled(true);//开启 Application Caches 功能

String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录

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

重写该函数让你的app更加人性化。

其他设置

setNeedInitialFocus(true); //当webview调用requestFocus时为webview设置节点
setLoadsImagesAutomatically(true); //支持自动加载图片
setDefaultTextEncodingName(“utf-8”); //设置编码格式
setPluginState(PluginState.OFF); //设置是否支持flash插件
setDefaultFontSize(20); //设置默认字体大小

那这里我们又有一个需求:我们要有一个进度条,那我们怎么知道时时的加载进度呢,就不是在WebViewClient类中了,而是在WebChromeClient中:

2. WebViewClient

WebViewClient用来帮助WebView处理各种通知,
请求事件。我们通过继承WebViewClient并重载它的方法可以实现不同功能的定制

/**
 * Tell the host application the current progress of loading a page.
 * @param view The WebView that initiated the callback.
 * @param newProgress Current page loading progress, represented by
 *                    an integer between 0 and 100.
 */
public void onProgressChanged(WebView view, int newProgress) {}

(1)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;
      }
  });

其中参数newProgress就是加载的时时进度。WebChromeClient中还用一些常用的函数,比如:

(2)onPageStarted()

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

 webView.setWebViewClient(new WebViewClient(){
      @Override
      public void  onPageStarted(WebView view, String url, Bitmap favicon) {
         //设定加载开始的操作
      }
  });
/**
 * Notify the host application of a change in the document title.
 * @param view The WebView that initiated the callback.
 * @param title A String containing the new title of the document.
 */
public void onReceivedTitle(WebView view, String title) {}

/**
 * Notify the host application of a new favicon for the current page.
 * @param view The WebView that initiated the callback.
 * @param icon A Bitmap containing the favicon for the current page.
 */
public void onReceivedIcon(WebView view, Bitmap icon) {}

(3) onPageFinished()

作用:在页面加载结束时调用。我们可以关闭loading 条,切换程序动作。

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

一个是可以获取网页的title,一个是可以title旁边的icon。

(4)onLoadResource()

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

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

然后说一下webview缓存问题:有时候我们有缓存的需求,就是在没有网络的情况下,以前可以打开的网页也可以通过缓存文件打开,主要代码为:

(5)onReceivedError()

作用:加载页面的服务器出现错误时(如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;
                }
            }
        });
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
webSettings.setAppCacheEnabled(true);
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/gefdemoweb";
Log.e(null,path);
webSettings.setAppCachePath(path);

(6) 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);    //可做其他处理
        }    
    });

第一行设置了缓存模式,第二行设置可以缓存,然后下面设置缓存路径,关于缓存模式有很多种:

3. WebChromeClient

辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。

public static final int LOAD_DEFAULT = -1;//默认模式,当缓存资源是可用的不过期,就使用,否次网络加载
public static final int LOAD_NORMAL = 0;//This value is obsolete,过时了,不用管
public static final int LOAD_CACHE_ELSE_NETWORK = 1;//当缓存资源是可用的就使用,即使它是过期的,否次网络加载
public static final int LOAD_NO_CACHE = 2;//不使用缓存
public static final int LOAD_CACHE_ONLY = 3;//不使用网络

(1) onProgressChanged()

:获得网页的加载进度并显示

webview.setWebChromeClient(new WebChromeClient(){

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

然后说一下按返回键的问题,如果你不做任何设置,按返回键肯定要跳到上一个activity,但是我们想让他返回到上一个加载的网页怎么办:

(2) onReceivedTitle()

获取Web页中的标题

每个网页的页面都有一个标题,比如www.baidu.com这个页面的标题即“百度一下,你就知道”,那么如何知道当前webview正在加载的页面的title并进行设置呢?

webview.setWebChromeClient(new WebChromeClient(){

    @Override
    public void onReceivedTitle(WebView view, String title) {
       titleview.setText(title);
    }

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

三: WebView缓存实现

在项目中如果使用到WebView控件, 当加载html页面时,
会在/data/data/包名目录下生成database与cache两个文件夹。
请求的url记录是保存在WebViewCache.db,
而url的内容是保存在WebViewCache文件夹下。

控制缓存行为

WebSettings webSettings = mWebView.getSettings();
//优先使用缓存
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); 
//(默认)根据cache-control决定是否从网络上取数据
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); 
//只在缓存中读取
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
//不使用缓存
WwebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);

清除缓存

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

完美解决。

四: Cookies

添加Cookies

public void synCookies() {
    if (!CacheUtils.isLogin(this)) return;
    CookieSyncManager.createInstance(this);
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.setAcceptCookie(true);
    cookieManager.removeSessionCookie();//移除
    String cookies = PreferenceHelper.readString(this, AppConfig.COOKIE_KEY, AppConfig.COOKIE_KEY);
    KJLoger.debug(cookies);
    cookieManager.setCookie(url, cookies);
    CookieSyncManager.getInstance().sync();
}

清除Cookies

CookieManager.getInstance().removeSessionCookie();

最后看一下webSetting的其它常用设置:

五: 关于前进 / 后退网页

setJavaScriptEnabled(true);  //支持js
setPluginsEnabled(true);  //支持插件 
setUseWideViewPort(false);  //将图片调整到适合webview的大小 
setSupportZoom(true);  //支持缩放 
setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); //支持内容重新布局  
supportMultipleWindows();  //多窗口 
setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);  //关闭webview中缓存 
setAllowFileAccess(true);  //设置可以访问文件 
setNeedInitialFocus(true); //当webview调用requestFocus时为webview设置节点
webview webSettings.setBuiltInZoomControls(true); //设置支持缩放 
setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 
setLoadWithOverviewMode(true); // 缩放至屏幕的大小
setLoadsImagesAutomatically(true);  //支持自动加载图片

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键控制网页后退

  • 问题:在不做任何处理前提下 ,浏览网页时点击系统的“Back”键,整个
    Browser 会调用 finish()而结束自身
  • 目标:点击返回后,是网页回退而不是推出浏览器
  • 解决方案:在当前Activity中处理并消费掉该 Back 事件

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

欢迎补充和纠正

六:常见问题

1. android webview ZoomButtonsController导致android.view.WindowLeaked 问题彻底解决

 @Override
    public void finish() {
        ViewGroup view = (ViewGroup) getWindow().getDecorView();
        view.removeAllViews();
        super.finish();

    }

参考

WebView基本用法(郭嘉
)

Android:最全面的 Webview
详解

发表评论

电子邮件地址不会被公开。 必填项已用*标注