注册

安卓知识点-应届生扫盲安卓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);
}

结束语


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


作者:37手游移动客户端团队
链接:https://juejin.cn/post/7245084484756144186
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册