Shadow支持WebView使用file:///android_asset/协议加载插件资源的方法

9,881 阅读2分钟

在正常的App开发中,我们可以用这样的代码加载App的Assets中打包的Web页面。

webview.loadUrl("file:///android_asset/index.html");

Android系统实现这个功能时,并没有像我们想象的使用webview对象的Context去查找Assets资源。而是通过当前应用的ApplicationId,反查了当前安装的应用的apk路径。所以查到的自然是安装的宿主应用。在宿主的apk中自然是找不到插件的Assets的。关于这个设计的原因,我猜测大概是因为WebView的渲染是在一个单独的进程中的,所以不方便拿到当前webview对象的context。

作为插件框架,我们的目标始终是尽可能的让原本正常安装能运行的代码,在插件环境下也能运行。而这个API在业务开发中还比较常用,因此这个API的支持就比较重要了。

虽然我没有做过特别广泛的调研,但我确实没见过其他插件框架支持这个能力。所以我直接讲一下Shadow是怎么在不使用非公开API的前提下支持这个功能的。

我受到了“Web离线包”方案的启发。在“Web离线包”方案中,客户端可以在本地拦截http请求,然后以本地资源直接返回。通过将一些Web资源直接打包在客户端中,通过这种技术可以提高Web的加载速度。因此,我们就可以应用AOP思想,对WebView loadUrl中file:///android_asset/协议进行修改。

所以方案非常简单,先通过Shadow Transform将App中用到的WebView都换成ShadowWebView。再Override ShadowWebView的loadUrl方法。将请求来的file:///android_asset/协议都修改成http://android.asset/协议。然后就可以采用“Web离线包”的方法,从插件的Assets中拿出需要的资源返回给这个请求了。之所以要将file协议换成http协议,是因为这种拦截本地请求的能力只支持http协议。

关于这部分代码,请查看com.tencent.shadow.core.runtime.ShadowWebView类的实现。

PS:我刚刚Google的时候发现还有一个我们没用过的file:///android_res协议,有兴趣的同学可以帮忙实现一下,参与到Shadow的开源共建中来。