Android基础: WebView常用类、JS交互、内存泄漏

2018-07-11  本文已影响123人  红发_SHANKS

原始简单用法##

在布局文件中加入 WebView

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

使用LoadUrl()方法加载 WebView

WebView myWebView = (WebView) findViewById(R.id.webview);
// 如果在loadUrl()方法中,网页产生异常,并不会抛出到我们的 app 中
myWebView.loadUrl("http://www.example.com");

最后不要忘记添加权限

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

打开系统浏览器

 * Uri uri = Uri.parse("https://www.example.com");
 * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
 * startActivity(intent);

WebView 常用方法

// 常见的让返回键在网页中回退,而不是直接退出网页
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { 
        mWebView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

三个常用类##

WebSettings###

用于对WebView进行配置和管理

//声明WebSettings子类,如果 WebView 已经 destroy,再调用 WebSettings 的方法会抛出 IllegalStateException
WebSettings webSettings = webView.getSettings();

//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);  

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

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

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

//其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //网络可用就网络加载,否则使用缓存,通过改变常量值可以调整为只网络加载或者只缓存加载 
webSettings.setAllowFileAccess(true); //设置可以访问文件 
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式

// 设置网页字体大小
setTextZoom (int textZoom) //textZoom 默认大小是 100
// 设置是否支持导航
setGeolocationEnabled(boolean flag)

// 设置缓存模式和缓存地址的常规方法
 File cacheFile = this.getApplicationContext().getCacheDir();
 if (cacheFile != null) {
     // 这个方法应该只被调用一次,重复调用会被无视
     mWebView.getSettings().setAppCachePath(cacheFile.getAbsolutePath());
 }
 /**
  * 设置缓存加载模式
  * LOAD_DEFAULT(默认值):如果缓存可用且没有过期就使用,否则从网络加载
  * LOAD_NO_CACHE:从网络加载
  * LOAD_CACHE_ELSE_NETWORK:缓存可用就加载即使已过期,否则从网络加载
  * LOAD_CACHE_ONLY:不使用网络,只加载缓存即使缓存不可用也不去网络加载
  */
 int type = AppUtil.getNetWorkType(this);
 switch (type) {
     case AppUtil.NETWORKTYPE_4G:
     case AppUtil.NETWORKTYPE_WIFI:
         mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
         break;
     default:
         mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
         break;
 }

WebViewClient类###

处理各种通知和请求事件,当发生的事情影响到内容的渲染 (例如, 错误或表单提交) 时, 就会调用它,还可以在此处拦截 URL 加载。

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    // 如果链接不是常规的 url , 尝试使用系统浏览器打开
    if (URLUtil.isNetworkUrl(url)) {
        view.loadUrl(url);
    } else {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        PackageManager pm = getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
        if (activities.size() > 0) {
            startActivity(intent);
        }
    }

    return true;
}

// webView默认是不处理https请求的,页面显示空白,需要进行如下设置:
webView.setWebViewClient(new WebViewClient() {    
        @Override    
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {    
            handler.proceed();    //表示等待证书响应,继续进行请求
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
        }    
    }); 

WebChromeClient类###

如果说WebViewClient是帮助WebView处理各种通知、请求事件的“内政大臣”的话,那么WebChromeClient就是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等偏外部事件的“外交大臣”。

// gei WebView 添加一个进度条
@Override
public void onProgressChanged(WebView view, int newProgress) {
    if (newProgress < 100) {
        progress.setVisibility(View.VISIBLE);
        progress.setProgress(newProgress);
    } else {
        progress.setProgress(newProgress);
        progress.setVisibility(View.GONE);
    }
    super.onProgressChanged(view, newProgress);
}

// 将网页标题设置到 WebViewActivity
webview.setWebChromeClient(new WebChromeClient(){

    @Override
    public void onReceivedTitle(WebView view, String title) {
       titleview.setText(title);
    }
// 处理网页中有需要提交文件的情况
//For Android  >= 4.1
  public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
      uploadMessage = valueCallback;
      openImageChooserActivity();
  }

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

...
// 在Activity 中选择文件和回调
    private void openImageChooserActivity() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_CHOOSER_RESULT_CODE) {
            if (null == uploadMessage && null == uploadMessageAboveL) return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            if (uploadMessageAboveL != null) {
                onActivityResultAboveL(requestCode, resultCode, data);
            } else if (uploadMessage != null) {
                uploadMessage.onReceiveValue(result);
                uploadMessage = null;
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
        if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null)
            return;
        Uri[] results = null;
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                String dataString = intent.getDataString();
                ClipData clipData = intent.getClipData();
                if (clipData != null) {
                    results = new Uri[clipData.getItemCount()];
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        results[i] = item.getUri();
                    }
                }
                if (dataString != null)
                    results = new Uri[]{Uri.parse(dataString)};
            }
        }
        uploadMessageAboveL.onReceiveValue(results);
        uploadMessageAboveL = null;
    }

Js与WebView交互##

对于Android调用JS代码的方法有2种:

  1. 通过WebView的webView.loadUrl("javascript:methodName(parameterValues)"); // 适用于无返回值的 JS方法
  2. 通过WebView的evaluateJavascript(String script, ValueCallback<String> resultCallback)// 适用于有返回值的 JS 方法
// 设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);

try {
    InputStream is = FileUtil.getStream(WebViewActivity.this, "raw://inithtml");
    String js = FileUtil.readStreamString(is, "UTF-8");
    mWebView.loadUrl("javascript:" + js);
} catch (IOException e) {
    e.printStackTrace();
}

// 这种方法执行不会使页面刷新,但是 loadUrl执行 js 代码则会刷新页面
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        //此处为 js 返回的结果
    }
});
}

对于JS调用Android代码的方法有3种:

  1. 通过WebView的addJavascriptInterface()进行对象映射
  2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
  3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
// 1、在本地定义供 JS 调用的原生方法,被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void show(String s){
    Toast.makeText(getApplication(), s, Toast.LENGTH_SHORT).show();
}

// 2、通过WebView的addJavascriptInterface()进行对象映射,将JSObject类映射成为 JS 中的 JSObjectInJavascript 对象
mWebView.addJavascriptInterface(new JSObject(this), "JSObjectInJavascript");

// 3、编写 JS 方法,调用第一步中编写的原生方法
function toastClick(){
    window.android.show("JavaScript called~!");
}

WebView 内存泄露###

// 在 onPause()中暂停 webview
mWebView.pauseTimers

//在 onDestroy 中释放资源
if (mWebView != null) {
  ViewParent parent = mWebView.getParent();
  if (parent != null) {
     ((ViewGroup) parent).removeView(mWebView);
  }
  mWebView.removeAllViews();
  mWebView.destroy();
  mWebView = null;
}

参考
Android:你要的WebView与 JS 交互方式 都在这里了
Android:最全面的 Webview 详解
WebView·开车指南

上一篇下一篇

猜你喜欢

热点阅读