Android进阶之路 - WebView全面剖析

因为最近在项目开发中使用的WebView场景相对较多,索性抽了一些时间把网上目前关于WebView的API进行了一次大总结(小部分API与方法重复,但是不影响查看与使用)

我那些关于WebView的回忆 ~ 包含入门使用、优化加载样式、监听加载状态、各场景后退键处理、俩端交互流程、header、user-agent传值、交互常见问题、较全API整合

友情提示 - 阅读更多 - 展开全文 - Ctrl+F - 搜索关键词

最近在抽时间整理该篇,整理完毕之后会删除此行 !!!

WebSettings 配置

获取配置:一切配置建立在此基础之上

//获取配置实例
WebSettings webSettings = webView.getSettings();
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);  

//支持插件
webSettings.setPluginsEnabled(true); 

//定位(location)
webSettings.setGeolocationEnabled(true);

//是否保存表单数据
webSettings.setSaveFormData(true);

//是否当webview调用requestFocus时为页面的某个元素设置焦点,默认值 true
webSettings.setNeedInitialFocus(true);  

//是否支持多窗口,默认值false
webSettings.setSupportMultipleWindows(false);

//布局算法
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);

当前页面请求是否允许进行定位

@param origin 权限设置的源地址
@param allow 是否允许定位
@retain 当前的选择是否让内核记住
public void onGeolocationPermissionsShowPrompt(String origin,  
            GeolocationPermissions.Callback callback)  
  • 资源访问
//是否可访问Content Provider的资源,默认值 true
webSettings.setAllowContentAccess(true); 
// 是否可访问本地文件,默认值 true
webSettings.setAllowFileAccess(true);    
// 是否允许通过file url加载的Javascript读取本地文件,默认值 false
webSettings.setAllowFileAccessFromFileURLs(false);  
// 是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false
webSettings.setAllowUniversalAccessFromFileURLs(false);
  • 设置自适应屏幕,两者合用
//将图片调整到适合webview的大小 
webSettings.setUseWideViewPort(true); 
// 缩放至屏幕的大小
webSettings.setLoadWithOverviewMode(true);
  • 缩放操作
//支持缩放,默认为true 是下面那个的前提 
webSettings.setSupportZoom(true);
//设置内置的缩放控件 若为false,则该WebView不可缩放
webSettings.setBuiltInZoomControls(true); 
//隐藏原生的缩放控件
webSettings.setDisplayZoomControls(false); 
  • 资源加载
// 是否自动加载图片
webSettings.setLoadsImagesAutomatically(true); 
// 禁止加载网络图片
webSettings.setBlockNetworkImage(false);   
// 禁止加载所有网络资源
webSettings.setBlockNetworkLoads(false);      
  • 默认文本编码,默认值 “UTF-8”
//默认值 "UTF-8"
webSettings.setDefaultTextEncodingName("UTF-8");
// 默认文字尺寸,默认值16,取值范围1-72
webSettings.setDefaultFontSize(16);        
// 默认等宽字体尺寸,默认值16
webSettings.setDefaultFixedFontSize(16);  
// 最小文字尺寸,默认值 8
webSettings.setMinimumFontSize(8);         
// 最小文字逻辑尺寸,默认值 8
webSettings.setMinimumLogicalFontSize(8);  
// 文字缩放百分比,默认值 100
webSettings.setTextZoom(100);              
  • 字体
// 标准字体,默认值 "sans-serif"
webSettings.setStandardFontFamily("sans-serif");   
// 衬线字体,默认值 "serif"
webSettings.setSerifFontFamily("serif");           
// 无衬线字体,默认值 "sans-serif"
webSettings.setSansSerifFontFamily("sans-serif");
// 等宽字体,默认值 "monospace"
webSettings.setFixedFontFamily("monospace");       
// 手写体(草书),默认值 "cursive"
webSettings.setCursiveFontFamily("cursive");
// 幻想体,默认值 "fantasy"
webSettings.setFantasyFontFamily("fantasy");

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    // 用户是否需要通过手势播放媒体(不会自动播放),默认值 true
    webSettings.setMediaPlaybackRequiresUserGesture(true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // 5.0以上允许加载http和https混合的页面(5.0以下默认允许,5.0+默认禁止)
    webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    // 是否在离开屏幕时光栅化(会增加内存消耗),默认值 false
    webSettings.setOffscreenPreRaster(false);
}

if (isNetworkConnected(context)) {
    // 根据cache-control决定是否从网络上取数据
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
} else {
    // 没网,离线加载,优先加载缓存(即使已经过期)
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
  • 其他细节操作
//关闭webview中缓存 
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//设置可以访问文件 
webSettings.setAllowFileAccess(true); 
//支持通过JS打开新窗口 
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
//支持自动加载图片
webSettings.setLoadsImagesAutomatically(true);
//设置编码格式
webSettings.setDefaultTextEncodingName("utf-8");

常用API

基础

  • 获取控制 WebView 的对象
WebSettings getSettings () 
  • 注入 Javascript 对象(交互)
void addJavascriptInterface(Object object, String name)
  • 删除之前注入的 Javascript 对象
void removeJavascriptInterface(String name) 
  • 重新加载当前的URL
void reload()

H5栈:网页数量、前进、后退

  • 获取WebView栈内存储了多少子页面
 WebBackForwardList webBackForwardList = mWebView.copyBackForwardList();
  • 是否可以后退
Webview.canGoBack() 
  • 后退网页
Webview.goBack()
  • 是否可以前进
Webview.canGoForward()
  • 前进网页
Webview.goForward()

H5状态:onResume、onPause

  • 激活 WebView 为活跃状态,能正常执行网页的响应
webView.onResume();
  • 当页面被失去焦点被切换到后台不可见状态,需要执行 onPause,通过 onPause 动作通知内核暂停所有的动作,比如 DOM 解析plugin执行JavaScript执行
webView.onPause();
  • 当应用程序(存在webview)被切换到后台时,全局的webview暂停请求(暂停所有webview的布局,HTML解析和JavaScript定时器)

这个方法不仅仅针对当前的 webview 而是全局的 全应用程序的webview,它会暂停所有webview的 layoutparsingjavascripttimer 降低CPU功耗

webView.pauseTimers();
  • 恢复 pauseTimers状态,所有WebView的状态(布局的解析和JavaScript,计时器)
webView.resumeTimers();

避免OOM:WebView关闭时停止加载、清理缓存等

这部分有时候会被面试到,当然更多的是我们在开发中会及时回收资源,或许算是性能优化的一部分吧

  • 停止当前加载
void stopLoading()
  • 销毁Webview

在关闭了Activity时,如果 webview 的音乐或视频,还在播放 就必须销毁 webview ,但是注意: webview 调用destory时, webview 仍绑定在Activity上,这是由于自定义 webview 构建时传入了该Activity的context对象,因此需要先从父容器中移除 webview ,然后再销毁 webview

rootLayout.removeView(webView);
webView.destroy();
  • 清除当前webview访问的历史记录(只会 webview 访问历史记录里的所有记录除了当前访问记录)
Webview.clearHistory();
  • 清除网页访问留下的缓存(由于内核缓存是全局的因此这个方法不仅仅针对 webview 而是针对整个应用程序),

Tip:是否清理缓存要看具体场景,很多大型H5页面首次加载都会很慢,如果有缓存就会好很多

Webview.clearCache(true);
  • 仅清除自动完成填充的表单数据,并不会清除 webview 存储到本地的数据
Webview.clearFormData();

滑动:可指定滚条样式、亦可滑动顶部、底部

  • 指定滚动条的样式 滚动条可以重叠或嵌入
void setScrollBarStyle(int style) 
  • WebView 展示的页面滑动至底部
pageDown(boolean bottom) 
  • WebView 展示的页面滑动至顶部
pageUp(boolean top) 

获取H5信息:标题、加载进度、图标、高度

  • 获取当前页面标题
String getTitle()
  • 获取当前页面进度(0-100)
int getProgress()
  • 获取当前页面图标
Bitmap getFavicon () 
  • 获取HTML内容高度
int getContentHeight()

尚未规整

  • 以当前的index为起始点前进或者后退到历史记录中指定的steps
    如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)
  • 启动安全浏览
startSafeBrowsing(added in API level 27void startSafeBrowsing (Context context, ValueCallback<Boolean> callback)
  • 注册监听器,以通知页面操作进度
void setFindListener(WebView.FindListener listener) 
  • 默认实现KeyEvent.Callback.onKeyDown():当视图可以点击时,执行按下视图KEYCODE_DPAD_CENTER或KEYCODE_ENTER释放视图 (软件键盘上的按键通常不会触发这个监听器)如果你处理了事件,则返回true 如果您想让事件由下一个接收器处理,则返回false
boolean onKeyDown (int keyCode, KeyEvent event) 
  • 会显示WebView的图形缩放选择器的小部件
void invokeZoomPicker()
  • 获取是否在此WebView中启用隐私浏览
boolean isPrivateBrowsingEnabled () 
  • 获取当前页面的原始URL 这并不一定与传递给WebViewClient.onPageStarted的URL相同,因为URL的加载已经开始,但当前页面可能还没有更改 也可能出现重定向导致原始请求的URL不同
String getOriginalUrl()
  • 获取页面SSL证书,如果没有,就返回Null
SslCertificate getCertificate()
  • 突出显示并滚动到找到的下一个匹配项 findAllAsync(String),就是一条一条的搜索文本 如果调用了clearMatches()那么这个方法就不执行了
void findNext(boolean forward) 
  • 在页面上找到查找的所有字符并且异步突出显示,通知任何注册了WebView.FindListener
void findAllAsync(String find) 
  • 搜索地址(只有在美国的地址才会检测到)
String findAddress (String addr) 
  • 文档中是否有图像,参数Message负责发送结果消息
void documentHasImages (Message response)
  • 将按键事件分发到焦点路径上的下一个视图 该路径指的是从视图树的顶部向下延伸到当前有焦点的视图 如果这个View有焦点,那么就分发给自己 KeyEvent 指的是按键事件
boolean dispatchKeyEvent (KeyEvent event) 
  • 由父类调用,请求子类在必要时更新 mScrollXmScrollY 的值
void computeScroll () 
  • 为你的WebView提供打印功能,PrintDocumentAdapter会将WebView内容转换成PDF 参数documentName,具体可以查看 PrintDocumentInfo
PrintDocumentAdapter createPrintDocumentAdapter(String documentName) 
  • 通知应用当前页退出了全屏模式,此时应用必须隐藏之前显示的自定义View
 void onHideCustomView()  
  • webview请求得到focus,发生这个主要是当前webview不是前台状态,是后台webview
void onRequestFocus(WebView view)  
  • 通知应用程序从关闭传递过来的webview并从view tree中remove
 void onCloseWindow(WebView window)  
  • 接收JavaScript控制台消息
 boolean onConsoleMessage(ConsoleMessage consoleMessage)
  • 使用baseUrl加载内容
void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
  • 异步执行查找网页内包含的字符串并设置高亮,查找结果会回调
void findAllAsync (String find)
清除
  • 清除通过findAllAsync(String)匹配的高亮文本
void clearMatches()
  • 清除存储的SSL首选项表
void clearSslPreferences()
缩放
  • 缩放
void zoomBy(float factor)
  • 放大
boolean zoomIn()
  • 缩放
boolean zoomOut()
  • 通知WebView内核网络状态

用于设置JS属性window.navigator.isOnline和产生HTML5事件online/offline

void setNetworkAvailable(boolean networkUp)

常用方法

记得要声明网络权限~

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

加载方式、类型:网页、apk包中html页面、html页面

webView 加载地址设置 - shouldOverrideUrlLoading()

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

  //复写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
    webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
          view.loadUrl(url);
      return true;
      }
  });

通知应用程序内核即将加载url指定的资源,应用程序可以返回本地的资源提供给内核,若本地处理返回数据,内核不从网络上获取数据

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param url    raw url 制定的资源
@return 返回WebResourceResponse包含数据对象,或者返回null
public WebResourceResponse shouldInterceptRequest(WebView view,  String url)  

加载状态监听:开始、结束

  • 加载开始的操作 - onPageStarted()
webView.setWebViewClient(new WebViewClient(){
      @Override
      public void  onPageStarted(WebView view, String url, Bitmap favicon) {
         //设定加载开始的操作
      }
  });
  • 加载结束的操作 - onPageFinished()
  webView.setWebViewClient(new WebViewClient(){
      @Override
      public void onPageFinished(WebView view, String url) {
         //设定加载结束的操作
      }
  });
  • 加载资源的操作 - onLoadResource()
   webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean onLoadResource(WebView view, String url) {
         //设定加载资源的操作
      }
  });

监听 加载错误

  • 加载页面的服务器出现错误时(如404)调用 - onReceivedError()
//步骤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;
                }
            }
        });
  • webView默认不处理https请求,页面会显示空白 - onReceivedSslError()
webView.setWebViewClient(new WebViewClient() {    
        @Override    
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {    
            handler.proceed();    //表示等待证书响应
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
        }    
    });

获取 js 交互返回值

WebView调用JavaScript有参数有返回值的函数,此方法必须在UI线程上调用,回调也是在UI线程的(added in API level 19)

private void evaluateJavaScript(WebView webView){
webView.evaluateJavascript("getName()", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("WebView",s.toString);
    }
  });
}

方法9 :如果浏览器需要重新发送POST请求,可以通过这个时机来处理 默认是不重新发送数据

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param dontResent 当浏览器不需要重新发送数据时,可以使用这个参数 
@param resent 当浏览器需要重新发送数据时, 可以使用这个参数 
public void onFormResubmission(WebView view, Message dontResend,  
            Message resend)  

方法10 :通知应用程序可以将当前的url存储在数据库中,意味着当前的访问url已经生效并被记录在内核当中 这个函数在网页加载过程中只会被调用一次 注意网页前进后退并不会回调这个函数

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param url 当前正在访问的url 
@ param isReload 如果是true 这个是正在被reload的url
public void doUpdateVisitedHistory(WebView view, String url,  
            boolean isReload)  

方法11 :通知应用程序WebView接收到了一个Http auth的请求,应用程序可以使用supplied 设置webview的响应请求 默认行为是cancel 本次请求

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param handler 用来响应WebView请求的对象
@param host  请求认证的host
@param realm 认真请求所在的域
public void onReceivedHttpAuthRequest(WebView view,  
            HttpAuthHandler handler, String host, String realm)  

方法12 :提供应用程序同步一个处理按键事件的机会,菜单快捷键需要被过滤掉 如果返回true,webview不处理该事件,如果返回false, webview会一直处理这个事件,因此在view 链上没有一个父类可以响应到这个事件 默认行为是return false;

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param  event 键盘事件名
@return  如果返回true,应用程序处理该时间,返回false 交有webview处理 
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)   

方法13 :通知应用程序webview 要被scale 应用程序可以处理改事件,比如调整适配屏幕

public void onScaleChanged(WebView view, float oldScale, float newScale) 

方法14 :通知应用程序有个自动登录的帐号过程

@param view 请求登陆的webview
@param realm 账户的域名,用来查找账户 
@param account 一个可选的账户,如果是null 需要和本地的账户进行check, 如果是一个可用的账户,则提供登录 
@param  args  验证制定参数的登录用户
public void onReceivedLoginRequest(WebView view, String realm,  String account, String args)  

缓存方面

  • 当加载 html 页面时,WebView会在/data/data/包名目录下生成 databasecache 两个文件夹
  • 请求的 URL 记录保存在 WebViewCache.db,而 URL 的内容是保存在 WebViewCache 文件夹下
  • 是否启用缓存:
    //优先使用缓存: 
    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 缓存目录

获取 加载进度、Title

  • 获取WebView的title - onReceivedTitle()
webview.setWebChromeClient(new WebChromeClient(){

    @Override
    public void onReceivedTitle(WebView view, String title) {
       titleview.setText(title)}
  • 进度条展示 - onProgressChanged()
webview.setWebChromeClient(new WebChromeClient(){

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

方法18 :当前页面有个新的favicon时候,会回调这个函数(接收图标)

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param icon 当前页面的favicon
public void onReceivedIcon(WebView view, Bitmap icon)  

方法19 :通知应用程序 apple-touch-icon的 url,如果应用程序需要这个icon的话, 可以通过这个url获取得到 icon

@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param url  apple-touch-icon 的服务端地址
@param precomposed  如果precomposed 是true 则touch-icon是预先创建的
Tips 
public void onReceivedTouchIconUrl(WebView view, String url,  
            boolean precomposed)  

方法20 :通知应用程序webview需要显示一个custom view,主要是用在视频全屏HTML5Video support(通知应用当前页进入了全屏模式,此时应用必须显示一个包含网页内容的自定义View)

@param view 即将要显示的view
@param callback  当view 需要dismiss 则使用这个对象进行回调通知
public void onShowCustomView(View view, CustomViewCallback callback)  

对话框:onJsAlert、onJsConfirm、onJsPrompt、onJsBeforeUnload

  • 通知应用程序显示 javascript alert对话框,如果应用程序返回true内核认为应用程序处理这个消息,返回false,内核自己处理
@param view 接收 WebViewClient 的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview 
@param url 当前请求弹出javascript 对话框webview 加载的url地址 
@param message 弹出的内容信息
@result 用来响应用户的处理 

public boolean onJsAlert(WebView view, String url, String message,  
            JsResult result) 
  • 通知应用程序提供 confirm 对话框
boolean onJsConfirm(WebView view, String url, String message, JsResult result)  
  • 通知应用程序显示一个 prompt 对话框
boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) 
  • 通知应用程序显示一个对话框,让用户选择是否离开当前页面,这个回调是 javascript 中的 onbeforeunload 事件,如果客户端返回true,内核会认为客户端提供对话框 默认行为是return false
boolean onJsBeforeUnload(WebView view, String url, String message,  
            JsResult result)  

原始方法:直接调默认的上传图片,请求拍照

这个回调是私有回调, 当页面需要请求打开系统的文件选择器,则会回调这个方法,比如我们需要上传图片,请求拍照,邮件的附件上传等等操作

public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture)  

如果不实现这个私有API,则下面的请求都将不会执行

public void onGeolocationPermissionsHidePrompt()  

有趣

appcache、web sql 超出限额

通知应用程序 webview 内核 web sql 数据库超出配额,请求是否扩大数据库磁盘配额 默认行为是不会增加数据库配额

@param url 触发这个数据库配额的url地址
@param databaseIdentifier  指示出现数据库超过配额的标识 
@param quota   原始数据库配额的大小,是字节单位bytes
@param estimatedDatabaseSize  到达底线的数据大小 bytes
@param totalQuota 总的数据库配额大小 bytes
@param quotaUpdater 更新数据库配额的对象,可以使用 quotaUpdater.updateQuota(newQuota);配置新的数据库配额大小 
public void onExceededDatabaseQuota(String url, String databaseIdentifier,  
            long quota, long estimatedDatabaseSize, long totalQuota,  
            WebStorage.QuotaUpdater quotaUpdater) 

通知应用程序内核已经到达最大的 appcache

public void onReachedMaxAppCacheSize(long requiredStorage, long quota,  
            WebStorage.QuotaUpdater quotaUpdater)  

注意事项

避免WebView内存泄露

产生环境:不在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);

解决方案:在 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();
    }

借鉴文章

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

远方那座山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值