WebViewClient.shouldInterceptRequest是在UI线程中执行的,如果资源加载出现堵塞,展示时就会堵塞,所以需要异步加载外部资源。
在网上搜的全是用PipeInputStream/PipeOutputStream实现,测试后,发现这种方法在out输出内容时,PipeInputStream已经关闭。所以改成重载InputStream实现,测试通过。
下面例子是用http访问远程的资源,其中的http使用OkHttp实现的,并不难,所以没有贴这部分代码:
- public static class UrlInputStream extends InputStream {
- private final String url;
- private final Map
headers; -
- private byte[] stream = null;
- private int i = 0;
- private int len = 0;
-
- public UrlInputStream(String url, Map
headers) { - this.url = url;
- this.headers = headers;
- }
-
- @Override
- public int read() throws IOException {
- if (stream == null) { //stream初始化必须放在read中,放在构造函数中,就会阻塞调用线程
- Request req = http.buildGet(url, headers,null, null);
- try (Response resp = http.syncSend(req);
- ResponseBody body = resp.body()){
- stream = body.bytes();
- len = stream.length;
- LOG.debug("read {}, len:{}", url, len);
- } catch (IOException e) {
- LOG.error("Fail to get {}", url, e);
- }
- }
-
- if (i >= len) {
- return -1;
- }
- return (((int)stream[i++]) & 0xff); //此处小心,必须强转后去除高位,否则可能会返回负数
- }
- }
在WebViewClient.shouldInterceptRequest中使用UrlInputStream:
- InputStream in = new HttpClient.UrlInputStream(url, null);
- return new WebResourceResponse(mimeType, DEFAULT_CHARSET_NAME, in);