返回顶部
首页 > 资讯 > 移动开发 >Android网页加载控件WebView应知应会
  • 307
分享到

Android网页加载控件WebView应知应会

webviewAndroid 2022-06-06 13:06:18 307人浏览 八月长安
摘要

目录一.什么是WEBView?WebView能干啥?二.玩一玩WebView常用api基本使用1.添加网络权限2.实例化WebView3.设置W

目录一.什么是WEBView?WebView能干啥?二.玩一玩WebView常用api基本使用1.添加网络权限2.实例化WebView3.设置WebClient4.设置WebChromeClient5.如何和js交互网页通过Scheme方式跳转其他应用1.scheme是什么?2.网页如果跳转app?3.推荐做法三.一些小坑四.发散学习五.总结 一.什么是WebView?WebView能干啥?

WebView 是一个用来显示 Web 网页的控件,它就是一个微型浏览器,包含一个浏览器该有的基本功能,例如:滚动、缩放、前进、后退下一页、搜索、执行 Js等功能。在Android4.4之前使用的是WebKit的内核,之后使用的是Chrome内核。

二.玩一玩WebView 常用API

void loadUrl(String url):加载网络链接 url
boolean canGoBack():判断 WebView 当前是否可以返回上一页
goBack():回退到上一页
boolean canGoForward():判断 WebView 当前是否可以向前一页
goForward():回退到前一页
onPause():类似 Activity 生命周期,页面进入后台不可见状态
pauseTimers():该方法面向全局整个应用程序的webview,它会暂停所有webview的layout,parsing,javascript Timer。当程序进入后台时,该方法的调用可以降低CPU功耗。
onResume():在调用 onPause()后,可以调用该方法来恢复 WebView 的运行。
resumeTimers():恢复pauseTimers时的所有操作。(注:pauseTimers和resumeTimers 方法必须一起使用,否则再使用其它场景下的 WebView 会有问题)
destroy():销毁 WebView
clearHistory():清除当前 WebView 访问的历史记录。
clearCache(boolean includeDiskFiles):清空网页访问留下的缓存数据。需要注意的时,由于缓存是全局的,所以只要是WebView用到的缓存都会被清空,即便其他地方也会使用到。该方法接受一个参数,从命名即可看出作用。若设为false,则只清空内存里的资源缓存,而不清空磁盘里的。
reload():重新加载当前请求
setLayerType(int layerType, Paint paint):设置硬件加速、软件加速
removeAllViews():清除子view。
clearSslPreferences():清除ssl信息。
clearMatches():清除网页查找的高亮匹配字符。
removeJavascriptInterface(String interfaceName):删除interfaceName 对应的注入对象
addJavascriptInterface(Object object,String interfaceName):注入 java 对象。
setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled):设置垂直方向滚动条。
setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled):设置横向滚动条。
loadUrl(String url, Map additionalHttpHeaders):加载制定url并携带http header数据。
evaluateJavascript(String script, ValueCallback resultCallback):Api 19 之后可以采用此方法之行 Js。
stopLoading():停止 WebView 当前加载。
clearView():在Android 4.3及其以上系统这个api被丢弃了, 并且这个api大多数情况下会有bug,经常不能清除掉之前的渲染数据。官方建议通过loadUrl(“about:blank”)来实现这个功能,阴雨需要重新加载一个页面自然时间会收到影响。
freeMemory():释放内存,不过貌似不好用。
clearFORMData():清除自动完成填充的表单数据。需要注意的是,该方法仅仅清除当前表单域自动完成填充的表单数据,并不会清除WebView存储到本地的数据。

基本使用

基本上是和WebSettings和WebClient、WebChromeClient搭配使用,还有和JS一些交互等

1.添加网络权限
 
2.实例化WebView

    

这里的WebView是单独封装的,配合WebSetting给WebView设置一些参数,比如支持js等

package com.seven.webview;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class SevenWebView extends WebView {
    public SevenWebView(Context context) {
        super(context);
        init(context);
    }
    public SevenWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    
    private void init(Context context) {
        WebSettings webSettings = getSettings();
        if (webSettings == null) return;
        // 支持 Js 使用
        webSettings.setJavaScriptEnabled(true);
        // 开启DOM缓存,默认状态下是不支持LocalStorage的
        webSettings.setDomStorageEnabled(true);
        // 开启数据库缓存
        webSettings.setDatabaseEnabled(true);
        // 设置 WebView 的缓存模式
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        // 支持缩放
        webSettings.setSupportZoom(true);
        // 设置 UserAgent 属性
        webSettings.setUserAgentString("");
        // 允许加载本地 html 文件/false
        webSettings.setAllowFileAccess(true);
        // 允许通过 file url 加载的 Javascript 读取其他的本地文件,Android 4.1 之前默认是true,在 Android 4.1 及以后默认是false,也就是禁止
        webSettings.setAllowFileAccessFromFileURLs(false);
        // 允许通过 file url 加载的 Javascript 可以访问其他的源,包括其他的文件和 http,https 等其他的源,
        // Android 4.1 之前默认是true,在 Android 4.1 及以后默认是false,也就是禁止
        // 如果此设置是允许,则 setAllowFileAccessFromFileURLs 不起做用
        webSettings.setAllowUniversalAccessFromFileURLs(false);
        //设置背景
        setBackgroundColor(getResources().getColor(android.R.color.transparent));
    }
}
3.设置WebClient

注意重写shouldOverrideUrlLoading这个方法,需要调用webView去加载网页,否则,会有系统浏览器去加载的,理解这句话的意思第一次加载还是用webView,但是网页上如果有链接,就不会拦截了。

package com.seven.webview;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.util.Log;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class SevenWebClient extends WebViewClient {
    private String TAG=getClass().getSimpleName();
    
    @Override
    public void onScaleChanged(WebView view, float oldScale, float newScale) {
        super.onScaleChanged(view, oldScale, newScale);
        Log.d(TAG, "onScaleChanged:oldScale="+oldScale+",newScale="+newScale);
    }
    
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Log.d(TAG, "shouldOverrideUrlLoading: "+url);
        view.loadUrl(url);
        return true;
    }
    
    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
        Log.d(TAG, "onPageStarted: "+url);
    }
    
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        Log.d(TAG, "onPageFinished: "+url);
    }
    
    @Override
    public void onLoadResource(WebView view, String url) {
        super.onLoadResource(view, url);
        Log.d(TAG, "onLoadResource: "+url);
    }
    
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        return super.shouldInterceptRequest(view, request);
    }
    
    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        super.onReceivedError(view, request, error);
        Log.d(TAG, "onReceivedError: "+error);
    }
    
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        super.onReceivedSslError(view, handler, error);
        Log.d(TAG, "onReceivedSslError: "+error);
    }
}
4.设置WebChromeClient

onProgressChanged这个方法会回调网页加载的进度,可以做一些进度条什么的,显示友好效果。

package com.seven.webview;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.webkit.ConsoleMessage;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

public class SevenWebChromeClient extends WebChromeClient {
    private String TAG=getClass().getSimpleName();
    private Context mContenxt;
    public SevenWebChromeClient(Context context) {
        this.mContenxt = context;
    }
    
    @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        Log.d(TAG, "onConsoleMessage: "+consoleMessage.message());
        return super.onConsoleMessage(consoleMessage);
    }
    
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
        Log.d(TAG, "onProgressChanged: "+newProgress);
    }
    
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        Log.d(TAG, "onJsAlert: "+message);
        return super.onJsAlert(view, url, message, result);
    }
    
    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        Log.d(TAG, "onJsConfirm: "+message);
        return super.onJsConfirm(view, url, message, result);
    }
    
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        Log.d(TAG, "onJsPrompt: "+message);
        return super.onJsPrompt(view, url, message, defaultValue, result);
    }
    
    @Override
    public void onReceivedIcon(WebView view, Bitmap icon) {
        super.onReceivedIcon(view, icon);
    }
    
    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
        Log.d(TAG, "onReceivedTitle: "+title);
    }
}
5.如何和JS交互

在我自定义的WebView的时候,就已经设置过支持javascript了,所以这里只要实例化一个java对象,并通过addJavascriptInterface这个方法将java对象注入进去。
实力恶化一个java对象,被js调用的方法上需要加上@JavascriptInterface这个注解

package com.seven.webview;
import android.content.Context;
import android.webkit.JavascriptInterface;
import android.widget.Toast;

public class NativeJsInterface {
    private Context mContext;
    public NativeJsInterface(Context context) {
        mContext = context;
    }
    @JavascriptInterface
    public void helloAndroid() {
        Toast.makeText(mContext, "Hello Android!", Toast.LENGTH_SHORT).show();
    }
    @JavascriptInterface
    public String getAndroid() {
        Toast.makeText(mContext, "getAndroid", Toast.LENGTH_SHORT).show();
        return "Android data";
    }
}
package com.seven.webview;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
    private String TAG=getClass().getSimpleName();
    @BindView(R.id.seven_web)
    WebView webView;
    private WebViewClient webViewClient;
    private WebChromeClient webChromeClient;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        loadUrl();
        supportJs();
    }
    
    private void loadUrl() {
        webViewClient = new SevenWebClient();
        webChromeClient = new SevenWebChromeClient(this);
        webView.setWebViewClient(webViewClient);
        webView.setWebChromeClient(webChromeClient);
        webView.loadUrl("file:///android_asset/html/ts.html");
    }
    
    private void supportJs() {
        NativeJsInterface nativeJsInterface = new NativeJsInterface(this);
        webView.addJavascriptInterface(nativeJsInterface, "NativeObject");
    }
    
    @OnClick(R.id.call_js)
    public void onViewClicked() {
        String jsMethod="javascript:androidCallJs()";
        if(Build.VERSION.SDK_INT< Build.VERSION_CODES.KITKAT){
            webView.loadUrl(jsMethod);
        }else {
              webView.evaluateJavascript(jsMethod, new ValueCallback() {
                  @Override
                  public void onReceiveValue(String value) {
                      Log.d(TAG, "onReceiveValue: "+value);
                  }
              });
        }
    }
}

6.写一个简单的网页


    <a href="https://www.lsjlt.com/tag/测试/" target="_blank"><strong class="keylink">测试</strong></a>页
        function callAndroid(){
            NativeObject.helloAndroid();
        }
        function getAndroid(){
            NativeObject.getAndroid();
        }
       function androidCallJs(){
        alert('js方法被执行!!!')
       }
       function jumpApp(){
       }
    
    
 重新打开自己

通过以上6步就可以完成基本的加载网页,支持java和js互相调用的功能了。

网页通过Scheme方式跳转其他应用 1.scheme是什么?

android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;通过scheme协议,服务器可以定制化告诉App跳转那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等。schema也是【隐示启动】中的一种,在data属性下的,其余内容查看Android Intent的隐示启动(启动其余APP界面并传递数据)
Scheme的格式:客户端自定义的 URL 作为从一个应用调用另一个的基础,遵循 RFC 1808 (Relative Uniform Resource Locators) 标准。这跟我们常见的网页内容 URL 格式一样。一个普通的 URL 分为几个部分,scheme、host、relativePath、query。

2.网页如果跳转app?

1)首先,你自定义一个scheme,我在上面的页面代码中已经写过了。
2)如果在webViewclient拦截了,在webView中使用shouldOverrideUrlLoading中去拦截

    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Log.d(TAG, "shouldOverrideUrlLoading: "+url);
        if (url.startsWith("scheme")) {
            Log.d(TAG, "shouldOverrideUrlLoading: 处理自定义scheme-->" + url);
            try {
                // 以下固定写法
                final Intent intent = new Intent(Intent.ACTION_VIEW,
                        Uri.parse(url));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                mContext.startActivity(intent);
            } catch (Exception e) {
                // 防止没有安装的情况
                e.printStackTrace();
                Toast.makeText(mContext,"您所打开的第三方App未安装!",Toast.LENGTH_SHORT).show();
            }
            return true;
        }
        view.loadUrl(url);
        return true;
    }

3)如果没有拦截,准确的来说是没有设置过webViewClient,可以直接用页面的方法直接打开。Android清单文件如下:


3.推荐做法

一般的做法都是拦截,在拦截出去启动app。不过也可以单独提供native方法给页面使用,让页面去跳转app。实现的方法可以有多种。

三.一些小坑

1.onPause() 尽力尝试暂停可以暂停的任何处理,如动画和地理位置。 不会暂停JavaScript。 要全局暂停JavaScript,可使用pauseTimers。onResume() 恢复onPause() 停掉的操作;pauseTimers() 暂停所有WebView的布局,解析和JavaScript定时器。 这个是一个全局请求,不仅限于这个WebView。resumeTimers() 恢复所有WebView的所有布局,解析和JavaScript计时器,将恢复调度所有计时器.另外注意 JS 端setTimeout()、setInterval() 方法使用,当不使用 pauseTimers() 和pauseTimers() ,从 Activity 返回上一个包含WebView 的Activity时,页面里的 setTimeout() 是不执行的,setInterval() 是可以恢复执行的。在适当的生命周期使用 pauseTimers() 和 pauseTimers() 既可以恢复setTimeout() 执行。

2.扩展的java方法是在子线程中执行的,更新UI需要切换至主线程,否则会报错!

3.跨域的小坑,记得设置setAllowFileAccessFromFileURLs(true)这个方法

4.页面和native保存账号的坑
原生和页面嵌套时,比如登录时,如何同步账号的问题,一般的解法都是使用cookieManage这个来保存原生登录时的cookie,以确保在网页操作时不至于需要重复登录。

5.文件选择,重写WebChromeClient的这几个方法

 // For Android < 3.0
    public void openFileChooser(ValueCallback valueCallback) {
        if(null!=valueCall){
            valueCall.onValueSelect(valueCallback);
        }
        openImageChooserActivity();
    }
    // For Android  >= 3.0
    public void openFileChooser(ValueCallback valueCallback, String acceptType) {
        if(null!=valueCall){
            valueCall.onValueSelect(valueCallback);
        }
        openImageChooserActivity();
    }
    //For Android  >= 4.1
    public void openFileChooser(ValueCallback valueCallback, String acceptType,  String capture) {
//        uploadMessage = valueCallback;
        if(null!=valueCall){
            valueCall.onValueSelect(valueCallback);
        }
        openImageChooserActivity();
    }
    // For Android >= 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        if(null!=valueCall){
            valueCall.onValuesSelectAboveL(filePathCallback);
        }
        openImageChooserActivity();
        return true;
    }
    private void openImageChooserActivity() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        mContenxt.startActivityForResult(Intent.createChooser(i, "Image Chooser"),     FILE_CHOOSER_RESULT_CODE);
    }
四.发散学习

安全漏洞·:Android 4.2 以下不要在使用 JavascriptInterface方式,4.2 以上需要添加注解 @JavascriptInterface 才能调用。(这部分和JsBrige 有关,更详细的内容后面会介绍)
当系统辅助功能服务被开启时,在 Android 4.4 以下的系统中,由系统提供的 WebView 组件都默认导出 ”accessibility” 和 ”accessibilityTraversal” 这两个接口,这两个接口同样存在远程任意代码执行的威胁,同样的需要通过 removeJavascriptInterface 方法将这两个对象删除。

       super.removeJavascriptInterface("searchBoxJavaBridge_");
       super.removeJavascriptInterface("accessibility");
       super.removeJavascriptInterface("accessibilityTraversal");

webView缓存问题
参考:WebView缓存原理分析和应用
如何优化加载速度
参考:WebView性能、体验分析与优化

五.总结

本文整理了一些webView常见用法、和js交互以及一些小坑。希望能对你有所帮助!如果觉得对你有用,我也觉得很高兴,毕竟技术是拿来分享的。


作者:android_seven


--结束END--

本文标题: Android网页加载控件WebView应知应会

本文链接: https://lsjlt.com/news/29111.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作