安卓知识点-应届生扫盲安卓WebView

作者

大家好,我叫Jack冯;

本人20年硕士毕业于广东工业大学,于2020年6月加入37手游安卓团队;

目前主要负责海外游戏发行安卓相关开发。

背景

最近在接触活动相关需求,其中涉及到一个安卓的WebView;

刚毕业的我,对安卓知识积累比较少,所以在这里对Webview进行相关学习,希望自己可以在安卓方面逐步积累。

Webview介绍

1、关于MockView

( 1 ) 在targetSdkVersion 28/29的工程里面查看WebView继承关系

java.lang.Object
  ↳	android.view.View
     ↳	android.view.ViewGroup
​     	  ↳	 android.widget.FrameLayout
  	      	↳	android.layoutlib.bridge.MockView
  	 	     	 ↳	android.webkit.WebView

( 2 ) 使用26/27等低版本SDK,查看源码中的WebView 继承关系

java.lang.Object
  ↳	android.view.View
   ↳  android.view.ViewGroup
  	   ↳	android.widget.AbsoluteLayout
  	 	   ↳	android.webkit.WebView

( 3 )对比

两种方式对比,AbsoluteLayout和FrameLayout都是重写ViewGroup的方法,如与布局参数配置相关的 generateDefaultLayoutParams()、checkLayoutParams()等。两种方式明显不同的是多了一层MockView 。这里来看看MockView是什么:

public class MockView extends FrameLayout{
...
//创建方式
public MockView(Context context) {...}
public MockView(Context context,AttributeSet attrs) {...}
public MockView(Context context,AttributeSet attrs,int defStyleRes) {...}
//重写添加view方法
@Override
public void addView(View child){...}
@Override
public void addView(View child,int index){...}
@Override
public void addView(View child,int width,int height){...}
@Override
public void addView(View child,ViewGroup.LayoutParams params){...}
@Override
public void addView(View child,int index,ViewGroup.LayoutParams params){...}
public void setText(CharSequence text){...}
public void setGravity(int gravity){...}
}

MockView,译为"虚假的view"。

谷歌发布的Sdk其实只是为了提供App开发运行接口,实际运行时候替换为当前系统的Sdk。

具体说就是当谷歌在新的系统(Framework)版本上准备对WebView实现机制进行改动,同时又希望把新的sdk提前发出来,不影响用到WebView的App开发,于是谷歌提供给Android开发的sdk中让WebView继承自MockView,这个WebView只是暴露了接口,没有具体实现;这样当谷歌关于WebView新的实现做好,利用WebView,app也就做好了

2、基本使用

(1)创建

①一般方式:

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

②建议方式:

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);

好处:构建不用依赖本地xml文件,自定义页面参数;手动销毁避免内存泄露;

③更多方式 : 继承Webview和主要API等进行拓展

public class BaseWebView extends WebView {...}
public class BaseWebClient extends WebClient {...}
public class BaseWebChromeClient extends WebChromeClient {...}

(2)加载

① 加载某个网页

webView.loadUrl("http://www.google.com/");

②新建assets目录,将html文件放到目录下,通过路径加载本地页面

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

③使用evaluateJavascript(String script, ValueCallback resultCallback)方法加载,(Android4.4+)

mWebView.evaluateJavascript("file:///android_asset/javascript.html",new ValueCallback<String>() {
	@Override
	public void onReceiveValue(String value) {
		Log.e("测试", "onReceiveValue:"+value );
	}
});

3、WebViewClient

当URL即将加载到当前窗口,如果没有提供WebViewClient,默认情况下WebView将使用Activity管理器为URL选择适当的处理器。

如果提供了WebViewClient,按自定义配置要求来继续加载URL。

(1)常用方法

//加载过程对url的处理(webview加载、系统浏览器加载、其他操作等)
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
	super.shouldOverrideUrlLoading(view, url);
}
//加载失败页面
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
	view.loadUrl("file:///android_asset/js_error.html");
}
//证书错误处理
@Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
}
//开始加载页面(可自定义页面加载计时等)
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
	super.onPageStarted(view, url, favicon);
	Log.e(TAG, "onPageStarted:" + url);
}
//结束加载页面
@Override
public void onPageFinished(WebView view, String url) {
	super.onPageFinished(view, url);
	Log.e(TAG, "onPageFinished: " + url);
}

(2)关于shouldOverrideUrlLoading

如果在点击链接加载过程需要更多的控制,就可以在WebViewClient()中重写shouldOverrideUrlLoading()方法。

涉及shouldOverrideUrlLoading()的情形,大概分为三种:

(1)没有设定setWebViewClient(),点击链接使用默认浏览器打开;

(2)设定setWebViewClient(new WebViewClient()),默认shouldOverrideUrlLoading()返回false,点击链接在Webview加载;

(3)设定、重写shouldOverrideUrlLoading()

返回true:可由应用代码处理该 url,WebView 中止处理(若重写方法没加上view.loadUrl(url),不加载);

返回false:由 WebView 处理加载该 url。(即使没加上view.loadUrl(url),也会在当前Webview加载)

【一般应用】

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
	super.shouldOverrideUrlLoading(view, url);
	if (url != null) {
	if (!(url.startsWith("http") || url.startsWith("https"))) {
			return true;
	}
	//重定向到别的页面
	//view.loadUrl("file:///android_asset/javascript.html");
	//区别不同链接加载
	view.loadUrl(url);
	}
return true;
}

(3)常见误区

【误区1】 : 需要重写 shouldOverrideUrlLoading 方法才能阻止浏览器打开页面。

解释:WebViewClient 源码中 shouldOverrideUrlLoading 方法已经返回 false,不设定setWebViewClient(),默认使用系统浏览器加载。如果重写该方法并返回true, 就可以实现在app页面中加载新链接而不去打开浏览器。

【误区2】 : 每一个url加载过程都会经过 shouldOverrideUrlLoading 方法。

Q1:加载一定会触发shouldOverrideUrlLoading?

Q2:触发时机一定在onPageStarted调用之前?

解释:关于shouldOverrideUrlLoading的触发

1)如果在点击页面链接时通过标签跳转,触发方法如下:

​ shouldOverrideUrlLoading() —> onPageStarted()—> onPageFinished()

2)如果使用loadUrl加载时,触发方法如下:

​ onPageStarted()—>onPageFinished()

3)如果使用loadUrl加载重定向地址时,触发方法如下:

​ shouldOverrideUrlLoadings—>onPageStarted —> onPageFinished

ps:多次重定向的过程,

onPage1Started

—>shouldOverrideUrlLoadings

—>onPage2Started —> xxx...

—> onPageNFinished

结论:shouldOverrideUrlLoading()方法不是每次加载都会调用,WebView的前进、后退等不会调用shouldOverrideUrlLoading方法;非loadUrl方式加载 或者 是重定向的,才会调用shouldOverrideUrlLoading方法。

【误区3 】: 重写 shouldOverrideUrlLoading 方法返回true比false的区别,多调用一次onPageStarted()和onPageFinished()。

解释:返回True:应用代码处理url;返回False,则由 WebView 处理加载 url。

ps:低版本系统(华为6.0),测试 False比True会多调用一次onPageStarted()和onPageFinished(),这点还在求证中。

4、WebChromeClient

对比WebviewClient , 添加了处理JavaScript对话框,图标,标题和进度等。

处理对象 : 影响浏览器的事件

(1)常用方法:

//alert弹出框
public boolean onJsAlert(WebView view, String url, String message,JsResult result){
return true;//true表示拦截
}

//confirm弹出框
public boolean onJsConfirm(WebView view, String url, String message,JsResult result){
return false;//false则允许弹出
}

 public boolean onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result) 
 
//打印 console 信息。return true只显示log,不显示js控制台的输入;false则都显示出来
 public boolean onConsoleMessage(ConsoleMessage consoleMessage){
 Log.e("测试", "consoleMessage:"+consoleMessage.message());
 }
 
//通知程序当前页面加载进度,结合ProgressBar显示
 public void onProgressChanged(WebView view, int newProgress){
 		if (newProgress < 100) {
			String progress = newProgress + "%";
			Log.e("测试", "加载进度:"+progress);
			webProgress.setProgress(newProgress);
		}
 }

(2)拦截示例:

JsResult.comfirm() --> 确定按钮的调用方法

JsResult.cancle() --> 取消按钮

示例:拦截H5的弹框,并显示自定义弹框,点击按钮后重定向页面到别的url

@Override
public boolean onJsConfirm(final WebView view, String url, String message, final JsResult result) {
	Log.e("测试", "onJsConfirm:"+url+",message:"+message+",jsResult:"+result.toString());
		new AlertDialog.Builder(chromeContext)
				.setTitle("拦截JsConfirm显示!")
				.setMessage(message)
				.setPositiveButton(android.R.string.ok,
						new AlertDialog.OnClickListener() {
							public void onClick(DialogInterface dialog,int which) {
								//重定向页面
								view.loadUrl("file:///android_asset/javascript.html");
								result.confirm();
							}
						}).setCancelable(false).create().show();
	return true;
}

5、WebSettings

用于页面状态设置\插件支持等配置.

(1)常用方法

WebSettings webSettings = webView.getSettings();
 /**
 * 设置缓存模式、支持Js调用、缩放按钮、访问文件等
 */
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(true);

//允许WebView使用File协议,访问本地私有目录的文件
webSettings.setAllowFileAccess(true);

//允许通过file url加载的JS页面读取本地文件
webSettings.setAllowFileAccessFromFileURLs(true);

//允许通过file url加载的JS页面可以访问其他来源内容,包括其他的文件和http,https等来源
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setLoadsImagesAutomatically(true);
webSettings.setDefaultTextEncodingName("utf-8")

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

结束语

过程中有问题或者需要交流的同学,可以扫描二维码加好友,然后进群进行问题和技术的交流等;

企业微信截图_5d79a123-2e31-42cc-b03f-9312b8b99df3.png

猜你喜欢

转载自juejin.im/post/7245084484756144186