WebView 使用详解(一)——Native 与 JS 相互调用(附 JadX 反编译)

本贴最后更新于 2487 天前,其中的信息可能已经天翻地覆

一直在用 WebView,还没有系统的总结过它的用法,下面就系统的总结下,分享给大家

一、基本用法

1、加载在线 URL

 void loadUrl(String url)  

这个函数主要加载 url 所对应的网页地址,或者用于调用网页中的指定的 JS 方法(调用 js 方法的用法,后面会讲),但有一点必须注意的是:loadUrl()必须在主线程中执行!!!否则就会报错!!!。
注意:加载在线网页地址是会用到联网 permission 权限的,所以需要在 AndroidManifest.xml 中写入下面代码申请权限:

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

本示例效果为:

从效果图中可以明显看出本示例的布局:
main.xml

<xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
			  android:orientation="vertical"  
			  android:layout_width="fill_parent"  
			  android:layout_height="fill_parent"  
		>  
	<Button  
			android:id="@+id/btn"  
			android:layout_width="match_parent"  
			android:layout_height="wrap_content"  
			android:text="加载URL"/>  

	<WebView  
			android:id="@+id/webview"  
			android:layout_width="match_parent"  
			android:layout_height="match_parent"/>  
<LinearLayout>  

对应的处理代码如下

public class MyActivity extends Activity {  

  private WebView mWebView;  
  private Button mBtn;  
  @Override  
  public void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.main);  

      mWebView = (WebView)findViewById(R.id.webview);  
      mBtn = (Button)findViewById(R.id.btn);  

      mBtn.setOnClickListener(new View.OnClickListener() {  
          @Override  
          public void onClick(View v) {  
              mWebView.loadUrl("http://www.baidu.com");  
          }  
      });  
  }  
}  

代码很简单,就是在点击按钮的时候加载网址,但需要注意的是:网址必须完整即以 http://或者 ftp://等协议开头,不能省略!不然将加载不出来,这是因为 webview 是没有自动补全协议功能的,所以如果我们不加,它将识别不出来网址类型,也就加载不出来了。
但如果我们运行上面的代码,效果却是利用浏览器来打开网址,却不是使用 webview 打开网址:

如果我们想实现像示例一样在 webview 中打开网址需要怎么做呢?
我们需要设置 WebViewClient:
修改后的代码为:
public class MyActivity extends Activity {

  private WebView mWebView;  
  private Button mBtn;  
  @Override  
  public void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.main);  

      mWebView = (WebView)findViewById(R.id.webview);  
      mBtn = (Button)findViewById(R.id.btn);  

      mWebView.setWebViewClient(new WebViewClient());  

      mBtn.setOnClickListener(new View.OnClickListener() {  
          @Override  
          public void onClick(View v) {  
              mWebView.loadUrl("http://www.baidu.com");  
          }  
      });  
  }  
}  

在上面的基础上,我们添加了下面一段代码:

mWebView.setWebViewClient(new WebViewClient());  

在这里我们利用 mWebView.setWebViewClient()函数仅仅设置了一个 WebViewClient 实例,就可以实现在 WebView 中打开链接了,至于原因我们下篇会讲到,这里就先忽略了,大家只需要知道要在 WebView 中打开链接,就必须要设置 WebViewClient;
最终的效果图就与开篇时一样的了,这里就不再帖效果图了,下面我们来看看如何加载本地 html 网页

源码在文章底部给出

2、加载本地 URL

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


web.html 的内容为:

	<head>  
		<meta charset="UTF-8">  
		<title>Titletitle>  
		<h1>欢迎光临启舰的blogh1>  
	<head>  
	<body>  
	<body>  
	<html>  

即大标题显示一段文字
我们同样在上面的示例的基础上加以改造,在点击按钮的时候加载本地 web.html 文件

public class MyActivity extends Activity {  
  private WebView mWebView;  
  private Button mBtn;  
  @Override  
  public void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.main);  

      mWebView = (WebView)findViewById(R.id.webview);  
      mBtn = (Button)findViewById(R.id.btn);  

      mBtn.setOnClickListener(new View.OnClickListener() {  
          @Override  
          public void onClick(View v) {  
              mWebView.loadUrl("file:///android_asset/web.html");  
          }  
      });  
  }  
}      

从这里可以看到与加载在线 URL 有两点不同:
**1、URL 类型不一样 **
在加载本地 URL 时,是以“
**2、不需要设置 WebViewClient **
这里很明显没有设置 WebViewClient 函数,但仍然是在 webview 中打开的本地文件。具体原因下篇文章讲到 WebViewClient 时我们会具体解释。
本例效果图如下:

**所以对于加载 URL 的总结就是: **
1、如果是在线网址记得添加网络访问权限
2、在线网址中,如果要使用 webview 打开,记得设置 WebViewClient
3、打开本地 html 文件时,是不需要设置 WebViewClient,对应的 asstes 目录的 url 为:

源码在文章底部给出

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)  

下面我们就举个例子来看下用法

示例 1:在 WebView 中启用 JavaScript:

public class MyActivity extends Activity {  

  private WebView mWebView;  
  private Button mBtn;  

  @Override  
  public void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.main);  

      mWebView = (WebView) findViewById(R.id.webview);  
      mBtn = (Button) findViewById(R.id.btn);  

      WebSettings webSettings = mWebView.getSettings();  
      webSettings.setJavaScriptEnabled(true);  
  }  
}  

示例 2:设置缓存

优先使用缓存

webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);  

不使用缓存:

 webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);  

示例 3:打开页面时, 自适应屏幕:

 WebSettings webSettings =   mWebView .getSettings();         
 webSettings.setUseWideViewPort(true);//设置此属性,可任意比例缩放  
 webSettings.setLoadWithOverviewMode(true);  

效果图如下:(所使用的网址为:http://www.w3school.com.cn/)

注意:对于我们自己写的网页代码,不必利用这处函数来做页面缩放适配,因为对于有些手机存在适配问题,只需要在 HTML 中做宽度 100% 自适应屏幕就行了

示例 4:使页面支持缩放:

 WebSettings webSettings = mWebView.getSettings();  
  //开启javascript支持  
  webSettings.setJavaScriptEnabled(true);   
  // 设置可以支持缩放  
  webSettings.setSupportZoom(true);  
  // 设置出现缩放工具  
  webSettings.setBuiltInZoomControls(true);  

示例 5:.如果 webView 中需要用户手动输入用户名、密码或其他,则 webview 必须设置支持获取手势焦点

 webview.requestFocusFromTouch();  

其它的设置就靠大家自己去尝试啦,这里就不再一一缀述了,大家只需要记着,如果需要设置 webview 就从 WebSettings 里面去找就可以啦。

源码在文章底部给出

二、JS 调用 Java 代码

在看了如何设置 webview 以后,我们来看下如何让 Webview 与网页中的 JS 代码交互的问题。

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 是一个 String,怎么被你说成对象了,理解不了没关系,下面有具体示例

2、示例

下面同样是上面的示例,我们对它加以更改,效果图如下:

在原来 html 上面添加了一个按钮,当点击按钮时调用 Android 的 Toast 函数弹出一个 toast 消息。
先看 Android 代码:

public class MyActivity extends Activity {  

 private WebView mWebView;  
  private Button mBtn;  

  @Override  
  public void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.main);  

      mWebView = (WebView) findViewById(R.id.webview);  

      WebSettings webSettings = mWebView.getSettings();  
      webSettings.setJavaScriptEnabled(true);  
      mWebView.addJavascriptInterface(this, "android");  
      mWebView.loadUrl("file:///android_asset/web.html");  
  }  

  public void toastMessage(String message) {  
      Toast.makeText(getApplicationContext(), "通过Natvie传递的Toast:"+message, Toast.LENGTH_LONG).show();  
  }  
}      

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

mWebView.addJavascriptInterface(this, "android");  

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

  <html lang="en">  
  <head>  
	  <meta charset="UTF-8">  
	  <title>Titletitle>  
	 <h1>欢迎光临启舰的blogh1>  
	  <input type="button" value="js调native" onclick="ok()">  
  <head>  
  <body>  
  <script type="text/javascript">  
  function ok() {  
   android.toastMessage("哈哈,i m webview msg");  
  }  
  <script>  
  <body>  
  <html>  

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

  function ok() {  
   android.toastMessage("哈哈,i m webview msg");  
  }  

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

源码在文章底部给出

3、addJavascriptInterface 自定义作用对象

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

public class MyActivity extends Activity {  

  private WebView mWebView;  
  private Button mBtn;  

  @Override  
 public void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.main);  

      mWebView = (WebView) findViewById(R.id.webview);  
      mBtn = (Button) findViewById(R.id.btn);  

      WebSettings webSettings = mWebView.getSettings();  
      webSettings.setJavaScriptEnabled(true);  
      mWebView.addJavascriptInterface(new JSBridge(), "android");  
      mWebView.loadUrl("file:///android_asset/web.html");  
  }  

  public class JSBridge{  
      public void toastMessage(String message) {  
          Toast.makeText(getApplicationContext(), "通过Natvie传递的Toast:"+message, Toast.LENGTH_LONG).show();  
      }  
  }  
}      

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

mWebView.addJavascriptInterface(new JSBridge(), "android");  

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

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

<html lang="en">  
<head>  
	<meta charset="UTF-8">  
	<title>Titletitle>  
	<h1>欢迎光临启舰的blogh1>  
	<input type="button" value="js调native" onclick="ok()">  
<head>  
<body>  
<script type="text/javascript">  

function ok() {
android.toastMessage("哈哈,i m webview msg");
}

  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    334 引用 • 323 回帖 • 4 关注
  • Webview
    7 引用 • 33 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...