参考文献:
Carson_Ho的Android:这是一份全面 & 详细的Webview使用攻略

本文知识点:

  • WebView的介绍
  • WebView的基本使用
  • WebView的一些开发中常用的API
  • WebView中一些常见的案例分析及实现
这里盗用Carson的一张图片,如果觉得不妥,及时告知马上删除

1.WebView的介绍

相信做Android的基本上都用过WebView,不论是加载网页还是处理本地内容,基本上都或多或少的使用过WebView。那么WebView能为我们带来什么呢?其实就相当于一个内置的浏览器,我们可以使用它完成一下操作:

1.WebView可以加载网页
2.WebView可以加载html片段
3.WebView可以加载本地html
4.WebView与原生App进行交互
以上种种都是WebView能帮我们完成的内容,所以现在有很多App就采用混合开发。这样使得我们使用WebView的场景越来越多,所以这里总结一下关于WebView的一些内容。

2.WebView的基本使用

其实如果你只用WebView加载一个网页的话,还是很好上手的。基本上就三步:

  • 在AndroidManifest注册网络权限:这个是必须的,切记切记!!!
 
  • 加载相应的网页资源
webView.loadUrl(url);

但是这里虽然加载的是一个url,但是你会发现他会直接蹦到系统自带的浏览器中去。作为强大的开发人员,怎么处理呢?当然是有办法的了。。。

  • 设置相应的本APP内打开的方法;
        webView.setWebViewClient(new WebViewClient() {            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)            @Override            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {                view.loadUrl(String.valueOf(request.getUrl()));                return true;            }            @Override            public boolean shouldOverrideUrlLoading(WebView view, String url) {                view.loadUrl(url);                return true;            }        });

上面的代码有几点需要说明一下:

  • shouldOverrideUrlLoading(WebView view, WebResourceRequest request) 这个方法是在5.0以上的版本上使用的,而shouldOverrideUrlLoading(WebView view, String url)这个方法是在5.0以下使用的,有了这两个方法,就能在自己的APP中打开相应的网页链接了。。。

上面就是WebView的简单实用,基本上就是展示一个网页,没有相应的交互问题。

3.WebView一些开中常用的API

加载类:

  • loadUrl(string url) 加载url地址的方法,参数就是一个Url地址;这里有一个点需要注意一下,这个方法加载的地址可以是
    • 网址 如 -> http://www.baidu.com
    • 手机本地html地址 如-> file:///android_asset/test.html
    • app包中assect目录下的html文件 如 -> content://com.android.htmlfileprovider/sdcard/test.html
  • loadData(String data, String mimeType, String encoding) 加载html片段
    • 参数1:html片段
    • 参数2:类型
    • 参数3:编码格式

状态类

  • onResume() 当webView为活跃状态的时候回调此方法(获取焦点)
  • onPause() 切换到后台的时候回调此方法(失去焦点)

其实上面这两个方法是和生命周期匹配的两个方法

  • pauseTimers() 全局范围切换到后台降低cpu功耗时回调的方法
  • resumeTimers() 恢复正常状态的时候回调的方法

上面这两个方法,是为了提高自己APP的性能的方法,主要是在生命周期的方法中调用的,也就是说在Activity的onResume()和onPause()中调用的(个人感觉啊。。。)

  • destroy() 销毁WebView的方法(主要是在音视频的时候,在这里释放相应的WebView,这里也是对应相应生命周期的方法的,但是这里注意一点,在销毁前必须先移除WebView,因为WebView会持有相应Activity的上下文引用所以这里要使用其父布局调用相应的removeView(View view)的方法进行移除)

操作网页类

  • canGoBack() 是否可以后退
  • goBack() 回退
  • canGoForward() 是否可以前进
  • goForward() 前进网页
  • **goBackOrForward(intsteps) ** 前进或者后退指定的位置,正数为前进/负数为后退

这里面涉及到的主要内容就是操作网页的前进和后退,主要应用场景是什么呢?试想一下,有的WebView是嵌到APP中的,一般这样的网页都是没有标题的,标题的交互留给APP处理返回的问题,就会用到上面相应的API了,基本上都是处理相应的返回问题

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

上面两段代码是一样的,但是这里有一个很重要的问题需要注意一下,很重要啊:
有些网页存在重定向的时候,那么canGoBack()返回的会一直是true,我这里使用的是百度的链接,因为百度重定向了,所以一直返回的是true,如果这里要是自己公司开发的Url的话,应该不会出现这种情况!这里提供一种解决办法

    @Override    public void goBack() {        WebBackForwardList mWebBackForwardList = copyBackForwardList();        // 判断当前历史列表是否最顶端,其实canGoBack已经判断过        if (mWebBackForwardList.getCurrentIndex() > 0) {            // 获取历史列表            String historyUrl = mWebBackForwardList.getItemAtIndex(                    mWebBackForwardList.getCurrentIndex() - 1).getUrl();            if (historyUrl.equals("https://www.baidu.com/")) {                //回到首页了                Activity activity = (Activity) mContext;                activity.finish();            }        }        super.goBack();    }

上面的代码就是拷贝出所有前进的页面,然后判断因为这里重定向了,所以你getUrl()获取的网址永远是重定向那个,所以这里我就想直接把网址写死的话就能处理了,但是我觉得我的解决办法比较笨,还请大神们指教!!!

缓存类

  • clearCache(true) 清除WebView产生的所有缓存
  • clearHistory() 清除当前WebView访问的历史数据
  • clearFormData() 清除表单数据

WebSettings类

这个类是对WebView进行管理和设置的

  • getSettings() 获取WebSettings的
  • setJavaScriptEnabled(boolean flag); 是否可以和js进行交互
  • setPluginState(PluginState state) 是否支持插件,这里面传递的是一个枚举对象
    • WebSettings.PluginState.OFF 不支持
    • WebSettings.PluginState.ON 支持
  • setUseWideViewPort(boolean use) 是否将图片调整到WebView的大小
  • setLoadWithOverviewMode(boolean overview) 是否将WebView调整到屏幕大小

上面两个API一起使用!切记,因为只有这样才能让界面看上去不那么丑!

  • setSupportZoom(boolean support)支持缩放,默认为true。是下面这个API的前提。

  • setBuiltInZoomControls(boolean enabled) 设置内置的缩放控件。若为false,则该WebView不可缩放

  • setDisplayZoomControls(boolean enabled) 隐藏原生的缩放控件,这里可以自己在页面中实现。

  • setCacheMode(@CacheMode int mode) 设置缓存的模式

    • WebSettings.LOAD_DEFAULT 默认的模式 根据cache-control决定是否从网络上取数据。
    • WebSettings.LOAD_CACHE_ELSE_NETWORK 只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据
    • WebSettings.LOAD_NO_CACHE 不使用缓存
    • WebSettings.LOAD_CACHE_ONLY 不使用网络,只加载缓存
  • setAllowFileAccess(boolean allow) 设置可以访问app中assect中的文件

  • setJavaScriptCanOpenWindowsAutomatically(boolean flag) 支持通过JS打开新窗口

  • setLoadsImagesAutomatically(boolean flag) 支持自动加载图片

  • setDefaultTextEncodingName(String encoding) 设置编码格式

  • setRenderPriority(RenderPriority priority) 设置渲染的优先级,这里传入的是一个枚举

    • NORMAL 正常
    • HIGH 高
    • LOW 低

WebViewClient类

处理各种通知和请求事件的控制类

  • shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
  • shouldOverrideUrlLoading(WebView view, String url)

下面这个方法已经过时了,最新的是上面的那个方法,对于相应的url可以通过WebResourceRequest进行获取。重写这两个方法自己处理的话,可以不使用相应的浏览器进行打开,在本APP中打开,这也是一开始的时候为什么要重写这个方法。

  • onPageStarted(WebView view, String url, Bitmap favicon) 加载WebView开始的时候回调的方法,一般都在这里添加相应的Loadding,与用户交互,告诉用户页面还没有加载完成;
  • onPageFinished(WebView view, String url) WebView加载完成的时候回调的方法和上面那个方法是相呼应的,可以在这里关闭相应的对话框!

这里有一个问题注意一下,还是重定向的问题。如果这个网页重定向了,那么上面这两个方法会被多次调用!

  • onLoadResource(WebView view, String url) 这个方法会在每次加载资源的时候回调,如果图片很多的话,会被调用多次。后面这个url代表的是相应加载的地址。

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

  • onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)

上面这个方法已经过时了,可以根据相应的code进行判断。后面这个方法主要是通过WebResourceError的错误编码进行相应的判断,处理相应的错误!这个方法的主要作用是加载错误的回调。

  • onReceivedError(WebView view, int errorCode,String description, String failingUrl)
  • onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)

这两个方法是处理相应的https的网络请求的(webView默认是不处理https请求的,页面会显示空白,使用如上面的方法进行处理)

            @Override            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {                super.onReceivedSslError(view, handler, error);                handler.proceed();            }            // 特别注意:5.1以上默认禁止了https和http混用,以下方式是开启            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);            }

WebChromeClient类

如果你对控制类没有那么多要求的话,使用上面的控制类就可以了,但是如果你要求比较高的话,就要用到下面这个控制类了

  • onProgressChanged(WebView view, int newProgress) 这个回调会实时的返回网页加载的总进度,最大值是100,可以根据当前值显示进度。
  • onReceivedTitle(WebView view, String title) 获取当前加载网页的标题
  • onJsAlert(WebView view, String url, String message, JsResult result) 如果这里返回true,那么js中弹出的警告对话框就由客户端处理;
  • onJsAlert(WebView view, String url, String message, JsResult result) 如果这里返回true,那么js中弹出的确认对话框就由客户端处理;
  • **onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) ** 如果这里返回true,那么js中弹出的提示对话框将由客户端进行处理。

4.WebView中一些常见的案例分析及实现

WebView显示进度

由很多APP中由都嵌入WebView,不知道大家仔细看过没有,有的WebView在加载的时候顶部由一个进度条,提示用户加载的进度,其实很好实现,这里就带大家实现一下:

效果图
  • 页面搭建
<?xml version="1.0" encoding="utf-8"?>        

这里就不讲解ProgressBar实现水平进度条了,百度一大堆!!!

  • 代码实现
    private java.lang.String mUrl = "https://www.baidu.com/";    private WebView mWb;    private ProgressBar progressBar;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mWb = findViewById(R.id.wb);        progressBar = findViewById(R.id.pb_progress);        progressBar.setMax(100);        setWebView(mWb);    }    @SuppressLint("SetJavaScriptEnabled")    private void setWebView(WebView wb) {        WebSettings settings = wb.getSettings();        settings.setJavaScriptEnabled(true);        settings.setPluginState(WebSettings.PluginState.ON);        settings.setUseWideViewPort(true);        settings.setLoadWithOverviewMode(true);        settings.setSupportZoom(true);        wb.loadUrl(mUrl);        wb.setWebViewClient(new WebViewClient() {            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)            @Override            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {                view.loadUrl(String.valueOf(request.getUrl()));                return true;            }            @Override            public boolean shouldOverrideUrlLoading(WebView view, String url) {                view.loadUrl(url);                return true;            }        });        wb.setWebChromeClient(new WebChromeClient() {            @Override            public void onProgressChanged(WebView view, int newProgress) {                super.onProgressChanged(view, newProgress);                progressBar.setProgress(newProgress);            }        });    }

主要代码就这么多就能,这里设置了一些其他的属性,上面都讲解了,这里就不去说了。。。

WebView加载错误显示缺省页面

private java.lang.String mUrl = "https://google.com/";    private WebView mWb;    private ProgressBar progressBar;    private java.lang.String mErrorUrl = "file:///android_asset/test.html";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mWb = findViewById(R.id.wb);        progressBar = findViewById(R.id.pb_progress);        progressBar.setMax(100);        setWebView(mWb);    }    @SuppressLint("SetJavaScriptEnabled")    private void setWebView(WebView wb) {        WebSettings settings = wb.getSettings();        settings.setJavaScriptEnabled(true);        settings.setPluginState(WebSettings.PluginState.ON);        settings.setUseWideViewPort(true);        settings.setLoadWithOverviewMode(true);        settings.setSupportZoom(true);        wb.loadUrl(mUrl);        wb.setWebViewClient(new WebViewClient() {            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)            @Override            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {                view.loadUrl(String.valueOf(request.getUrl()));                return true;            }            @Override            public boolean shouldOverrideUrlLoading(WebView view, String url) {                view.loadUrl(url);                return true;            }            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)            @Override            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {                super.onReceivedHttpError(view, request, errorResponse);                // 这个方法在6.0才出现                int statusCode = errorResponse.getStatusCode();                System.out.println("onReceivedHttpError code = " + statusCode);                if (404 == statusCode || 500 == statusCode) {                    view.loadUrl("about:blank");// 避免出现默认的错误界面                    view.loadUrl(mErrorUrl);                }            }            @RequiresApi(api = Build.VERSION_CODES.M)            @Override            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {                super.onReceivedError(view, request, error);                // 断网或者网络连接超时                if (error.getErrorCode() == ERROR_HOST_LOOKUP || error.getErrorCode() == ERROR_CONNECT || error.getErrorCode() == ERROR_TIMEOUT) {                    view.loadUrl("about:blank"); // 避免出现默认的错误界面                    view.loadUrl(mErrorUrl);                }            }            @Override            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {                super.onReceivedError(view, errorCode, description, failingUrl);                // 断网或者网络连接超时                if (errorCode == ERROR_HOST_LOOKUP || errorCode == ERROR_CONNECT || errorCode == ERROR_TIMEOUT) {                    view.loadUrl("about:blank"); // 避免出现默认的错误界面                    view.loadUrl(mErrorUrl);                }            }        });        wb.setWebChromeClient(new WebChromeClient() {            @Override            public void onProgressChanged(WebView view, int newProgress) {                super.onProgressChanged(view, newProgress);                Log.e("done", "onProgressChanged: " + newProgress);                progressBar.setProgress(newProgress);            }            @Override            public void onReceivedTitle(WebView view, String title) {                super.onReceivedTitle(view, title);                // android 6.0 以下通过title获取                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {                    if (title.contains("404") || title.contains("500") || title.contains("Error")) {                        view.loadUrl("about:blank");// 避免出现默认的错误界面                        view.loadUrl(mErrorUrl);                    }                }            }        });    }

因为WebView引起了google的重视,所以版本之间存在很多差异,所以这里区分了很多版本的问题,上面都有相应的注解!其实就是在断网或者网络重连的时候添加了相应的错误页面,防止用户看见那个难看的断网页面。增加用户的体验!

WebView与JS交互的实现

这里先贴一段测试的js代码,下面会用到:

        App调用js的代码    

假装这个一个页面

1. 原生代码调用js中的代码

这里可以有两种实现方案:
这里注意一点,无论使用那种方案调用,前提是一定要有下面的代码!!!

        WebSettings settings = mWb.getSettings();        settings.setJavaScriptEnabled(true);//允许相应js代码
  • 通过loadUrl("javascript:callJS()")调用js中的代码,但是会刷新相应的页面(这里面callJS是上面function的方法名称)

  • 通过evaluateJavascript(String script, ValueCallback resultCallback)的callBack进行调用相应的方法(这个处理效率高,但是这个方法是在API19才可以使用的,所以一般在使用的时候通过判断版本两个一起使用!!!)

整体的使用代码如下:

        if (Build.VERSION.SDK_INT > 18) {            mWb.evaluateJavascript("javascript:callJS()", new ValueCallback() {                @Override                public void onReceiveValue(String value) {                    Log.e("done", "onReceiveValue: " + value);                }            });        } else {            mWb.loadUrl("javascript:callJS()");        }

这样既解决了效率的问题也解决了版本的问题。

2. 原生代码调用js中的代码

这里最常用的方式就是通过注释@JavascriptInterface进行调用,但是看了Carson_Ho的文章说这里有漏洞,但是说4.2就没有这种漏洞了,所以没有去研究,感觉现在一般的手机都应该超过4.2了,如果以后需要适配的时候在仔细看一下!!!

  • 步骤1:
    创建一个类用于给JS提供相应的方法;
    class JSMothod {        @JavascriptInterface        public void hello(String msg) {            System.out.println("JS调用了Android的hello方法");        }    }

这里有一点需要注意的,就是@JavascriptInterface这个注解一定要加上,否则对于4.0一下的手机可能会存在问题!!!切记。。。

  • 步骤2:
    添加相应的调用方法
addJavascriptInterface(Object object, String name);

这里面的两个参数分别是上面写的那个类对象和相应js中的方法名;具体的代码是这样的。。。

mWb.addJavascriptInterface(new JSMothod(),"test");

这样就能实现js调用Android的代码了,其实很简单。


基本上上面的内容就囊括了Android在使用WebView中的一些常见的使用场景,可能还有些内容没有想到,如果有什么不明白的,可以在下方给我留言,还希望这些内容可以帮到你!!!

更多相关文章

  1. Android的一些冷知识
  2. Android中Context的总结及其用法
  3. Android类库打包方法探究
  4. Android中滑屏初探 - scrollTo 以及 scrollBy方法使用说明
  5. Android:异步处理之Handler、Looper、MessageQueue之间的恩怨(三)
  6. 【起航计划 013】2015 起航计划 Android(安卓)APIDemo的魔鬼步伐
  7. Android(安卓)之 Choreographer 详细分析
  8. 利用Android(安卓)Studio、MAT对Android进行内存泄漏检测
  9. 【Android】实现登录、注册、数据库操作(极复杂)

随机推荐

  1. Windows下配置jdk、Android环境变量
  2. 带你了解Android常见的内存缓存算法
  3. android 意见反馈
  4. Android开发基础—通过匿名内部类实现按
  5. Android(安卓)录像添加时间戳水印
  6. 【Android四大组件之Service】(二)Android
  7. Android技术提升
  8. Android的view重绘
  9. cocos2dx android mk 2.0.2编译文件目录
  10. Android(安卓)开发技术选型(博客,新闻,阅读