Android中的WebView使用总结

用途

加载网页

  • 说明:默认会打开自带的浏览器,使用WebView去加载,则需复写shouldOverrideUrlLoading方法,并返回为true
mWebView.setWebViewClient(new WebViewClient(){
   @Override
   public boolean shouldOverrideUrlLoading(WebView view, String url) {
       view.loadUrl(url);
       //返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
       return true;
   }
});
  • return值的说明:
    • true: 在打开新的url时WebView就不会再加载这个url了,所有处理都需要在WebView中操作,包含加载
    • false(默认): 系统就认为上层没有做处理,接下来还是会继续加载这个url的
      • 如果我们拦截了某个url,那么return false 和 return true区别不大,所以一般建议 return false

302问题

  • 通过判断HitTestResult类型, 并return false;
WebView webView = (WebView) findViewById(R.id.webview);
webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        HitTestResult hit = webView.getHitTestResult();
        int hitType = hit.getType();
        if (hitType == HitTestResult.SRC_ANCHOR_TYPE) {//点击超链接
        //这里执行自定义的操作                
            return true;//返回true浏览器不再执行默认的操作
        }else if(hitType == 0){//重定向时hitType为0
            return false;//不捕获302重定向
        }else{
            return false;
        }
    }
});

加载本地的html布局

  • 步骤:
    1. 将静态的html放在assets目录(与res目录同级)下
    2. 调用mWebView.loadUrl("file:///android_asset/neterror.html");

获取点击跳转的url地址

  • 自定义WebViewClient,复写shouldOverrideUrlLoading方法,返回为true,用于截获url地址,可以开启新的Activity页面
public boolean shouldOverrideUrlLoading(WebView view, String url) {
   return false;
}

获取标题

  • 自定义WebChromeClient,复写onReceivedTitle

清除之前的view的状态, 释放页面的资源(包括JS),

  • webView.loadUrl("about:blank");

播放视频

  • 5.0以下,setWebChromeClient中需添加onShowCustomView
  • 开启硬件加速 android:hardwareAccelerated="true"
  • 支持插件
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
webView.getSettings().setUseWideViewPort(true);
  • 兼容混合模式(https和http)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
  • 全屏

与JS的交互

Android调用JS

调用JS中的方法

  • 调用形式:mWebView.loadUrl("javascript:callJavaScriptMethod()");
  • 说明:callJavaScriptMethod()为JS中的方法

调用JS中的alert,confirm和prompt

  • 调用方式: 调用setWebChromeClient方法,复写onJsAlert,onJsConfirmonJsPrompt方法

JS调用Android

  • 调用形式:<a onClick="window.obj.invokeAndroid()">
  • 说明:
    1. obj为android中指定的调用名称;
    2. 添加mWebView.addJavascriptInterface(new MyJavaScriptInterface(), "obj");
    3. MyJavaScriptInterface为一个类,该类中有invokeAndroid()的方法,且该方法必须为public

双向交互

  • 思路:
    • 将前面的两种方式进行组合
  • 方式:
    1. JS 先调用Android,Android再调用JS中的方法 :
      • 在Android的MyJavaScriptInterface类中调用mWebView.loadUrl("javascript:wave()");
    2. Android先调用JS,JS再反过来调用Android中MyJavaScriptInterface类的方法
      • 目的:通过JS向Android的方法中传递参数,将该参数保存带全局变量
      • mWebView.loadUrl("javascript:window.obj.get");

使用小技巧

使用物理按键回退上一个网页(如果存在)

  • 复写onKeyDown方法
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

    if(keyCode==KeyEvent.KEYCODE_BACK {
        if(webView.canGoBack()){
            webView.goBack();//返回上一页面
            return true;
        } else{
            System.exit(0);//退出程序
        }
    }
    return super.onKeyDown(keyCode, event);
}

访问https网站

  • 如果出现某些https的网站无法访问,首先需要添加支持JS
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
  • 在2.2及以上系统中处理只需要重载WebViewClient 的 onReceivedSslError即可。
webview.setWebViewClient(new WebViewClient() {  
    @Override  
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)     
        //handler.cancel(); 默认的处理方式,WebView变成空白页   
        //handler.process();接受证书    
        //handleMessage(Message msg); 其他处理  
    }
});

与SwiperefreshLayout 结合使用

  • 可以调用WebView的reload方法,再次加载页面

通用配置

WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setSupportZoom(false);
webSettings.setBuiltInZoomControls(false);
webSettings.setAllowFileAccess(true);
webSettings.setDatabaseEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setGeolocationEnabled(true);
webSettings.setAppCacheEnabled(true);
webSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
webSettings.setDefaultTextEncodingName("UTF-8");

//屏幕自适应
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
} else {
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    webSettings.setDisplayZoomControls(false);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    webSettings.setLoadsImagesAutomatically(true);
} else {
    webSettings.setLoadsImagesAutomatically(false);
}

mWebView.setScrollBarStyle(WDWebView.SCROLLBARS_INSIDE_OVERLAY);
mWebView.setHorizontalScrollBarEnabled(false);
mWebView.setHorizontalFadingEdgeEnabled(false);
mWebView.setVerticalFadingEdgeEnabled(false);

注意事项:

播放视频:

  • 声音消失: 调用pauseTimers(), 需要注意在onResume调用resumeTimers(), 而且同一个进程中的WebView都会收到影响, 需要在onResume中 调用resumeTimers(), 不然会卡住

关于refer

  • 5.0以下手动设置会失效(官方bug)

关于select标签

  • WebView的初始化不能传入Application的context, 不然调不起来,或者crash

Cookie管理

  • 与原生cookie管理
CookieSyncManager.createInstance(mContext);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);

String[] cookievalues = cookies.split(";");
for (String value : cookievalues){
    cookieManager.setCookie(url,value);
}

if (Build.VERSION.SDK_INT < 21) {
    CookieSyncManager.getInstance().sync();
} else {
    CookieManager.getInstance().flush();
}

文件上传

  • WebChromeClient中提供了选择文件的方法, 但每个版本的内核的方法不同, 需要进行兼容处理

兼容处理

public ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> mUploadMessageForAndroid5;
private final int OPEN_GALLERY_CODE = 110;
private final int OPEN_GALLERY_CODE_ANDROID5 = 111;
 
//For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
    openChooserImpl(uploadMsg);
}

// For Android  > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
    openChooserImpl(uploadMsg);
}

//For Android 5.0+
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
    openFileChooserImplForAndroid5(filePathCallback);
    return true;
}

//3.0+
public void openChooserImpl(ValueCallback<Uri> uploadMsg) {
    mUploadMessage = uploadMsg;
    Intent intentImage = new Intent(Intent.ACTION_PICK);
    intentImage.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
    mActivity.startActivityForResult(intentImage, OPEN_GALLERY_CODE);
}

//5.0+
public void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
    mUploadMessageForAndroid5 = uploadMsg;
    Intent intentImage = new Intent(Intent.ACTION_PICK);
    intentImage.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
    mActivity.startActivityForResult(intentImage, OPEN_GALLERY_CODE_ANDROID5);
}

注意:

  • openFileChooser为hide的方法, 打包混淆需要在proguard-rules.pro中keep
-keepclassmembers  class  com.hinabian.migrate.activity.AtWebView$MyChromeViewClient{
    *;
}

参考