深入理解Android WebView的加载流程与事件回调

在Android开发中,WebView用于显示网页和执行JavaScript。理解其加载流程和事件回调对于开发一个功能丰富且用户友好的基于Web的应用至关重要。本文将详细介绍 WebView 加载一个URL时的整个流程和相关的事件回调,帮助开发者更好地掌握其使用方法和处理可能出现的问题。

一、WebView 加载流程时序图

当用户通过 WebView 加载一个URL时,整个过程涉及多个组件和一系列复杂的交互。下面是一个 WebView 加载URL的时序图,以及对每个回调事件的详细说明。

用户 WebView WebViewClient Native层 (AwContents) loadUrl() nativeLoadUrl() 页面加载开始 onPageStarted() 页面开始加载时调用,通知应用页面加载已经开始。 shouldInterceptRequest (多次) WebView每次请求资源时调用,允许应用拦截、修改或替换资源请求。 onLoadResource (多次) 加载页面资源时调用,通知应用页面资源正在加载。 onFormResubmission() 用户尝试重新提交表单时调用,询问用户是否重新提交表单。 onReceivedHttpAuthRequest() 需要HTTP认证时调用,请求HTTP认证信息。 onReceivedClientCertRequest() 服务器请求客户端证书时调用,提供客户端证书。 onReceivedSslError() SSL验证失败时调用,处理SSL错误。 onReceivedHttpError() HTTP错误响应时调用,处理HTTP错误。 onTooManyRedirects() 页面重定向过多时调用,处理重定向问题。 onSafeBrowsingHit() 访问到被谷歌标记为不安全的网页时调用,处理安全浏览警告。 shouldOverrideUrlLoading() WebView即将加载某个URL时调用,决定是否由应用而非WebView处理该URL。 shouldOverrideKeyEvent() onUnhandledKeyEvent() onScaleChanged() onPageCommitVisible() 页面内容即将显示时调用,通知页面内容即将可见。 页面开始显示 onPageFinished() 页面加载完成时调用,通知应用页面加载已经完成。 页面加载完成 用户 WebView WebViewClient Native层 (AwContents)

上面的时序图展示了从开始加载URL到页面加载完成的整个过程中WebView和WebViewClient的交互。每个回调都在特定的时机被触发,以处理不同的事件和状态变化。

二、WebView 加载过程中的原生层处理及代码示例

在这里插入图片描述

在使用 WebView 进行Android开发时,可能会遇到一系列实际问题。下面是一些常见问题的解决方案和代码示例,以帮助读者更有效地利用 WebView 的功能。

2.1 触发加载

用户或应用触发 loadUrl() 方法,开始加载指定的URL。在实际应用中,可能需要处理网络不可用的情况。以下是如何检查网络状态并相应地处理的示例代码:

if(isNetworkAvailable()) {
    webView.loadUrl("http://www.example.com");
} else {
    Toast.makeText(context, "Network is unavailable", Toast.LENGTH_LONG).show();
}

public boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}

2.2 原生层处理

WebView 调用其底层实现(通常是 AwContentsnativeLoadUrl() 方法)来开始网络请求。在这个阶段,开发者可能需要添加自定义的用户代理或处理重定向。示例代码如下:

webView.getSettings().setUserAgentString("CustomUserAgent");
webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (Uri.parse(url).getHost().equals("www.example.com")) {
            // This is my web site, so do not override; let my WebView load the page
            return false;
        }
        // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(intent);
        return true;
    }
});

2.3 页面加载的各阶段

在页面加载的各个阶段,开发者可以通过 onPageStarted()onLoadResource() 方法来显示加载指示器或日志资源加载情况。例如:

webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void onLoadResource(WebView view, String url) {
        super.onLoadResource(view, url);
        Log.d("WebView", "Resource Loaded: " + url);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        progressBar.setVisibility(View.GONE);
    }
});

2.4 处理特殊事件

在加载过程中,WebView 可能会遇到需要特殊处理的情况,如SSL错误处理。以下是如何处理SSL错误的示例:

webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage(R.string.notification_error_ssl_cert_invalid);
        builder.setPositiveButton("continue", (dialog, which) -> handler.proceed());
        builder.setNegativeButton("cancel", (dialog, which) -> handler.cancel());
        final AlertDialog dialog = builder.create();
        dialog.show();
    }
});

2.5 页面内容显示

当页面即将可见时,onPageCommitVisible() 方法被调用。这是更新UI或进行最后调整的好时机。例如:

webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageCommitVisible(WebView view, String url) {
        super.onPageCommitVisible(view, url);
        // Perform actions when page is almost visible
        Toast.makeText(context, "Page almost visible", Toast.LENGTH_SHORT).show();
    }
});

通过这些示例和解决方案,可以更好地理解和利用 WebView 的各种功能。

三、AwContents

本节介绍时序图中的AwContentsAwContents 是 Android WebView 的一个核心组件,它在 Android WebView 架构中扮演着非常重要的角色。AwContents 是 Chromium 项目的一部分,它负责管理 WebView 的内容渲染和事件处理。在 Android 系统中,AwContents 作为 WebView 的底层实现,提供了与 Chromium 引擎的直接交互接口。

3.1 主要功能和职责

  1. 内容渲染
    AwContents 负责将网页内容渲染到 WebView 组件上。它使用 Chromium 的渲染引擎(Blink)来解析 HTML、CSS 和 JavaScript,确保网页内容能够正确显示。

  2. 事件处理
    它处理来自上层 WebView 和 WebViewClient 的各种事件和请求,如页面加载、资源请求、导航事件等,并将这些事件转发到 Chromium 引擎。

  3. JavaScript 交互
    AwContents 提供了与 JavaScript 代码交互的接口,允许 Android 应用与网页中的 JavaScript 代码进行通信。

  4. 安全和隐私
    它实现了多种安全措施,如同源策略、内容安全策略等,以保护用户的安全和隐私。

  5. 网络请求管理
    AwContents 管理所有网络请求,包括图片、CSS 文件、JavaScript 文件等资源的加载。它支持自定义网络请求的处理,例如通过 shouldInterceptRequest() 方法拦截和修改请求。

3.2 架构和实现

AwContents 是一个桥接类,它连接了 Android 的 WebView API 和 Chromium 的底层实现。在 Android WebView 的架构中,AwContents 位于 Java 层和 native 层之间,它通过 JNI(Java Native Interface)与 native 代码进行交互。

在 native 层,AwContents 对应的实现是 content::WebContents,这是 Chromium 中用于管理网页内容的核心类。WebContents 负责页面的生命周期管理、渲染进程管理和页面内容的实际渲染。

3.3 使用场景

开发者通常不直接与 AwContents 交互,而是通过 WebView 提供的高级 API 来进行开发。然而,了解 AwContents 的工作原理对于解决 WebView 中的高级问题、性能优化以及实现自定义功能非常有帮助。

AwContents 是 Android WebView 中的一个关键组件,它使得 WebView 能够利用 Chromium 引擎的强大功能,提供高性能和高兼容性的网页浏览体验。

四、利用WebView回调函数检测白屏

在Android开发中,使用WebView时偶尔会遇到白屏问题,这通常是由于网页加载不完全、资源加载失败或者JavaScript错误等原因引起的。利用WebView的回调函数可以帮助我们检测并诊断这种白屏问题。以下是一些策略和步骤,展示如何使用WebView的回调函数来检测白屏:

4.1 使用onPageStartedonPageFinished检测加载时间

白屏可能是因为页面加载时间过长。通过记录onPageStartedonPageFinished之间的时间差,可以判断页面是否在合理的时间内完成加载。

webView.setWebViewClient(new WebViewClient() {
    long startTime;

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
        startTime = System.currentTimeMillis();
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        long loadTime = System.currentTimeMillis() - startTime;
        if (loadTime > ACCEPTABLE_LOAD_TIME) {
            // 记录或处理加载时间过长的情况
        }
    }
});

4.2 利用onReceivedErroronReceivedHttpError检测加载错误

这些回调函数可以帮助我们捕获在加载过程中发生的错误,这些错误可能会导致页面内容无法正确显示,从而出现白屏。

webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        super.onReceivedError(view, request, error);
        // 处理加载错误
    }

    @Override
    public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
        super.onReceivedHttpError(view, request, errorResponse);
        // 处理HTTP错误
    }
});

4.3 使用shouldInterceptRequest监控资源加载

如果关键资源(如CSS或JavaScript文件)加载失败,可能会导致页面显示不完整或白屏。通过shouldInterceptRequest可以监控这些资源的加载情况。

webView.setWebViewClient(new WebViewClient() {
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        // 检查请求的资源,记录加载失败的资源
        return super.shouldInterceptRequest(view, request);
    }
});

4.4 使用onPageCommitVisible

onPageCommitVisible在页面内容即将显示时调用,如果在这个阶段页面内容为空或不完整,可能是一个白屏的迹象。但是,onPageCommitVisible回调本身并不能直接提供页面内容的信息,我们需要结合其他方法来实现这个目标。一种可能的方法是在onPageCommitVisible回调中使用evaluateJavascript来检查页面的DOM结构。例如,我们可以检查某个关键元素是否存在,或者是否有内容。以下是一个示例:

webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageCommitVisible(WebView view, String url) {
        super.onPageCommitVisible(view, url);

        // 检查页面内容是否可见或部分内容是否缺失
        view.evaluateJavascript("(function() { return document.getElementById('keyElement').innerHTML; })();", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                if (value == null || value.isEmpty()) {
                    // 如果关键元素不存在或没有内容,那么可能存在白屏问题
                    Log.e("WebView", "Key element is missing or empty");
                }
            }
        });
    }
});

在这个示例中,我们假设keyElement是页面中的一个关键元素,我们通过JavaScript代码获取这个元素的内容,然后在回调中检查这个内容是否存在。如果不存在,那么可能存在白屏问题。

实际的检查方法可能需要根据你的具体需求进行调整。例如,你可能需要检查多个元素,或者使用更复杂的JavaScript代码来检查页面的状态。

4.5 结合JavaScript和evaluateJavascript

通过注入JavaScript代码检查DOM元素的存在或内容,可以帮助确认页面是否正确渲染。

webView.evaluateJavascript("(function() { return document.body.innerHTML; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String html) {
        if (html.isEmpty()) {
            // 处理白屏情况
        }
    }
});

通过上述方法,结合日志记录和异常处理机制,可以有效地检测和诊断WebView中的白屏问题。这些技术不仅可以帮助开发者提高应用的稳定性和用户体验,还可以在开发和测试阶段快速定位问题。

五、结论

WebView 的加载流程涉及复杂的交互和多个阶段,每个阶段都可能触发不同的事件回调。作为开发者,理解这些过程和回调的时机及其作用是非常重要的。这不仅可以帮助我们更有效地使用 WebView,还可以在开发过程中预见并解决潜在问题,从而创建更加稳定和可靠的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/900762.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据库、数据仓库、数据湖和数据中台有什么区别

很多企业在面对数据存储和管理时不知道如何选择合适的方式&#xff0c;数据库、数据仓库、数据湖和数据中台&#xff0c;这些方式都是什么&#xff1f;有什么样的区别&#xff1f;企业根据其业务类型该选择哪一种&#xff1f;本文就针对这些问题&#xff0c;来探讨下这些方式都…

基于Netty构建WebSocket服务并实现项目群组聊天和实时消息通知推送

文章目录 前言需求分析技术预研Web端方案服务端技术 技术方案设计思路功能实现添加依赖自定义NettyServer自定义webSocketHandler使用NettyServer向在线用户发送消息 需要完善的地方 前言 我们的项目有个基于项目的在线文档编制模块&#xff0c;可以邀请多人项目组成员在线协同…

2024mathorcup大数据竞赛B题【电商品类货量预测及品类分仓规划】思路详解

问题 1&#xff1a;建立货量预测模型&#xff0c;对该仓储网络 350 个品类未来 3 个月&#xff08;7-9月&#xff09;每个月的库存量及销量进行预测&#xff0c;其中库存量根据历史每月数据预测月均库存量即可&#xff0c;填写表 1 的预测结果并放在正文中&#xff0c;并将完整…

Discuz发布原创AI帖子内容生成:起尔 | AI原创帖子内容生成插件开发定制

Discuz发布原创AI帖子内容生成&#xff1a;起尔 | AI原创帖子内容生成插件开发定制 在当今互联网快速发展的时代&#xff0c;内容创作成为了网站运营、社交媒体管理和个人博客维护不可或缺的一部分。然而&#xff0c;高质量内容的创作往往耗时耗力&#xff0c;特别是对于需要频…

实现prometheus+grafana的监控部署

直接贴部署用的文件信息了 kubectl label node xxx monitoringtrue 创建命名空间 kubectl create ns monitoring 部署operator kubectl apply -f operator-rbac.yml kubectl apply -f operator-dp.yml kubectl apply -f operator-crd.yml # 定义node-export kubectl app…

Qt 支持打包成安卓

1. 打开维护Qt&#xff0c;双击MaintenanceTool.exe 2.登陆进去,默认是添加或移除组件&#xff0c;点击下一步&#xff0c; 勾选Android, 点击下一步 3.更新安装中 4.进度100%&#xff0c;完成安装&#xff0c;重启。 5.打开 Qt Creator&#xff0c;编辑-》Preferences... 6.进…

self-supervised learning(BERT和GPT)

1芝麻街与NLP模型 我們接下來要講的主題呢叫做Self-Supervised Learning&#xff0c;在講self-supervised learning之前呢&#xff0c;就不能不介紹一下芝麻街&#xff0c;為什麼呢因為不知道為什麼self-supervised learning的模型都是以芝麻街的人物命名。 因為Bert是一個非常…

maven下载依赖报错Blocked mirror for repositories

原因&#xff1a;Maven版本过高 解决办法 setting文件添加 或者降低maven版本 <mirrors><mirror><id>maven-default-http-blocker</id><mirrorOf>external:dummy:*</mirrorOf><name>Pseudo repository to mirror external reposit…

表格切割效果,“两个”表格实现列对应、变化一致

如何让两个表格的部分列对应且缩放一致 先看效果 使用一个原生table的即可实现 “两个”表格的视觉效果让“两个”表格的对应列缩放保持一致 废话不多说&#xff0c;直接上代码 html: <html><div><table><caption class"table-name">表格…

模拟信号采集显示器+GPS同步信号发生器制作全过程(焊接、问题、代码、电路)

1、制作最小系统板 在制作最小系统板的时候&#xff0c;要用USB转TTL给板子供电&#xff0c;留了一个电源输入的四个接口&#xff0c;同时又用排针引出来VCC和GND用于后续其他外设的电源供应&#xff0c;电源配有电源指示灯和保护电容&#xff0c; 当时在焊接的时候把接口处的…

设计模式(二)工厂模式详解

设计模式&#xff08;二&#xff09;工厂模式详解 简单工厂模式指由一个工厂对象来创建实例,适用于工厂类负责创建对象较少的情况。例子&#xff1a;Spring 中的 BeanFactory 使用简单工厂模式&#xff0c;产生 Bean 对象。 工厂模式简介 定义&#xff1a;工厂模式是一种创建…

机房巡检机器人有哪些功能和作用

随着数据量的爆炸式增长和业务的不断拓展&#xff0c;数据中心面临诸多挑战。一方面&#xff0c;设备数量庞大且复杂&#xff0c;数据中心内服务器、存储设备、网络设备等遍布&#xff0c;这些设备需时刻保持良好运行状态&#xff0c;因为任何一个环节出现问题都可能带来严重后…

java项目之电影评论网站(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的电影评论网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 电影评论网站的主要使用者管…

如何在 Ubuntu 24.04 上安装多PHP版本 (从8.3到5.6) ?

PHP 代表超文本预处理器&#xff0c;它仍然是网络的基石&#xff0c;为互联网上很大一部分网站和网络应用程序提供动力。大多数顶级网站和博客工具仍然使用 PHP&#xff0c;如 WordPress, Facebook, Wikipedia 等。如果你在 Ubuntu 24.04 上为 web 开发&#xff0c;安装 PHP 可…

算法的学习笔记—数组中只出现一次的数字(牛客JZ56)

&#x1f600;前言 在数组中寻找只出现一次的两个数字是一道经典的问题&#xff0c;通常可以通过位运算来有效解决。本文将详细介绍这一问题的解法&#xff0c;深入解析其背后的思路。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f970;数组中只出现一次的数字…

rtsp的2种收流模式

rtsp协商成功以后就是rtp收流&#xff0c;又分为两种模式:rtp over rtsp(tcp)和rtp over udp。 1.rtsp over rtsp 这个现在一般都叫TCP&#xff0c;它的特点是rtsp服务端和客户端是共用一个tcp链接&#xff0c;也就是说rtsp协议报文、rtp包、rtcp数据都是通过这一个链接来交互…

合约门合同全生命周期管理系统:企业合同管理的数字化转型之道

合约门合同全生命周期管理系统&#xff1a;企业合同管理的数字化转型之道 1. 引言 在现代企业中&#xff0c;合同管理已经不再是简单的文件存储和审批流程&#xff0c;而是企业合规性、风险管理和业务流程的关键环节之一。随着企业规模的扩大和合同数量的增加&#xff0c;传统…

第二单元历年真题整理

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 参考答案 1. A 2. A 3. A 4. D 5. D 6. D 解析&#xff1a; 栈和队列是两个不一样的结构&#xff0c;不能放在一起表示 7. B 8. C 解析&#xff1a; S --> A0 | B1 --> (S1 | 1) 0 | (S0 | 0)1 --> S10 | 10 | S…

51单片机快速入门之 模拟 I2C 用精准中断来控制

51单片机快速入门之 模拟 I2C 用精准中断来控制 首先复习一下51单片机快速入门之定时器和计数器(含中断基础) 再看看之前的I2C操作 51单片机快速入门之 IIC I2C通信 定时器/计数器是51单片机中用于实现精确延时的硬件资源。通过配置定时器的初始值和工作模式&#xff0c;可以…

Unable to open nested entry ‘********.jar‘ 问题解决

今天把现网版本的task的jar拖回来然后用7-zip打开拖了一个jar进去替换mysql-connector-java-5.1.47.jar 为 mysql-connector-java-5.1.27.jar 启动微服务的时候就报错下面的 Exception in thread "main" java.lang.IllegalStateException: Failed to get nested ar…