Android WebView注入JS覆盖已有对象失效问题咨询
Android WebView持久覆盖页面对象行为实现方案
问题场景
Android端WebView加载不可修改源码的第三方网页时,需要覆盖页内指定JS对象的行为;页面执行过程中会通过构造函数重新实例化目标对象,导致常规注入的自定义逻辑被覆盖,核心执行逻辑如下:
obj.init();// 调用注入的ObjInterface类方法 obj = new ObjectClass(); obj.init();// 调用ObjectClass原生方法,注入逻辑失效
当前已完成的WebView基础配置:
settings.setJavaScriptEnabled(true); settings.setCacheMode(WebSettings.LOAD_NO_CACHE); settings.setDomStorageEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setAllowFileAccess(true);
具体实现方式
核心思路是从根源Hook对象的构造逻辑,而非直接修改对象实例,保证页面后续所有实例化操作生成的对象都带有自定义逻辑,同时要把注入时机提前到页面所有脚本执行之前,避免注入过晚失效。
- 优先选择文档起始阶段注入Hook脚本
不要在onPageFinished回调中做注入,该回调触发时页面同步脚本已经执行完成,很容易错过Hook时机。如果项目接入了AndroidX WebView依赖,直接调用WebViewCompat.addDocumentStartJavaScript()注入脚本,该API会保证脚本在页面DOM构建、所有JS资源加载执行前就运行,是最稳定的注入时机。
如果没有接入AndroidX WebView,就在WebViewClient.onPageStarted回调中第一时间调用evaluateJavascript()注入脚本,大部分场景下也能抢在页面前置脚本前执行。 - 注入的Hook脚本参考如下,直接重写全局构造函数,从实例生成阶段就植入自定义逻辑:
// 保存原始构造函数的引用 const originObjectClass = window.ObjectClass; // 替换全局构造函数 window.ObjectClass = function (...args) { // 调用原始构造逻辑生成正常实例 const instance = new originObjectClass(...args); // 保存原始方法引用 const originInit = instance.init.bind(instance); // 覆盖目标方法 instance.init = function () { // 这里写自定义逻辑,比如调用安卓侧注入的接口方法 window.YourInjectInterface.customInit(); // 不需要保留原有逻辑就删掉下面这行 return originInit(); }; // 其他需要覆盖的方法按照相同逻辑处理即可 return instance; }; // 拷贝原型链,避免原型方法访问异常 window.ObjectClass.prototype = originObjectClass.prototype;
这段逻辑执行后,不管页面后续执行多少次new ObjectClass(),生成的实例都会自动带上你覆盖的自定义逻辑,不会被重置。
- 兜底方案应对特殊场景
如果遇到目标构造函数没有挂在window全局、页面做了构造函数校验的情况,可以重写WebViewClient.shouldInterceptRequest方法,拦截页面对应JS文件的请求,拿到响应的JS内容后,把上面的Hook逻辑插入到JS代码的最开头,再将修改后的内容返回给WebView。这种方式相当于直接修改了页面加载的JS源码,无论页面逻辑怎么写都绕不开自定义修改。
避坑提醒
- 不要直接通过给
window.obj赋值的方式覆盖对象,页面代码中显式执行obj = new ObjectClass()时会直接替换该变量的引用,自定义逻辑会立刻失效。 - 如果需要调用安卓侧注入的接口,记得提前通过
addJavascriptInterface把对应接口类注入到WebView,注意不要暴露敏感方法避免安全风险。
内容的提问来源于stack exchange,提问作者Bilbo
相关产品推荐
相关产品推荐

