
避免WebView的快速交付陷阱------日后可能付出高昂代价.
学习应用开发
WebView 曾是企业快速在移动应用中集成网页功能的有利选择. 我不得不承认------得益于响应式网页设计和当今先进的网页技术, WebView 确实能让网站完美适配移动端界面, 甚至与原生应用无异.
然而, 凭借超过十年的移动开发经验, 我多次目睹这一决策最终反噬自身. 修复临时问题------或更糟糕的是, 用原生解决方案替换 WebView------的最终成本, 可能远高于从一开始就采用原生开发的方式.
这就是我分享这段反思的原因: 提供一些见解并提高意识. 在选择 WebView 进行快速功能交付之前, 了解潜在的长期权衡至关重要.
一个真实案例: WebView问题如何影响一款成功应用
某家在数字广告领域拥有数百万用户的大型成功企业, 其官网已建立多年. 随着iPhone和Android系统逐渐普及, 该公司明智地投资开发了首款iOS和Android应用.
两款应用均成功上线, 获得良好评价和积极反馈. 然而, 当时iPhone应用的用户基数更大, 而Android版本则落后于iPhone, 主要是因为当时大部分用户使用iPhone, 而Android系统仍处于发展初期. 因此, 该公司决定停止对Android应用的开发投资, 转而专注于iPhone应用.
后来, 一次重大后端服务升级要求两款应用均需更新. 尽管iOS应用仍有专门团队负责更新, 但Android版本已无团队维护. 为节省成本, 公司决定发布一个简化版的Android应用------仅包含一个Webview的单一活动界面, 该界面仅加载其移动网站.

单一Webview应用
起初, 这似乎是一个简单且成本效益高的解决方案. 该应用作为Android应用的2.0版本发布.
但问题很快浮出水面.
这款本质上仅由WebView组成的Android应用存在严重问题. 在网络连接较慢的情况下, 用户在加载时会遇到空白屏幕. 性能表现极差. 此前使用过完全原生版本1的应用的用户感到愤怒. 投诉蜂拥而至, 评分急剧下降------从平均4星以上跌至2星.
糟糕的评分也损害了应用在Google Play商店的可见性和推广效果. 正当Android用户群体开始快速增长时, 竞争对手趁机而入. 拥有完全原生Android应用的竞争对手提供了WebView应用无法支持的功能, 如推送通知和Android Wear兼容性.
最终, 该公司不得不做出重大决策. 他们重新聚焦于Android平台, 并花费超过一年时间从头开始原生重建应用. 尽管他们成功恢复部分用户信任并提升了使用体验, 但评分受损的影响仍持续存在. 无论他们如何改进应用, 历史上的低评分始终如影随形.
幸运的是,2019年谷歌推出了重置应用评分的功能(来源). 这为公司提供了急需的全新起点, 挽救了因早期依赖Webview而受损的声誉.
Webview: 通往网络世界的隐秘通道
我们都知道, 应用程序不应仅仅是一个被原生代码包裹的Webview. 核心体验------尤其是主界面------应采用原生开发, 以确保更好的性能, 响应速度和用户体验.
不过, 对于不太关键的界面, 如新闻文章或简单内容页面, 利用网页团队已完成的工作, 在WebView中展示网页内容是有意义的.
乍一看, 这似乎是个好主意. 一旦网页建成, 应用程序只需直接展示, 无需重复工作. 轻松获胜, 对吧?
但有一个常被忽视的陷阱.
大多数网页包含指向其他页面的链接. 这在网页上完全没问题, 但在应用的WebView中, 这些链接可能让用户离开你预期的内容. 有时他们甚至可能跳转到谷歌等搜索引擎, 使你的应用变成一个意外的网页浏览器.
例如:
- 新闻文章页面可能包含指向网站其他无关部分的页眉链接, 或页脚链接可能直接将用户带离应用.

示例网页: 页眉包含指向其他网页的链接

示意图: 应用的WebView如何被用作网页浏览器
如果处理不当, 你的WebView可能会成为通向更广泛网络的无法控制的入口.
这不仅会让用户感到困惑, 还可能引入安全风险并损害应用程序体验的完整性.
如何管理它
在应用程序中使用WebView时:
- 移除不必要的链接: 理想情况下, 加载简化版本的页面, 不包含页眉, 页脚或外部链接.
- 拦截导航: 捕获WebView内的链接点击, 并决定是否应在其他地方打开, 阻止或在应用内原生处理.
- 避免使用页面内导航: 网页内的页面内导航无法被WebView轻松拦截, 这使得无法正确管理用户返回应用内的流程.
通过谨慎管理WebView内容, 你可以保持应用专注, 安全且用户友好------同时避免意外将其转变为网页浏览器.
导航: 复杂的流程混合
从原生界面, 用户可通过WebView跳转至网页. 在WebView内, 用户可在不同网页间切换, 或应用可拦截特定 URL 将其引导回原生界面.
乍看之下, 原生屏幕与WebView之间的导航灵活性似乎颇具吸引力. 然而, 仔细分析后会发现, 导航路径的可能组合可能令人应接不暇.

在这些可能的状态下, 应用内的导航可能迅速变得不可预测, 导致几乎无限的流程组合.
为了说明这一挑战, 我将提供以下场景示例.

状态管理挑战
对于每个加载的屏幕, 应用程序必须维护其状态. 当深入应用程序时, 某些屏幕可能因优化而从内存中移除, 并在用户回退时需要恢复.
- 对于原生屏幕, 恢复状态相对简单.
- 对于基于WebView的屏幕, 恢复精确的网页状态------尤其是在处理滚动和多页面过渡时------会变得显著复杂.
考虑以下场景:
- 用户通过列表界面滚动至第一页之后再切换至下一页.
- 若应用被系统置于后台并最终终止, 能否保证所有WebView状态(包括滚动位置和导航历史)被正确恢复?
返回导航复杂性
- 从最后一个原生界面返回至第一个界面需要执行多少次"返回"操作? 三次? 四次?
- WebView中的"返回"操作是仅弹出顶层网页, 还是会关闭整个WebView并返回原生屏幕?
后果: 不稳定的用户体验
如此复杂的导航结构常导致不一致且存在缺陷的行为, 混淆用户并降低整体应用体验. 在原生与WebView元素之间管理导航需要精心规划, 以避免意外问题.
设计挑战: 确保视图一致性
随着移动开发规模扩大及多个团队参与应用开发, 集中式设计系统变得至关重要. 它确保所有团队遵循与组织领域一致的统一设计规范.
一个结构良好的设计系统可提供:
- 应用内一致的UI/UX.
- 通过简化复杂组件和工作流程加速功能开发.
- 全局视图控制, 如深色模式和浅色模式.
- 集中式无障碍功能增强, 以提升所有用户的可用性.
然而, 当WebView集成到应用中时, 网页也必须与应用的设计保持一致, 以维持无缝体验. 若缺乏这种一致性, 用户可轻易区分原生应用与嵌入的网页内容, 导致体验碎片化.
WebView集成挑战
- 应用程序和网页设计系统必须保持持续同步, 确保网页元素在视觉和功能上与原生应用保持兼容. 这为设计系统管理增加了额外的复杂性.
- WebView中的无障碍支持是一个重大挑战. 虽然原生应用依赖于内置的无障碍功能, 但网页必须明确设计以支持这些功能, 这需要额外的开发努力.

在原生和 WebView 组件之间保持一致且可访问的设计至关重要------但这也增加了设计和开发团队的负担.
WebView 的扩展: WebView 的增长
我们已掌握如何在 WebView 中有效管理与应用兼容的网页. 通过仅原生开发少量关键界面, 并将其余部分通过 WebView 与网页团队共享, 我们能加速开发并减少重复工作.
这种方法使团队能够根据需要快速添加新的WebView界面. 随着移动应用开发规模的扩大, WebView的数量也会相应增加------无论是作为用户界面显示的WebView, 还是WebView对象及其声明的实例数量. 随着时间的推移, 它们被视为与Activity, Fragment, ViewController或Screen Function类似的组件.

这种方法效果很好...直到它不再有效.
有一天, 你意识到所有WebView都需要符合新的要求. 例如:
- 你需要向每个WebView传递一致的跟踪数据.
- 你需要一个标志来指示应用处于深色模式, 以便网页可以相应调整.
突然间, 你发现WebView对象散布在整个代码库中. 现在你不得不翻遍整个仓库, 试图弄清楚每个WebView的位置以及它加载的网页.
此时, 一个想法浮现: 如果我们有一个中央集权的WebView来统领一切, 会怎样?
但随后另一个挑战浮现: 每个团队都根据自身需求对 WebView 进行了定制化修改. 如何将所有这些变体重构为一个能满足所有需求的"超级 WebView"?
随之而来的是一个痛苦的重构过程, 伴随着团队之间关于 WebView 核心功能的漫长讨论与协商. 经过数月的反复沟通, 最终引入了一个具备合理限制条件的通用 WebView. 这是一次痛苦的回顾教训.
认证: 两个不同的世界
并非所有网页都有相同的要求------有些网页的行为会根据用户是否已认证而有所不同. 在WebView中支持认证流程通常需要比标准网页更复杂的控制流程.
1. 在不同环境中共享认证状态
原生应用和WebView通常在不同的环境中运行, 因此共享认证状态并不像使用常见的环境变量那样简单.
常见的解决方案是通过 cookie 管理认证状态. 通过将认证令牌存储在 cookie 中, 原生应用和WebView均可访问共享的会话数据. 然而, 在原生和WebView环境之间保持这些 cookie 的同步可能充满挑战, 需要谨慎处理.

cookie 同步至关重要, 因为在用户与WebView交互时可能发生令牌刷新. 若未进行适当同步, 原生应用可能无法跟踪会话状态, 导致认证问题.
参考资料与进一步阅读
2. 触发用户登录流程
在原生应用中, 我们通常会设计专用的原生登录流程. 而在网页端, 通常会在网页本身内嵌独立的登录流程.
然而, 当网页在WebView中加载且用户需要登录时, 通常更倾向于提供一致的原生登录体验而非基于网页的登录流程. 通过原生流程完成登录后, 用户应无缝返回至WebView中其之前浏览的具体位置------可能位于网页流程的深层.

处理此流程 实现此类无缝体验可能较为复杂. 常见方法包括:
- JavaScript 桥接: 在WebView内容与原生代码之间建立通信通道, 以触发原生操作(如启动登录流程).
- DeepLink拦截: 拦截WebView中的特定 URL, 使原生应用接管并启动原生登录流程.
挑战 无论采用哪种方法, 这两种方案都较为脆弱, 需要精心同步. 存在许多边界情况需要处理, 例如:
- 用户登录失败
- 用户取消登录流程
- 确保登录后WebView会话正确更新
复杂性源于管理两个不同环境(网页和原生)之间的交互, 每个环境都有自己的生命周期, 状态管理和错误处理机制.
3. 升级身份验证框架
假设你计划将身份验证系统从 Auth 1.0 升级至 Auth 2.0 . 一个常见问题是: 应先升级应用还是网页?
在理想情况下 如果应用和网页平台完全独立运行, 你可以分别升级而无需过多担忧.
实际情况(涉及 WebView 时) 对于嵌入 WebView 并访问需要身份验证网页的应用程序, 情况会变得复杂:
- 你不能先升级应用程序, 因为 WebView 中加载的网页内容仍依赖于旧的身份验证流程.
- 你不能先升级网页, 因为旧版本的应用程序仍与 WebView 互动, 并期望使用旧的身份验证机制.
- 你不能同时升级两者, 因为WebView中的网页可能同时被新旧版本的应用访问.
唯一的实际升级路径
- 首先让网页同时支持Auth 1.0和Auth 2.0. 这确保了现有应用版本的向后兼容性, 同时允许新版本开始使用Auth 2.0.
- 发布使用 Auth 2.0 的更新版应用. 应用现在可以使用新的认证流程, 而网页继续支持两者.
- 在网页上保持双重支持. 直到可以安全地废弃 Auth 1.0------在所有用户都迁移到最新应用版本之后.

关键要点 将WebView与原生应用认证流程紧密耦合不仅会增加应用升级的复杂性, 还会提升网页团队的工作难度. 他们通常需要长期维护向后兼容性, 同时支持旧版和新版应用.
应用隐私: 审计范围超越应用本身...唉!
大约在2020年底, 苹果推出了[应用跟踪透明度(ATT)](https://www.z2adigital. com/blog-content/a-timeline-of-apples-privacy-changes-in-safari-and-ios-infographic), 要求应用在跨其他公司应用和网站跟踪用户前必须获得用户授权. 该政策于2021年正式实施, 不符合要求的应用将不再被允许发布.
如果我们的应用仅包含 Git 仓库中的 Swift 代码(一个 iOS 项目), 我们可以通过扫描代码库并确认符合苹果的要求来轻松审核它.
然而, 我们的应用不仅包含原生应用内容, 还通过 WebView 加载了大量网页. 与结构化的 iOS 代码库不同, 这些网页分散在不同域名中, 每个域名都有自己的独立代码历史. 其中许多网页已存在多年, 充斥着需要验证的遗留元素. 这使得审核过程复杂得多.

完成审核需要数月时间, 迫使我们搁置重要项目并调整公司业务优先级------仅为确保符合苹果的要求. 那时, 我们真希望应用程序从未包含WebView!
不久之后, 谷歌也为安卓应用引入了类似要求, 要求开发者在Google Play数据安全部分声明数据使用详情. 这意味着通过WebView加载的每个网页不仅要符合苹果的ATT要求, 还需遵守谷歌的Play数据安全政策------此外还需遵守其他法规如GDPR.
无法回退: WebView的网页将永久与应用绑定
曾有一个网页最初没有付费内容, 我们的应用WebView可正常加载该网页.
然而, 随着时间推移, 该网页引入了付费内容. 由于苹果禁止应用程序通过其**应用内购买(IAP)**系统以外的方式提供付费内容, 该网页不应再通过WebView访问.
团队最初认为这是一个简单的修复------只需阻止该网页在应用的原生界面中加载. 但他们忽略了一个关键细节:
同一网页(现已包含付费内容)仍可通过DeepLink和推送通知访问! 这形成了一个漏洞, 允许用户从应用内访问该网页------从而违反了苹果的政策. 幸运的是, 这个问题在应用内仍可修复.
遗憾的是, 该修复方案仅适用于最新版本的应用. 旧版本保持不变 , 这意味着使用过时版本的应用的用户仍可访问包含付费内容的网页. 另一个重大漏洞! 更糟糕的是, 除非强制关闭所有旧版本 的应用, 否则该问题无法修复.
唉! 这意味着应用现在违反了苹果的政策.

简而言之, 一旦网页通过WebView访问, 若后续引入付费内容, 便无法简单回滚. 唯一的繁琐变通方案?创建一个全新的网页------该网页不仅在最新应用版本中被明确屏蔽, 也在所有旧版本中被屏蔽. 这为网页团队增加了大量额外工作!
应用依赖性: 如何影响网页开发
当网页被集成到 WebView 中时, 它将无法再独立进行开发和发布. 对网页的任何修改都必须在应用内进行彻底测试, 以确保兼容性.
这是必要的, 因为:
- 防止破坏性更改, 意外链接或不应出现在应用中的内容.
- 确保符合 Apple 的 应用跟踪透明度 (ATT) 和 Google Play 数据安全 要求.
- 阻止直接购买 , 因为 Apple 要求使用 应用内购买 (IAP) 进行交易.
- 处理应用特定的标志和 cookie, 例如分析跟踪.
- 支持多个应用版本, 包括仍依赖WebView的初始版本.
因此, 网页开发变得更加复杂和受限, 尤其是旧版本应用必须继续正常运行.
另一个故事: WebView集成日益增长的复杂性
当WebView首次引入时, 开发者必须找到一种方法来指示网页是否在应用内加载. 最原始的方法是:
1. URL 参数
- 应用在网页 URL 后附加一个参数(例如
?source=app
). - 这种方法对第一个页面有效, 但后续每个网页都必须 传递相同的参数, 这使得导航变得复杂并增加了维护开销.
为了解决这个问题, 开发者探索了更好的方案:
2. 网页 Cookie
- 使用Cookie意味着信息可在网页间持久保存, 无需手动传递参数.
- 然而, 旧版本应用仍依赖URL参数, 因此两种方法必须同时支持.
后来, 团队发现了理想的解决方案:
3. User-Agent字符串
- 原生应用可修改User-Agent头部以指示网页是否在WebView中加载.
- 这种方法更干净, 更标准化, 减少了网页内手动处理的需要.
问题所在: 更多层级, 无实际收益
尽管User-Agent 方法看似改进, 但并未消除对URL参数和Cookie 的支持需求------因为旧版本应用仍依赖它们. 这不仅未简化开发, 反而为网页团队增加了另一层复杂性.
最终, 除非原生团队彻底停用旧版本应用, 否则网页团队仍需维护三种方法, 使WebView集成成为持续挑战.
避免使用WebView! 为应用未来做好准备
在移动应用开发初期, 苹果和谷歌引入WebView作为快速解决方案, 利用网页开发者构建原生应用. 然而, 随着原生开发技术的进步, 基于WebView的应用已不再是首选方案.
如今, 苹果和谷歌均不建议使用大量WebView的应用, 原因包括:
- 安全风险 --- 恶意网页可能访问应用的敏感数据.
- 技术限制 --- WebView 是一种过时的应用开发方法.
- 隐私问题 --- WebView 在应用环境中运行, 使得严格的安全边界难以实施.
为解决这些问题, 谷歌和苹果推出了 Chrome Custom Tabs 和 Safari 视图控制器 --- 这些更好的替代方案允许在应用内浏览, 同时将网页和应用环境分离, 从而提升隐私和安全性.
尽管 WebView 目前仍被允许使用, 但未来 苹果和谷歌可能实施更多限制 并不令人意外. 过度依赖 WebView 的开发者可能发现自己陷入了 过时且存在风险的开发方式.
通过放弃 WebView, 你不仅能让应用具备 未来兼容性 , 还能通过清晰分离网页和应用组件来 简化开发流程.
若无WebView, 则无此担忧!
好吧, 今天的内容就分享到这里啦!
一家之言, 欢迎拍砖!
Happy coding! Stay GOLDEN!