webview常规问题

版权声明:转发请标明原著 https://blog.csdn.net/weixin_39460667/article/details/83029499

 

一、简介

这部分主要介绍下 WebView,WebView 是一个用来显示 Web 网页的控件,继承自 AbsoluteLayout,和使用系统其他控件没什么区别,只是 WeView 控件方法比较多比较丰富。因为它就是一个微型浏览器,包含一个浏览器该有的基本功能,例如:滚动、缩放、前进、后退下一页、搜索、执行 Js等功能。

二、开始最简单的使用

2.1 加载一个最简单的页面

 2.2 xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ly_web"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </WebView>

</LinearLayout>

但是我们一般不是很支持直接在xml中静态页面写上 webview 控件,因为防止webview出现内存泄露,下文会提及到。

2.3 activity载入某宝页面

webview.getSettings().setJavaScriptEnabled(true);
webview.loadUrl(" https://www.taobao.com/");

2.4 在销毁activity的时候

   @Override
    protected void onDestroy() {
        if (webview != null) {
            webview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            webview.clearHistory();

            ((ViewGroup) webview.getParent()).removeView(webview);
            webview.destroy();
            webview = null;
        }
        super.onDestroy();
    }

2.5 因为要加载网页页面 切记记得加上权限

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

三、webview的渲染

webview的渲染主要就介绍两个类,下面先简单的介绍使用方式

WebViewClient

主要帮助WebView处理各种通知、请求事件的,有以下常用方法

onPageFinished 页面请求完成

onPageStarted 页面开始加载

shouldOverrideUrlLoading 拦截url

onReceivedError 访问错误时回调,例如访问网页时报错404,在这个方法回调的时候可以加载错误页面

WebChromeClient

主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等,有以下常用方法

onJsAlert webview不支持js的alert弹窗,需要自己监听然后通过dialog弹窗

onReceivedTitle 获取网页标题

onReceivedIcon 获取网页icon

onProgressChanged 加载进度回调

1、创建 WebViewClient实例

private WebViewClient webViewClient = 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 boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            //处理其他情况,比如国内不能访问google,拦截该url
            return super.shouldOverrideUrlLoading(view, request);
        }
    };

2.创建WebChromeClient实例

private WebChromeClient webChromeClient =new WebChromeClient(){
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            //加载进度回调
            super.onProgressChanged(view, newProgress);
        }

        @Override
        public void onReceivedTitle(WebView view, String title) {
            //获取网页标题
            super.onReceivedTitle(view, title);
        }

        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            //因为WebView不支持alert弹窗,在这个方法中用AlertDialog去弹窗
            return super.onJsAlert(view, url, message, result);
        }
    };

3.设置 WebViewClient 和 WebChromeClient

wv_baidu.setWebViewClient(webViewClient);
wv_baidu.setWebChromeClient(webChromeClient);

四、Webview类常用方法

4.1 加载url

//方式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");

第二种的html存放路径位于 app/src/build/assets

4.2  关于前进 / 后退网页

//是否可以后退
Webview.canGoBack() 
//后退网页
Webview.goBack()

我们常用于在重写 物理控件返回的回调 onBackPressed 之中,避免一触摸返回键就退出

    @Override
    public void onBackPressed() {
            if (webview.canGoBack()) {
                //后退
                webview.goBack();
            } else {
                //退出
                finish();
            }
    }

4.3  webview.requestFocusFromTouch();//设置支持获取手势焦点

五、WebSettings类

        //设置支持 webview js方法
        webview.getSettings().setJavaScriptEnabled(true);
        //打开 webview 插件
        webview.getSettings().setPluginState(WebSettings.PluginState.ON);

         //缓存处理

        if (isConnectIsNomarl()) {
            webview.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
        } else {
            webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
        }

        String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
        webview.getSettings().setGeolocationDatabasePath(dir);
        // 1. 设置缓存路径
        webview.getSettings().setAppCacheMaxSize(4*1024*1024);
        // 2. 设置缓存大小
        webview.getSettings().setAppCacheEnabled(true);


        //设置自适应屏幕,两者合用
        webview.getSettings().setUseWideViewPort(true); //将图片调整到适合webview的大小
        webview.getSettings().setLoadWithOverviewMode(true); // 缩放至屏幕的大小

        
       //缩放操作
        webview.getSettings().setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
        webview.getSettings().setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
        webview.getSettings().setDisplayZoomControls(false); //隐藏原生的缩放控件

        //其他细节操作
//        webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
        webview.getSettings().setAllowFileAccess(true); //设置可以访问文件
        webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        webview.getSettings().setLoadsImagesAutomatically(true); //支持自动加载图片
        webview.getSettings().setDefaultTextEncodingName("utf-8");//设置编码格式

六、js调用安卓的方法

通过小demo简单介绍

6.1、自创建一个html页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Carson</title>
    <script>
             function callAndroid(){
                // 由于对象映射,所以调用test对象等于调用Android映射的对象
                test.hello("凉凉");
             }
             function callJSOne(){
                 alert("Android调用了JS的callJS方法");
             }
              function callJSTwo(){
                 return "hello world";
             }
    </script>
</head>
<body>
<button type="button" id="button1" onclick="callAndroid()">点击调用安卓代码</button>

<a href="tel://17875305749 ">拨打电话</a>
</br>
<iframe height=498 width=510 src='http://player.youku.com/embed/XMzM4NTQ3ODcwNA==' frameborder=0
        allowfullscreen='true'></iframe>
</body>
</html>

6.2、使用  addJavascriptInterface 通过映射 ,使 js 拥有一个 java 的一个上下文引用。

使用案例  参数一 就是 上下文  ,参数二就是 引用

webview.addJavascriptInterface(new Webviewjs(WebViewDemoActivity.this), "test");
//         通过addJavascriptInterface()将Java对象test映射到JS对象
        webview.addJavascriptInterface(new Webviewjs(WebViewDemoActivity.this), "test");
//        设置一下路径,标准格式如下
        webview.loadUrl("file:///android_asset/myjs.html");

6.3、我们在html中已经给我们的button控件注册点击事件了

<button type="button" id="button1" onclick="callAndroid()">点击调用安卓代码</button>
             function callAndroid(){
                // 由于对象映射,所以调用test对象等于调用Android映射的对象
                test.hello("凉凉");
             }

6.4 传入 WebViewDemoJS类

public class Webviewjs extends Activity{
    private Context context;
    public Webviewjs(Context context) {
        this.context = context;
    }

    @JavascriptInterface
    public void hello(String msg) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }
}

七、安卓调用js的代码

(注意 想调用 js 必须在 onPageFinished 结束之后才够使用

webview.setWebViewClient(new WebViewClient() {

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                btnOne.setEnabled(true);
                btnTwo.setEnabled(true);
            }

在这里我给大家简单介绍,还有涉及一个小技巧

7.1、先写js的方法。

 function callJSOne(){
                 alert("Android调用了JS的callJS方法");
             }

7.2、给我们的原生按钮设置监听这些,我就不多加啰嗦了,直接上如何引用,及其一个方法

我们在上文已经说到 webview不支持 alert 弹框,所以我们可以这样,用 我们安卓的弹框来进行代替。

  webview.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                AlertDialog.Builder b = new AlertDialog.Builder(WebViewDemoActivity.this);
                b.setTitle("Alert");
//                Log.e("onJsAlert: ", );
                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;
            }
    }

调用的方式就是通过 webview.loadUrl 来进行调用 

  case R.id.btn_one:
                webview.post(new Runnable() {
                    @Override
                    public void run() {
                        // 注意调用的JS方法名要对应上
                        // 调用javascript的callJS()方法
                        webview.loadUrl("javascript:callJSOne()");
                    }
                });
                break;

为什么要通过post方法呢,因为在经常状态下我们都会独立的开一个进程给我们的webview使用,避免出现崩溃问题出现,导致整一个应用程序的崩溃。

你以为结束了吗,并没有,我这里还有一点小干货让你吸收。

其实 我们 不止 可以通过上面我介绍的那个方法来调用 js 方法 ,但是呢,我在这里不介绍,这是因为另一个方法并不支持 4.4以下的。但是那个方法又有反回值,我们可以获得他所返回给我们的数据。

这里我就是想来介绍一下我们如何通过   webview.loadUrl( ); 方法来获得 js 给我们的返回值 如下案例

 function callJSTwo(){
                 return "hello world";
             }
  case R.id.btn_two:
                webview.post(new Runnable() {
                    @Override
                    public void run() {
                        // 注意调用的JS方法名要对应上
                        // 调用javascript的callJS()方法
                        webview.loadUrl("javascript:alert(callJSTwo())");
                    }
                });

我相信你看完已经明白了。

8、进度问题

 webview.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                if (newProgress == 100) {
                    progress.setVisibility(View.GONE);
                } else {
                    progress.setVisibility(View.VISIBLE);
                }
            }

9、视频播放横放问题

lyweb是webview的父控件。

    @BindView(R.id.ly_web)
    LinearLayout lyWeb;  
  //视频切换
    private View myView = null;
    //内核
    private WebChromeClient chromeClient = null;
    private WebChromeClient.CustomViewCallback myCallBack = null;
        webview.setWebChromeClient(new WebChromeClient() {

            @Override
            public void onShowCustomView(View view, CustomViewCallback callback) {
                if (myView != null) {
                    callback.onCustomViewHidden();
                    return;
                }
                //设置横屏
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                myCallBack = callback;
                //隐藏网页
                lyWeb.removeView(webview);
                //添加视频
                lyWeb.addView(view);
                myView = view;
            }

            @Override
            public void onHideCustomView() {
                if (myView == null) {
                    return;
                }
                //设置竖屏
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                //隐藏视频
                lyWeb.removeView(myView);
                //添加网页
                lyWeb.addView(webview);
                myView = null;
                myCallBack.onCustomViewHidden();
            }
        });

10、点击打电话,发邮箱等等等操作。

     webview.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                if (url.startsWith("http:") || url.startsWith("https:")) {
                    return false;
                } else if (url.startsWith(WebView.SCHEME_TEL) ||
                        url.startsWith("sms:") ||
                        url.startsWith(WebView.SCHEME_MAILTO) ||
                        url.startsWith(WebView.SCHEME_GEO) ||
                        url.startsWith("maps:")) {
                    try {
                        Intent intent = new Intent(Intent.ACTION_VIEW);
                        intent.setData(Uri.parse(url));
                        startActivity(intent);
                    } catch (ActivityNotFoundException e) {
                    }
                }
                return true;
            }

 

11、解决https出错的问题

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

12、记得记得(避免出现泄露)

   @Override
    protected void onResume() {
        super.onResume();
        webview.getSettings().setJavaScriptEnabled(true);
        webview.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
        webview.getSettings().setJavaScriptEnabled(false);
    }

    @Override
    protected void onPause() {
        super.onPause();
        webview.onPause();
    }

    @Override
    protected void onDestroy() {
        if (webview != null) {
            webview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            webview.clearHistory();

            ((ViewGroup) webview.getParent()).removeView(webview);
            webview.destroy();
            webview = null;
        }
        super.onDestroy();
    }

 

13、坑


1.WebView内存泄漏

当你要用webview的时候,记得最好另外单独开一个进程去使用,并且当这个进程结束时,手动调用System.exit(0)。使用此方法,所有因为webview引发的资源无法释放等问题都可以解决  

https://blog.csdn.net/qq_36523667/article/details/79053625

https://www.jianshu.com/p/b66c225c19e2

2.getSettings().setBuiltInZoomControls(true) 引发的crush

这个方法调用以后 如果你触摸屏幕 弹出那个提示框还没消失的时候 你如果activity结束了 就会报错了。3.0以上 4.4以下很多手机会出现这种情况所以为了规避他,我们通常是在activity的onDestroy方法里手动的将webiew设置成 setVisibility(View.GONE)

3.onPageFinished 函数到底有用没有?

这个函数并没有什么卵用,有的时候是提前结束,有的时候就迟迟无法结束,你信这个函数 还不如信上帝,甚至于onProgressChanged这个函数都比onPageFinished 要准一些,更加靠谱的方法是使用onprogresschange方法代替该方法的功能,当newProgress为100的时候,即是页面加载完成

4.后台无法释放js 导致耗电

如果webview加载的html里 有一些js 一直在执行比如动画之类的东西,如果此刻webview 挂在了后台这些资源是不会被释放 用户也无法感知。。。导致一直占有cpu 耗电特别快,所以大家记住了,如果遇到这种情况 请在onstop和onresume里分别把setJavaScriptEnabled();给设置成false和true。
webview加载网页的时候,会自动创建线程,如果如果使用不当,这些线程会永远在后台运行,导致你的应用耗电量居高不下,这个问题的解决方式是在activity的ondetory方法中销毁webview

5.前端最好不要使用了ES6语法

6.webview动态添加到其他布局的时候,在activity销毁的生命周期时,需要主动调用webview.removeallviews和webview的ondestory方法释放内存,否则会导致内存泄漏

7.webview硬件加速导致渲染问题,比如加载的时候会有闪屏现象,解决方式就是暂时关闭硬件加速

8.webview导致内存溢出的原因,主要是因为内部类持有外部类的引用导致外部类无法释放的问题

9.关于webview.onPause()

通知内核尝试停止所有处理,如动画和地理位置,但是不能停止Js,如果想全局停止Js,可以调用**pauseTimers()**全局停止Js,调用onResume()恢复。
完结~
 

https://blog.csdn.net/weixin_37418246/article/details/82987594

https://www.jianshu.com/p/3c94ae673e2a

 https://www.jianshu.com/p/345f4d8a5cfa

 https://www.jianshu.com/p/5e7075f4875f

 https://www.jianshu.com/p/3a345d27cd42

https://blog.csdn.net/qq_30379689/article/details/51898640

https://blog.csdn.net/lijizhi19950123/article/details/77914601

猜你喜欢

转载自blog.csdn.net/weixin_39460667/article/details/83029499