Android端HTTPDNS+Weex最佳实践
由于WebView
并未暴露出设置DNS的接口,因而在WebView
场景下使用HTTPDNS
存在很多限制,但如果接入WEEX
,则可以较好地植入HTTPDNS
,本文主要介绍在WEEX
场景下接入HTTPDNS
的方案细节。
重要
当前最佳实践文档只针对结合使用时,如何使用HTTPDNS解析出的IP,关于HTTPDNS本身的解析服务,请先查看Android SDK接入。
代码示例
HTTPDNS+Weex方案详细代码参见Weex+HTTPDNS Android Demo。
概述
在WEEX
运行时环境下,所有的逻辑最终都会转换到Native Runtime
中执行,网络请求也不例外。同时WEEX
也提供了自定义相应实现的接口,通过重写网络请求适配器,我们可以较为简单地接入HTTPDNS。在WEEX运行环境中,主要有两种网络请求:
通过Stream进行的网络请求
标签指定的加载图片的网络请求
Stream网络请求+HTTPDNS
Stream
网络请求在Android端最终会通过DefaultWXHttpAdapter完成,同时WEEX
也提供了相应的接口自定义网络请求适配器。
具体的逻辑如下:
创建自定义网络请求适配器,实现
IWXHttpAdapter
接口public class WXHttpdnsAdatper implements IWXHttpAdapter { @Override public void sendRequest(final WXRequest request, final OnHttpListener listener) { ...... } }
该接口需要实现
sendRequest
方法,该方法传入一个WXRequest
对象的实例,该对象提供了以下信息:public class WXRequest { // The request parameter public Map<String, String> paramMap; // The request URL public String url; // The request method public String method; // The request body public String body; // The request time out public int timeoutMs = WXRequest.DEFAULT_TIMEOUT_MS; // The default timeout public static final int DEFAULT_TIMEOUT_MS = 3000; }
通过该对象我们可以获取到请求报头、URL、方法以及body。
在
WEEX
初始化时注册自定义网络适配器,替换默认适配器:InitConfig config=new InitConfig.Builder() .setHttpAdapter(new WXHttpdnsAdatper()) // 注册自定义网络请求适配器 ...... .build(); WXSDKEngine.initialize(this,config);
之后所有的网络请求都会通过
WXHttpdnsAdatper
实现,所以只需要在WXHttpdnsAdapter
中植入HTTPDNS逻辑即可,具体逻辑可以参考如下代码:public class WXHttpdnsAdatper implements IWXHttpAdapter { ...... private void execute(Runnable runnable){ if(mExecutorService==null){ mExecutorService = Executors.newFixedThreadPool(3); } mExecutorService.execute(runnable); } @Override public void sendRequest(final WXRequest request, final OnHttpListener listener) { if (listener != null) { listener.onHttpStart(); } Log.e(TAG, "URL:" + request.url); execute(new Runnable() { @Override public void run() { WXResponse response = new WXResponse(); WXHttpdnsAdatper.IEventReporterDelegate reporter = getEventReporterDelegate(); try { // 创建连接 HttpURLConnection connection = openConnection(request, listener); reporter.preConnect(connection, request.body); ...... } catch (IOException |IllegalArgumentException e) { ...... } } }); } private HttpURLConnection openConnection(WXRequest request, OnHttpListener listener) throws IOException { URL url = new URL(request.url); // 创建一个植入HTTPDNS的连接 HttpURLConnection connection = openHttpDnsConnection(request, request.url, listener, null); return connection; } private HttpURLConnection openHttpDnsConnection(WXRequest request, String path, OnHttpListener listener, String reffer) { HttpURLConnection conn = null; URL url = null; try { url = new URL(path); conn = (HttpURLConnection) url.openConnection(); // 创建一个接入httpdns的连接 HttpURLConnection tmpConn = httpDnsConnection(url, path); ...... int code = conn.getResponseCode();// Network block Log.e(TAG, "code:" + code); // SNI场景下通常涉及重定向,重新建立新连接 if (needRedirect(code)) { Log.e(TAG, "need redirect"); String location = conn.getHeaderField("Location"); if (location == null) { location = conn.getHeaderField("location"); } if (location != null) { if (!(location.startsWith("http://") || location .startsWith("https://"))) { //某些时候会省略host,只返回后面的path,所以需要补全url URL originalUrl = new URL(path); location = originalUrl.getProtocol() + "://" + originalUrl.getHost() + location; } Log.e(TAG, "code:" + code + "; location:" + location + "; path" + path); return openHttpDnsConnection(request, location, listener, path); } else { return conn; } } else { // redirect finish. Log.e(TAG, "redirect finish"); return conn; } } ...... return conn; } private HttpURLConnection httpDnsConnection(URL url, String path) { HttpURLConnection conn = null; // 通过HTTPDNS SDK接口获取IP String ip = HttpDnsManager.getInstance().getHttpDnsService().getIpByHostAsync(url.getHost()); if (ip != null) { // 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置 Log.d(TAG, "Get IP: " + ip + " for host: " + url.getHost() + " from HTTPDNS successfully!"); String newUrl = path.replaceFirst(url.getHost(), ip); try { conn = (HttpURLConnection) new URL(newUrl).openConnection(); } catch (IOException e) { return null; } // 设置HTTP请求头Host域 conn.setRequestPro