深度解密AgentWeb:WebChromeClient与WebViewClient核心实现原理
你是否在Android开发中遇到过WebView加载进度难以监控?JavaScript对话框样式与App风格不统一?第三方App跳转混乱等问题?本文将通过剖析AgentWeb的WebChromeClient与WebViewClient实现原理,带你彻底解决这些痛点,掌握Android WebView的高级应用技巧。
读完本文你将获得:
- WebChromeClient与WebViewClient的协作机制
- 进度条与标题更新的实现方案
- 文件选择与权限请求的处理逻辑
- URL拦截与第三方App跳转控制
- 自定义WebView客户端的最佳实践
AgentWeb客户端架构概览
AgentWeb作为基于Android WebView的增强库,通过中间件模式对WebChromeClient和WebViewClient进行了封装,形成了灵活可扩展的客户端架构。
核心类关系如下:
- WebChromeClientDelegate:代理原生WebChromeClient,实现方法转发
- WebViewClientDelegate:代理原生WebViewClient,实现方法转发
- DefaultChromeClient:提供默认ChromeClient实现,处理进度、标题、文件选择等
- DefaultWebClient:提供默认WebViewClient实现,处理URL加载、错误页面等
主要源码文件:
WebChromeClient实现原理
WebChromeClient主要负责浏览器界面相关的功能,如进度显示、标题更新、JavaScript对话框、文件选择等。AgentWeb通过DefaultChromeClient提供了增强实现。
进度条与标题更新机制
在DefaultChromeClient中,通过重写onProgressChanged方法实现进度条更新:
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (mIndicatorController != null) {
mIndicatorController.progress(view, newProgress);
}
}
进度条控制器IndicatorController通过mIndicatorController.progress()方法更新UI,对应实现类为IndicatorHandler.java。
JavaScript对话框处理
AgentWeb对JS对话框进行了统一处理,确保样式与App一致:
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if (mAgentWebUIController.get() != null) {
mAgentWebUIController.get().onJsAlert(view, url, message);
}
result.confirm(); // 默认确认
return true; // 表示已处理
}
类似地,onJsConfirm和onJsPrompt方法也通过mAgentWebUIController将对话框展示逻辑委托给UI控制器处理。
文件选择器实现
不同Android版本的文件选择API存在差异,DefaultChromeClient通过反射适配了各种版本:
// Android >= 4.1
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
commonRefect(this.mDelegate, "openFileChooser",
new Object[]{uploadFile, acceptType, capture},
ValueCallback.class, String.class, String.class);
}
// Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
commonRefect(this.mDelegate, "openFileChooser",
new Object[]{valueCallback}, ValueCallback.class);
}
对于Android 5.0及以上版本,使用onShowFileChooser方法:
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
return openFileChooserAboveL(webView, filePathCallback, fileChooserParams);
}
权限请求处理
对于地理位置、摄像头等权限请求,DefaultChromeClient通过PermissionInterceptor接口提供了拦截机制:
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
if (mPermissionInterceptor != null) {
if (mPermissionInterceptor.intercept(this.mWebView.getUrl(),
AgentWebPermissions.LOCATION, "location")) {
callback.invoke(origin, false, false);
return;
}
}
// 权限请求逻辑...
}
WebViewClient实现原理
WebViewClient主要负责页面加载相关的功能,如URL拦截、页面开始/结束加载、资源加载、错误处理等。AgentWeb通过DefaultWebClient提供了增强实现。
URL加载与拦截机制
shouldOverrideUrlLoading方法用于决定是否拦截URL加载:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(HTTP_SCHEME) || url.startsWith(HTTPS_SCHEME)) {
return (webClientHelper && HAS_ALIPAY_LIB && isAlipay(view, url));
}
if (!webClientHelper) {
return false;
}
// 处理电话、短信等通用链接
if (handleCommonLink(url)) {
return true;
}
// 处理intent scheme
if (url.startsWith(INTENT_SCHEME)) {
handleIntentUrl(url);
return true;
}
// 微信支付
if (url.startsWith(WEBCHAT_PAY_SCHEME)) {
startActivity(url);
return true;
}
// ...其他URL处理逻辑
return super.shouldOverrideUrlLoading(view, url);
}
支付宝与微信支付支持
DefaultWebClient内置了支付宝和微信支付的处理逻辑:
private boolean isAlipay(final WebView view, String url) {
try {
Activity mActivity = mWeakReference.get();
if (mActivity == null) return false;
// 使用反射创建PayTask实例
Class clazz = Class.forName("com.alipay.sdk.app.PayTask");
Constructor<?> mConstructor = clazz.getConstructor(Activity.class);
Object payTask = mConstructor.newInstance(mActivity);
// 调用支付拦截方法
Method payInterceptorWithUrl = clazz.getMethod("payInterceptorWithUrl",
String.class, boolean.class, H5PayCallback.class);
return (boolean) payInterceptorWithUrl.invoke(payTask, url, true,
new H5PayCallback() {
@Override
public void onPayResult(H5PayResultModel result) {
// 支付结果处理
}
});
} catch (Exception e) {
// 异常处理
}
return false;
}
错误页面处理
当页面加载出错时,DefaultWebClient会显示自定义错误页面:
private void onMainFrameError(WebView view, int errorCode, String description, String failingUrl) {
mErrorUrlsSet.add(failingUrl);
if (mAgentWebUIController.get() != null) {
mAgentWebUIController.get().onMainFrameError(view, errorCode, description, failingUrl);
}
}
错误页面布局定义在agentweb_error_page.xml。
SSL错误处理
对于SSL证书错误,提供了自定义处理机制:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if (mAgentWebUIController.get() != null) {
mAgentWebUIController.get().onShowSslCertificateErrorDialog(view, handler, error);
}
}
中间件设计模式的应用
AgentWeb采用中间件模式实现WebChromeClient和WebViewClient的功能扩展,通过MiddlewareWebChromeBase和MiddlewareWebClientBase实现责任链。
WebChromeClient中间件
public class MiddlewareWebChromeBase extends WebChromeClient {
private MiddlewareWebChromeBase mMiddleWareWebChromeBase;
public MiddlewareWebChromeBase(WebChromeClient webChromeClient) {
this.mMiddlewareWebChromeBase = webChromeClient instanceof MiddlewareWebChromeBase ?
(MiddlewareWebChromeBase) webChromeClient : null;
}
public void enq(MiddlewareWebChromeBase middlewareWebChromeBase) {
if (this.mMiddlewareWebChromeBase == null) {
this.mMiddlewareWebChromeBase = middlewareWebChromeBase;
return;
}
this.mMiddlewareWebChromeBase.enq(middlewareWebChromeBase);
}
// 重写WebChromeClient方法并委托给下一个中间件
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (this.mMiddlewareWebChromeBase != null) {
this.mMiddlewareWebChromeBase.onProgressChanged(view, newProgress);
return;
}
super.onProgressChanged(view, newProgress);
}
// ...其他方法
}
WebViewClient中间件
类似地,WebViewClient也通过中间件模式实现功能扩展:
public class MiddlewareWebClientBase extends WebViewClient {
private MiddlewareWebClientBase mMiddleWareWebClientBase;
public MiddlewareWebClientBase(WebViewClient webViewClient) {
this.mMiddlewareWebClientBase = webViewClient instanceof MiddlewareWebClientBase ?
(MiddlewareWebClientBase) webViewClient : null;
}
public void enq(MiddlewareWebClientBase middlewareWebClientBase) {
if (this.mMiddlewareWebClientBase == null) {
this.mMiddlewareWebClientBase = middlewareWebClientBase;
return;
}
this.mMiddlewareWebClientBase.enq(middlewareWebClientBase);
}
// ...重写WebViewClient方法并委托
}
这种设计允许开发者灵活地添加自定义功能,而无需修改原有代码。
客户端配置与使用
在AgentWeb中配置自定义WebChromeClient和WebViewClient非常简单:
AgentWeb.with(this)
.setAgentWebParent(container, new ViewGroup.LayoutParams(-1, -1))
.useDefaultIndicator()
.setWebChromeClient(new CustomWebChromeClient())
.setWebViewClient(new CustomWebViewClient())
.createAgentWeb()
.ready()
.go("https://www.example.com");
其中CustomWebChromeClient和CustomWebViewClient可以继承AgentWeb提供的客户端基类,实现自定义功能。
总结与最佳实践
AgentWeb通过精心设计的WebChromeClient和WebViewClient实现,解决了Android WebView开发中的诸多痛点问题。主要优势包括:
- 完善的进度监控:通过IndicatorController实现精确的进度条控制
- 统一的对话框样式:自定义JS对话框与App风格保持一致
- 版本兼容的文件选择:适配各Android版本的文件选择API
- 内置支付支持:无缝集成支付宝和微信支付
- 灵活的中间件扩展:通过中间件模式实现功能扩展
最佳实践建议:
- 对于简单需求,直接使用DefaultChromeClient和DefaultWebClient
- 需要自定义时,继承MiddlewareWebChromeBase和MiddlewareWebClientBase实现中间件
- 处理敏感操作时,使用PermissionInterceptor进行权限控制
- 第三方App跳转采用ASK_USER_OPEN_OTHER_PAGE模式,提升用户体验
通过深入理解AgentWeb的客户端实现原理,我们可以构建出体验更优、功能更强大的Android Web应用。AgentWeb的设计思想也为我们提供了WebView封装的优秀范例,值得在实际项目中学习和应用。
关注本项目获取更多Android WebView开发技巧,下期我们将深入探讨AgentWeb的JavaScript交互机制。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




