Android中WebView使用详解及Java与js相互调用

经常用到WebView,还没有系统的总结过它的用法,下面就系统的总结下,不足的地方希望大家多多指教

一、基本用法

1、加载在线URL

void loadUrl(String url) 

这个函数主要加载url所对应的网页地址,或者用于调用网页中的指定的JS方法(调用js方法的用法,后面会讲),但有一点必须注意的是:loadUrl()必须在主线程中执行,否则就会报错。

注意:加载在线网页地址是会用到联网permission权限的,所以需要在AndroidManifest.xml中写入下面代码申请权限:

如果我们想在webview中打开在线网址需要怎么做呢?
我们需要设置WebViewClient即:mWebView.setWebViewClient(new WebViewClient());

2、加载本地URL

一般而言,我们会将本地html文件放在assets文件夹下,比如:

Android中WebView使用详解及Java与js相互调用_第1张图片

加载本地web.html文件代码如下:

 webView.loadUrl("file:///android_asset/web.html");

加载本地与加载在线URL有两点不同:

  • 1、URL类型不一样

在加载本地URL时,是以“file:///”开头的,而assets目录所对应的路径名为anroid_asset,写成其它的将识别不了,这是assets目录的以file开头的url形式的固定访问形式。

  • 2、不需要设置WebViewClient

加载本地URL不需要设置WebViewClient函数,但仍然是在webview中打开的本地文件。

3、WebView基本设置

如果我们需要设置WebView的属性,是通过WebView.getSettings()获取设置WebView的WebSettings对象,然后调用WebSettings中的方法来实现的。

WebSettings的方法及说明如下:

/** 
 * 是否支持缩放,配合方法setBuiltInZoomControls使用,默认true 
 */  
setSupportZoom(boolean support)  
  
/** 
 * 是否需要用户手势来播放Media,默认true 
 */  
setMediaPlaybackRequiresUserGesture(boolean require)  
  
/** 
 * 是否使用WebView内置的缩放组件,由浮动在窗口上的缩放控制和手势缩放控制组成,默认false 
 */  
setBuiltInZoomControls(boolean enabled)  
  
/** 
 * 是否显示窗口悬浮的缩放控制,默认true 
 */  
setDisplayZoomControls(boolean enabled)  
  
/** 
 * 是否允许访问WebView内部文件,默认true 
 */  
setAllowFileAccess(boolean allow)  
  
/** 
 * 是否允许获取WebView的内容URL ,可以让WebView访问ContentPrivider存储的内容。 默认true 
 */  
setAllowContentAccess(boolean allow)  
  
/** 
 * 是否启动概述模式浏览界面,当页面宽度超过WebView显示宽度时,缩小页面适应WebView。默认false 
 */  
setLoadWithOverviewMode(boolean overview)  
  
/** 
 * 是否保存表单数据,默认false 
 */  
setSaveFormData(boolean save)  
  
/** 
 * 设置页面文字缩放百分比,默认100% 
 */  
setTextZoom(int textZoom)  
  
/** 
 * 是否支持ViewPort的meta tag属性,如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口 
 */  
setUseWideViewPort(boolean use)  
  
  
/** 
 * 是否支持多窗口,如果设置为true ,WebChromeClient#onCreateWindow方法必须被主程序实现,默认false 
 */  
setSupportMultipleWindows(boolean support)  
  
/** 
 * 指定WebView的页面布局显示形式,调用该方法会引起页面重绘。默认LayoutAlgorithm#NARROW_COLUMNS 
 */  
setLayoutAlgorithm(LayoutAlgorithm l)  
  
/** 
 * 设置标准的字体族,默认”sans-serif”。font-family 规定元素的字体系列。 
 * font-family 可以把多个字体名称作为一个“回退”系统来保存。如果浏览器不支持第一个字体, 
 * 则会尝试下一个。也就是说,font-family 属性的值是用于某个元素的字体族名称或/及类族名称的一个 
 * 优先表。浏览器会使用它可识别的第一个值。 
 */  
setStandardFontFamily(String font)  
  
/** 
 * 设置混合字体族。默认”monospace” 
 */  
setFixedFontFamily(String font)  
  
/** 
 * 设置SansSerif字体族。默认”sans-serif” 
 */  
setSansSerifFontFamily(String font)  
  
/** 
 * 设置SerifFont字体族,默认”sans-serif” 
 */  
setSerifFontFamily(String font)  
  
/** 
 * 设置CursiveFont字体族,默认”cursive” 
 */  
setCursiveFontFamily(String font)  
  
/** 
 * 设置FantasyFont字体族,默认”fantasy” 
 */  
setFantasyFontFamily(String font)  
  
/** 
 * 设置最小字体,默认8. 取值区间[1-72],超过范围,使用其上限值。 
 */  
setMinimumFontSize(int size)  
  
/** 
 * 设置最小逻辑字体,默认8. 取值区间[1-72],超过范围,使用其上限值。 
 */  
setMinimumLogicalFontSize(int size)  
  
/** 
 * 设置默认字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。 
 */  
setDefaultFontSize(int size)  
  
/** 
 * 设置默认填充字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。 
 */  
setDefaultFixedFontSize(int size)  
  
/** 
 * 设置是否加载图片资源,注意:方法控制所有的资源图片显示,包括嵌入的本地图片资源。 
 * 使用方法setBlockNetworkImage则只限制网络资源图片的显示。值设置为true后, 
 * webview会自动加载网络图片。默认true 
 */  
setLoadsImagesAutomatically(boolean flag)  
  
/** 
 * 是否加载网络图片资源。注意如果getLoadsImagesAutomatically返回false,则该方法没有效果。 
 * 如果使用setBlockNetworkLoads设置为false,该方法设置为false,也不会显示网络图片。 
 * 当值从true改为false时。WebView会自动加载网络图片。 
 */  
setBlockNetworkImage(boolean flag)  
  
/** 
 * 设置是否加载网络资源。注意如果值从true切换为false后,WebView不会自动加载, 
 * 除非调用WebView#reload().如果没有android.Manifest.permission#INTERNET权限, 
 * 值设为false,则会抛出java.lang.SecurityException异常。 
 * 默认值:有android.Manifest.permission#INTERNET权限时为false,其他为true。 
 */  
setBlockNetworkLoads(boolean flag)  
  
/** 
 * 设置是否允许执行JS。 
 */  
setJavaScriptEnabled(boolean flag)  
  
/** 
 * 是否允许Js访问任何来源的内容。包括访问file scheme的URLs。考虑到安全性, 
 * 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的, 
 * 不会受到影响。ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本 
 * 以上默认为false 
 */  
setAllowUniversalAccessFromFileURLs(boolean flag)  
  
  
/** 
 * 是否允许Js访问其他file scheme的URLs。包括访问file scheme的资源。考虑到安全性, 
 * 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的, 
 * 不会受到影响。如果getAllowUniversalAccessFromFileURLs为true,则该方法被忽略。 
 * ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本以上默认为false 
 */  
setAllowFileAccessFromFileURLs(boolean flag)  
  
/** 
 * 设置存储定位数据库的位置,考虑到位置权限和持久化Cache缓存,Application需要拥有指定路径的 
 * write权限 
 */  
setGeolocationDatabasePath(String databasePath)  
  
/** 
 * 是否允许Cache,默认false。考虑需要存储缓存,应该为缓存指定存储路径setAppCachePath 
 */  
setAppCacheEnabled(boolean flag)  
  
/** 
 * 设置Cache API缓存路径。为了保证可以访问Cache,Application需要拥有指定路径的write权限。 
 * 该方法应该只调用一次,多次调用自动忽略。 
 */  
setAppCachePath(String appCachePath)  
  
/** 
 * 是否允许数据库存储。默认false。查看setDatabasePath API 如何正确设置数据库存储。 
 * 该设置拥有全局特性,同一进程所有WebView实例共用同一配置。注意:保证在同一进程的任一WebView 
 * 加载页面之前修改该属性,因为在这之后设置WebView可能会忽略该配置 
 */  
setDatabaseEnabled(boolean flag)  
  
/** 
 * 是否存储页面DOM结构,默认false。 
 */  
setDomStorageEnabled(boolean flag)  
  
/** 
 * 是否允许定位,默认true。注意:为了保证定位可以使用,要保证以下几点: 
 * Application 需要有android.Manifest.permission#ACCESS_COARSE_LOCATION的权限 
 * Application 需要实现WebChromeClient#onGeolocationPermissionsShowPrompt的回调, 
 * 接收Js定位请求访问地理位置的通知 
 */  
setGeolocationEnabled(boolean flag)  
  
/** 
 * 是否允许JS自动打开窗口。默认false 
 */  
setJavaScriptCanOpenWindowsAutomatically(boolean flag)  
  
/** 
 * 设置页面的编码格式,默认UTF-8 
 */  
setDefaultTextEncodingName(String encoding)  
  
/** 
 * 设置WebView代理,默认使用默认值 
 */  
setUserAgentString(String ua)  
  
/** 
 * 通知WebView是否需要设置一个节点获取焦点当 
 * WebView#requestFocus(int,android.graphics.Rect)被调用的时候,默认true 
 */  
setNeedInitialFocus(boolean flag)  
  
/** 
 * 基于WebView导航的类型使用缓存:正常页面加载会加载缓存并按需判断内容是否需要重新验证。 
 * 如果是页面返回,页面内容不会重新加载,直接从缓存中恢复。setCacheMode允许客户端根据指定的模式来 
 * 使用缓存。 
 * LOAD_DEFAULT 默认加载方式 
 * LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存 
 * LOAD_NO_CACHE 不使用缓存 
 * LOAD_CACHE_ONLY 只使用缓存 
 */  
setCacheMode(int mode)  
  
/** 
 * 设置加载不安全资源的WebView加载行为。KITKAT版本以及以下默认为MIXED_CONTENT_ALWAYS_ALLOW方 
 * 式,LOLLIPOP默认MIXED_CONTENT_NEVER_ALLOW。强烈建议:使用MIXED_CONTENT_NEVER_ALLOW 
 */  
setMixedContentMode(int mode)  

二、JS调用Java代码

1、概述

更多时候,网页中需要通过JS代码来调用本地的Android代码,比如H5页面需要判断当前用户是否登录等。
利用JS代码调用JAVA代码,主要是用到WebView下面的一个函数:

public void addJavascriptInterface(Object obj, String interfaceName)  

这个函数有两个参数:

  • Object obj:interfaceName所绑定的对象
  • String interfaceName:所绑定的对象所对应的名称

它有意义就是向WebView注入一个interfaceName的对象,这个对象绑定的是obj对象,通过interfaceName就可以调用obj对象中的方法。interfaceName可以理解为对象的别名

2、示例

下面是上面的概述,效果图如下:

Android中WebView使用详解及Java与js相互调用_第2张图片

在原来html上面添加了一个按钮,当点击按钮时调用Android的getMessage函数Android代码中按钮的文字改变
先看Android代码:

public class WebViewActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_webView;
    private WebView webView;
    private WebSettings settings;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);
        btn_webView = findViewById(R.id.btn_webView);
        btn_webView.setText("加载URL");
        webView = findViewById(R.id.webView);
        settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);//支持JavaScript脚本
        webView.addJavascriptInterface(this, "android");
        btn_webView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        webView.loadUrl("file:///android_asset/web.html");
    }
    
    public void getMessage(String message) {
        btn_webView.setText(message);
    }

}

这里最主要是的下面这句:

 webView.addJavascriptInterface(this, "android");

这句的意思是把WebViewActivity对象注入到WebView中,在WebView中的对象别名叫android;另外,我们还在WebViewActivity中额外写了一个函数getMessage(String message),用于显示MSG
下面我们看看html代码: 




    
    Title
    

我是html页面

在这个html中,我添加了一个button按钮,当点击时调用ok函数:

function ok() {
 android.getMessage("哈哈,js调用了java本地方法");
}

它的意义就是调用android对象里的getMessage方法,这个android就是我们利用webView.addJavascriptInterface(this, “android”)注入到WebView的android,它所对应的对象就将WebViewActivity;所以在JS中,我们就可以通过android这个别名来调用WebViewActivity对象中的任何public方法。

3、addJavascriptInterface自定义作用对象

在上面的示例中webView.addJavascriptInterface(this, “android”);我们直接通过this,把当前整个类作为对象传给WebView了,但这会有很大风险,因为我们这个类中可能会有各种函数,而这些函数是只有本地Native代码才会用到,WebView是根本用不到的。所以如果通过全部注入给WebView的话,那么一些存心不良的同学就可以任意调用我们这个类中的方法,给我们APP带来危害。
所以,一般而言,我们很少直接会传this,把整个类注入给WebView,而是单独写一个类专门用于JS交互,比如:

public class WebViewActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_webView;
    private WebView webView;
    private WebSettings settings;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);
        btn_webView = findViewById(R.id.btn_webView);
        btn_webView.setText("加载URL");
        webView = findViewById(R.id.webView);
        settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);//支持JavaScript脚本
        webView.addJavascriptInterface(new JsInterface(), "android");
        btn_webView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        webView.loadUrl("file:///android_asset/web.html");
    }

    public class JsInterface {

        public void getMessage(String message) {
            btn_webView.setText(message);
        }
    }


}

在这段代码中,在通过addJavascriptInterface注入时:

  webView.addJavascriptInterface(new JsInterface(), "android");

 指定android对象绑定的是JSBridge对象!所以在WebView中,通过JS只能访问JSBridge中所定义的对象,如果访问其它类的函数,比如MyActivity中的函数,就会报下面的错误(即方法找不到)

大家可以自己尝试下;
然后对应的html代码: 




    
    Title
    

我是html页面

 5、JavascriptInterface注解

为了解决addJavascriptInterface()函数的安全问题,在android:targetSdkVersion数值为17(Android4.2)及以上的APP中,JS只能访问带有 @JavascriptInterface注解的Java函数,所以如果你的android:targetSdkVersion是17+,与JS交互的Native函数中,必须添加JavascriptInterface注解,不然无效,比如:

 public class JsInterface {
       @JavascriptInterface
        public void getMessage(String message) {
            btn_webView.setText(message);
        }
    }

这也就是很多同学在高target上,addJavascriptInterface()函数无效的主要原因。
注意:虽然在target 17以后,已经修复了这个安全问题,但目前大多数APP都还是target 17以前的,所以大家可以尝试着找一些APP来演示下这个漏洞哦……

三、JAVA调用JS代码

前面给大家演示了如何通过JS调用Java代码,这里就反过来看看,如何在Native中调用JS的代码
本例的效果图如下:

Android中WebView使用详解及Java与js相互调用_第3张图片

在点击“调用js中的方法”按钮时,调用webview中的JavaScript求和函数,将结果显示在标题中。
先看html代码:




    
    Title
    

我是html页面

在这里,我们写了一个求和函数sum(i,m)
结果中就是把h1标签的内容改为求和后的结果值。
再来看看JAVA的调用代码:

public class WebViewActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_webView;
    private WebView webView;
    private WebSettings settings;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);
        btn_webView = findViewById(R.id.btn_webView);
        btn_webView.setText("调用js中的方法");
        webView = findViewById(R.id.webView);
        settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);//支持JavaScript脚本
        webView.addJavascriptInterface(new JsInterface(), "android");
        webView.loadUrl("file:///android_asset/web.html");
        btn_webView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        webView.loadUrl("javascript:sum(3,8)");
    }

    public class JsInterface {
       @JavascriptInterface
        public void getMessage(String message) {
            btn_webView.setText(message);
        }
    }


}

在点击按钮时调用JS函数:

mWebView.loadUrl("javascript:sum(3,8)");  

这里也就是在JAVA中调用JS函数的方法:

String url = "javascript:methodName(params……)"; 
webView.loadUrl(url); 

 javascript:伪协议让我们可以通过一个链接来调用JavaScript函数
中间methodName是JavaScript中实现的函数
jsonParams是传入的参数列表

 

 

 

你可能感兴趣的:(android进阶)