博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android基于JsBridge封装的高效带加载进度的WebView
阅读量:6883 次
发布时间:2019-06-27

本文共 11859 字,大约阅读时间需要 39 分钟。

概述

从去年4月项目就一直用起了JsBridge,前面也针对jsBridge使用姿势介绍过一篇入门篇,《》,本篇接着继续深入,通过再次优化封装,大大优化了部分代码,简化上层调用流程,快速部署你的Hybridge APP。

再进行具体编码前 ,我先进行了一般商业APP对WebView的需求

  • 可加载本地和云端H5
  • 拥有cookie持久能力
  • 添加公共参数
  • 回退前进功能
  • Js与本地navtive交互
  • 拥有加载默认错误页面能力
  • 加载网页可展现进度
  • 支持https

好为了满足以上常用功能,大致对webview相关知识进行下普及。

效果图:

这里写图片描述

WebView

谷歌提供的系统组件,用来加载和展现html网页,其采用webkit内核驱动,来实现网页浏览功能。

拥有load() URL和本地html文件

// 云端    webView.loadUrl("https://www.baidu.com");     // 本地    webView.loadUrl("file:///android_asset/demo.html");
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

WebViewClient

WebViewClient主要辅助WebView执行处理各种响应请求事件的,比如:

  • onLoadResource
  • onPageStart
  • onPageFinish
  • onReceiveError
  • onReceivedHttpAuthRequest
  • shouldOverrideUrlLoading

本次加载失败页面,和拦截加入header头必须用到它,由于无法拦截h5本身ajax的请求,所以对header同步不是很好,建议大家对于ajax请求采用cookie形式,以防止url参数服务端无法获取的问题。

加入header 一般直接使用webView.load(url, header)

view.loadUrl(url, header);

为了方便上层开发者调用,可以将此code加入到WebViewClient 的shouldOverrideUrlLoading中执行 

姿势那就是这样:

public boolean shouldOverrideUrlLoading(WebView view, String url) {   if(this.onPageHeaders(url) != null) {     view.loadUrl(url, this.onPageHeaders(url)); }   return super.shouldOverrideUrlLoading(view, url);}

错误页面也是复写WebViewClient的onReceivedError() 来加入自定义的抽象onPageError(),姿势如下:

public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {    view.loadUrl(this.onPageError(failingUrl));}

onPageHeaders()便是上层抽象出来的接口,方便我们直接加入header,而onPageError()是方便指定加载错误页面,那么在activity中就是这样了,

mProgressBarWebView.setWebViewClient(new    CustomWebViewClient(mProgressBarWebView.getWebView()) {        @Override        public String onPageError(String url) {            return "file:///android_asset/error.html";        }        @Override        public Map
onPageHeaders(String url) { return CookieManger.getHeader(getContext()); } });

WebChromeClient

主要辅助WebView处理的对话框、网站Logo、网站title、load进度等处理。

  • onCloseWindow(关闭WebView)
  • onCreateWindow()
  • onJsAlert ()
  • onJsPrompt
  • onJsConfirm
  • onProgressChanged
  • onReceivedIcon
  • onReceivedTitle
  • onShowCustomView

WebView只是用来处理一些html的页面内容,只用WebViewClient就行了,如果需要更丰富的处理效果,比如、进度条等,就要用到WebChromeClient。因为这次功能要用加载进度,不得不说它。

为了加入顶部的加载进度条,复写WebChromeClient中onProgressChanged,在这里更改我们加入的ProgressBar的进度,你也可以设置网页标题,甚至可以全屏!

public class CustomWebChromeClient extends WebChromeClient {private NumberProgressBar mProgressBar;public CustomWebChromeClient(NumberProgressBar progressBar) {    this.mProgressBar = progressBar;}public void onProgressChanged(WebView view, int newProgress) {    if(newProgress >= 95) {        this.mProgressBar.setVisibility(8);    } else {        if(this.mProgressBar.getVisibility() == 8) {            this.mProgressBar.setVisibility(0);        }        this.mProgressBar.setProgress(newProgress);    }    super.onProgressChanged(view, newProgress);}   //获取tittle    @Override    public void onReceivedTitle(WebView view, String title) {        super.onReceivedTitle(view, title);    }    //全屏    @Override    public void onShowCustomView(View view, CustomViewCallback callback) {        super.onShowCustomView(view, callback);    }}

好了准备好了同步Header和进度条之后,就的考虑cookie同步问题

CookieSync

CookieManager 

CookieManager是用来管理Cookie的,主要来管理cookie相关,提供如下API

  • setAcceptCookie()
  • setCookie()
  • getCookie(String url);
  • removeSessionCookies();
  • hasCookies()
  • removeAllCookie()

CookieSyncManager

CookieSyncManagerl继承WebSyncManager,来管理同步cookie相关,主要有以下API

  • resetSync()
  • stopSync()
  • sync()
  • syncFromRamToFlash()
  • checkInstanceIsAllowed()

你想问这些api什么意思,请保留点你的童真,不要问这么简单的问题好吗?

接着我们就可以这样操作来实现cookie同步了,

CookieManager cookieManager = CookieManager.getInstance();   // 接受服务器cookie    cookieManager.setAcceptCookie(true);    //移除之前的cookie    cookieManager.removeSessionCookie();    // 注入cookies    List
cookies = getCookies(customCookies); for (String cookie : cookies) { cookieManager.setCookie(uri.getHost(), cookie); } // 同步cookie CookieSyncManager.getInstance().sync();

这里需要注意棒棒糖以上的会出现无法同步问题那么请这样做

if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {        cookieManager.flush();    } else {        CookieSyncManager.getInstance().sync();    }

经,完美!

你可能想问?我想自定义像header一样加入一些自定义cookie,行,没问题,继续看!

public static List
createCustomCookies() { List
cookies = new ArrayList<>(); cookies.add(“author ” + "= " + "tamic"); cookies.add(“data” + "= " + "2016.8.15"); cookies.add(“key” + "= " + 4fdfsfd34dfdfswer"); cookies.add(“chanel” + "= " + "简书"); return cookies;}

很可能会遇到处理缓存问题,设置缓存webView缓存模式!这里在普及下相关姿势!

缓存模式

webview缓存模式有5种,具体方式: 

- LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据 
- LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。 
- LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式 
- LOAD_NO_CACHE: 不使用缓存,只从网络获取数据. 
- LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。

www.baidu.com的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网,只要本地有缓存,都会加载缓存。本地没有缓存时才从网络上获取, 

这个和Http缓存一致,我不在过多介绍,如果你想自定义缓存策略和时间,可以尝试下,

清除缓存

CacheManager来处理webview缓存相关:

clearCache(boolean)  CacheManager.clear

在4.4以上的此api已经无法使用,也就是说缓存清空涉及安全,需要你自己去实现,就类似picasso, okhttp缓存,一样要开发者自我去实现。

当然也可以这样:

WebView.clearCache(true);

清空历史记录

mWebview.clearHistory();

需要在onPageFinished()的方法之后调用

webview支持https

webView.setWebViewClient(new SSLTolerentWebViewClient());webView.loadUrl(myhttps url);

复写WebViewClient的nReceivedSslError函数,执行handler.cancel();给用户感知的话,来个对话框授权下就行了额

private class SSLTolerentWebViewClient extendsWebViewClient {   public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {  AlertDialog.Builder builder = new   AlertDialog.Builder(Tab1Activity.this);  AlertDialog alertDialog = builder.create();  String message = "SSL Certificate error.";  switch (error.getPrimaryError()) {    case SslError.SSL_UNTRUSTED:     message = "The certificate authority is not trusted.";      break;    case SslError.SSL_EXPIRED:    message = "The certificate has expired.";     break;    case SslError.SSL_IDMISMATCH:    message = "The certificate Hostname mismatch.";     break;    case SslError.SSL_NOTYETVALID:    message = "The certificate is not yet valid.";    break;}message += " Do you want to continue anyway?";alertDialog.setTitle("SSL Certificate Error"); alertDialog.setMessage(message);alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {    // Ignore SSL certificate errors    handler.proceed();}});alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {   @Override    public void onClick(DialogInterface dialog, int which) {     handler.cancel();    }    });     alertDialog.show();  }}

ProgressBarWebView

学习了上面基础知识,我这里就开始进行自定义的进度条ProgressBarWebView的封装了,这里我直接对BridgeWebView进行扩展。下面是主要部分。

public class ProgressBarWebView extends LinearLayout {   static final String TAG = ProgressBarWebView.class.getSimpleName();   private NumberProgressBar mProgressBar;    private BridgeWebView mWebView;   public ProgressBarWebView(Context context) {       super(context);       this.init(context, (AttributeSet)null);   }   public ProgressBarWebView(Context context, AttributeSet attrs) {      super(context, attrs);      this.init(context, attrs);  }@TargetApi(11)public ProgressBarWebView(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    this.init(context, attrs);}@TargetApi(21)public ProgressBarWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {    super(context, attrs, defStyleAttr, defStyleRes);    this.init(context, attrs);}

为了使webview能有后退功能!我屏蔽了长按事件,并且对返回键建进行了拦截。

mWebView.setOnLongClickListener(new OnLongClickListener() {        public boolean onLongClick(View v) {            return true;        }    });    this.mWebView.setOnKeyListener(new OnKeyListener() {        public boolean onKey(View v, int keyCode, KeyEvent event) {            if(event.getAction() == 0 && keyCode == 4 &&      ProgressBarWebView.this.mWebView.canGoBack()) {                ProgressBarWebView.this.mWebView.goBack();                return true;            } else {                return false;            }        }    });

如果防止webview代码产生内存泄漏,请及时在activity销毁时,清空webview

@Override  public void onDestroy() {    super.onDestroyView();    if (mProgressBarWebView.getWebView() != null) {        mProgressBarWebView.getWebView().destroy();    }}

看了构造方法你已明白,里面包含一个BridgeWebView和一个NumberProgressBar 成员属性, 

接着就是对JsBridge进行封装了

调用js代码

public void registerHandler(final String handlerName, final JsHandler handler) {     this.mWebView.registerHandler(handlerName, new BridgeHandler() {         public void handler(String data, CallBackFunction function) {             if(handler != null) {                handler.OnHandler(handlerName, data, function);             }        }    });}

js调用Native

public void callHandler(final String handlerName, String javaData, final JavaCallHandler handler) {    this.mWebView.callHandler(handlerName, javaData, new CallBackFunction() {        public void onCallBack(String data) {            if(handler != null) {                handler.OnHandler(handlerName, data);            }        }    });}

看可jsBridge的可能问这个JsHandler谁神马。本来在jsBridge源码中没这个东东的, 是为了方便上层调用我自己封装的接口,

public interface JsHandler {void OnHandler(String var1, String var2, CallBackFunction var3);

好了 关键的东西已经介绍完,如果对jsBridge可以看看去年我写的一篇对他的介绍:,

接着使用我们封装好的ProgressBarWebView

案列使用

配置

Gradle:

root:

repositories {maven { url "https://jitpack.io" }jcenter()  }

Module:

dependencies {   .....   compile 'com.tamic:browse:1.0.0'   }

初始化

ProgressBarWebView  mProgressBarWebView = (ProgressBarWebView)    findViewById(R.id.login_progress_webview);

设置自定义WebViewClient

mProgressBarWebView.setWebViewClient(new   CustomWebViewClient(mProgressBarWebView.getWebView()) {        @Override        public String onPageError(String url) {            //指定网络加载失败时的错误页面            return "file:///android_asset/error.html";        }        @Override        public Map
onPageHeaders(String url) { // 可以加入header return null; } });

加载指定Url

mProgressBarWebView.loadUrl("file:///android_asset/demo.html");

当然,也可以支持网络url;

注册Js回调函数

ArrayList
mHandlerNames = new ArrayList<>(); mHandlers.add("login"); mHandlers.add("callNative"); mHandlers.add("callJs"); mHandlers.add("open");

回调js的方法

mProgressBarWebView.registerHandlers(mHandlers, new JsHandler() {        @Override        public void OnHandler(String handlerName, String responseData, CallBackFunction function) {            if (handlerName.equals("login")) {                Toast.makeText(MainActivity.this, responseData, Toast.LENGTH_SHORT).show();            } else if (handlerName.equals("callNative")) {                Toast.makeText(MainActivity.this, responseData, Toast.LENGTH_SHORT).show();                function.onCallBack("我在上海");            } else if (handlerName.equals("callJs")) {                Toast.makeText(MainActivity.this, responseData, Toast.LENGTH_SHORT).show();                // 想调用你的方法:                function.onCallBack("好的 这是图片地址 :xxxxxxx");            } if (handlerName.equals("open")) {                mfunction = function;                pickFile();            }        }    });

Native调用js

mProgressBarWebView.callHandler("callNative", "hello H5, 我是java", new JavaCallHandler() {        @Override        public void OnHandler(String handlerName, String jsResponseData) {            Toast.makeText(MainActivity.this, "h5返回的数据:" + jsResponseData, Toast.LENGTH_SHORT).show();        }    });

Native发送消息给js

mProgressBarWebView.send("哈喽", new CallBackFunction() {        @Override        public void onCallBack(String data) {            Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();        }    });}

Xml文件和js代码这里不做介绍,具体看项目案列中源码:

GtiHub:

如果喜欢,请star!

结束语

这里感谢曾技术经理出身的同事的对相关部分代码的封装,感谢振南同学!

你可能感兴趣的文章
Swing-选项卡面板JTabbedPane-入门
查看>>
(21/24) webpack实战技巧:webpack对三方类库的优化操作
查看>>
复利完结观看评价
查看>>
service 需要注意的地方
查看>>
【并查集】Gym - 101128B - Black Vienna
查看>>
【模拟】洛谷 P1328 NOIP2014提高组 day1 T1 生活大爆炸版石头剪刀布
查看>>
JavaScript中的forEach
查看>>
【BZOJ】3039: 玉蟾宫 悬线法
查看>>
Clash Detection
查看>>
从CAP理论中分析Eureka与zookeeper的区别
查看>>
20172318 2018-2019-1 《程序设计与数据结构》第2周学习总结
查看>>
文件操作
查看>>
ubuntu忘记root密码解决
查看>>
windows 80端口被占用的解决方法
查看>>
Qt学习五 - 对话框
查看>>
Android 学习 笔记_12. Spinner的简单实使用
查看>>
手册与参考链接
查看>>
做错的题目——this的指向
查看>>
Struts、JSTL标签库的基本使用方法
查看>>
A Tour of Go Numeric Constants
查看>>