怎么用原生开发使 react native 实现对 webview 的广告拦截

7 天前
 Mistsink

如题,我使用 expo 开发 react native 应用时,使用 react-native-webview 作为 RN 中的 Webview 组件。希望能够使用如 ghostery/adblocker 这样的第三方库来完成广告拦截。但是发现这个 Webview 组件没有开放什么接口可以拦截或者获取一个网页中所有的资源请求,如各种 js 文件,css 文件,这些资源请求没法拦截,所以没法使用 adblocker 完成相应的拦截。

我有一个想法能不能使用原生开发,替换 android 、ios 底层的 webview 组件,然后开放几个函数用于 onLoadStart 、onLoadEnd ,这几个函数可以在 RN 层级中作为参数传给 Webview 组件。这个方案是否可行呢?如果可行的话我的顾虑是:RN 中使用的 react-native-webview 的 webview 组件会不会是自己在底层替换的 webview 组件呢?如果不是,这个应该怎么解决呢?

如果解决思路有问题,非常想请教大家有没有更好的思路呢?

955 次点击
所在节点    程序员
16 条回复
mxT52CRuqR6o5
7 天前
用 injectedJavaScriptBeforeContentLoaded 注入 js 代码,hook 会导致资源加载的 api 并 MutationObserver 监视资源加载,进行阻止,不知道行不行
Mistsink
7 天前
@mxT52CRuqR6o5 MutationObserver 只能监听 dom 变化吧,不能监听网络请求呢,这个应该没法做到广告拦截的效果呢,我看主流的那些方案都是拦截网络请求+注入 css+注入 js 来实现的。现在的难点就是怎么拦截所有的网络请求。
mxT52CRuqR6o5
7 天前
@Mistsink #2 加载 css 、js 通常需要把 link 、script 标签加载到 html 里(但我不清楚 html 首屏的 script 标签 MutationObserver 能不能监听到)
Mistsink
7 天前
@mxT52CRuqR6o5 这里感觉不好控制呢,一旦 MutationObserver 创建的慢了就会漏掉很多,而且可能也会造成性能压力。你觉得呢?
br_wang
7 天前
@Mistsink #4 MutationObserver 创建前先对现有的 DOM 标签筛选、处理一遍?
Mistsink
7 天前
@br_wang 这种方案我觉着可能有些 hack 呢,不好接入现成的拦截方案。
ltaoo1o
7 天前
没用过 react-native-webview ,用过 flutter-inappwebivew ,思路应该类似。

1. FlutterInAppWebivew
2. Windows Webview2 (原生组件)

在 flutter 端调用 webview 组件并传入 shouldInterceptRequest 属性,此时原生代码调用 Webiview2 ,Webview2 调用 add_WebResourceRequestd 方法可以监听所有网络请求
https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp#L874

并调用 flutter 传入的 shouldInterceptRequest
https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp#L945

在回调中判断 shouldInterceptRequest 是否有返回值,如果没有就不响应(等于拦截了请求),也可以修改响应(返回自定义结果)
上面是 Windows 代码,它还有 ios 和 android 不过我没用上,你可以参考他的原生代码那块。

另外 flutter-inappwebivew 内置了 ad block 功能,也可以参考他是怎么实现的
https://github.com/pichillilorenzo/flutter_inappwebview_examples/tree/main/webview_ad_blocker
br_wang
7 天前
@Mistsink #6 最后都是一遍一遍扫,只是补齐触发时机点而已。针对 ad 的现有拦截方案也都是这个思路吧。
Mistsink
7 天前
@br_wang 明白,也可以实现,但有的情况,就比如一个 script 标签,我应该拦截他,不能让他加载,但是这种方案不能保证我在他加载运行之前就把他删掉吧?
Mistsink
7 天前
@li1218040201 是,我记得好像 flutter 是开放了这种细粒度的接口,可以针对每个资源的网络请求决定是否拦截,但是 RN 的 webview 没有这种细粒度的,就只有这个网页要不要加载。
我是不是还得像题目里说的那样去改 rn 的 webview 源码,用原生去写呐😂
daysv
7 天前
注入 service worker , 然后统一拦截
br_wang
7 天前
@Mistsink #9 不光外部脚本会插广告,网站自己内部脚本也会啊。纯靠脚本拦截,就无法拦截网站自己脚本插入的广告吧。
Mistsink
7 天前
@br_wang 嗷那也确实,写在 script 里面的 js 那是没法拦截了,后面我试试扫一遍再注入 js 的法子。
@daysv webview 中也可以注入 service worker 吗? service worker 不是浏览器自己整的一个独立的 js 进程吗?这块我不太懂 hhh ,不清楚能不能把别的扩展的 service worker 直接拿来用
Mistsink
6 天前
@br_wang 破案了,根据 html 中的 dom 标签,然后注入相应的 css 和 js 行不通呢,我是拿 https://adblock-tester.com/测试的,分数没变
ltaoo1o
6 天前
我看了下 API 文档,有个 onShouldStartLoadWithRequest ,具体说明就是可以决定是否继续某个请求,看 issue 也有人问 https://github.com/react-native-webview/react-native-webview/issues/286 ,也是说用这个 API ,这个试试看呢?
Mistsink
6 天前
@li1218040201 hh 就是这个不行呢,只有网页加载时候执行一次,里面的 js 等资源请求时不会触发

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1095183

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX