由于
并未暴露处设置DNS的接口,因而在
- WebView
场景下使用
- WebView
存在很多无法限制,但如果接入
- HttpDns
,则可以较好地植入
- WEEX
,本文主要介绍在
- HTTPDNS
场景下接入
- WEEX
的方案细节。
- HTTPDNS
在
运行时环境下,所有的逻辑最终都会转换到
- WEEX
中执行,网络请求也不例外。同时
- Native Runtime
也提供了自定义相应实现的接口,通过重写网络请求适配器,我们可以较为简单地接入HTTPDNS。在WEEX运行环境中,主要有两种网络请求:
- WEEX
网络请求 + HTTPDNS
- Stream
网络请求在Android端最终会通过DefaultWXHttpAdapter完成,同时
- Stream
也提供了相应的接口自定义网络请求适配器。具体的逻辑如下:
- 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
中植入HTTPDNS逻辑即可,具体逻辑可以参考如下代码:
- WXHttpdnsAdapter
- 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.setRequestProperty("Host", url.getHost());
- // HTTPS场景
- if (conn instanceof HttpsURLConnection) {
- final HttpsURLConnection httpsURLConnection = (HttpsURLConnection)conn;
- WXTlsSniSocketFactory sslSocketFactory = new WXTlsSniSocketFactory((HttpsURLConnection) conn);
- // SNI场景,创建SSLScocket解决SNI场景下的证书问题
- conn.setInstanceFollowRedirects(false);
- httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
- // HTTPS场景,证书校验
- httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- String host = httpsURLConnection.getRequestProperty("Host");
- Log.e(TAG, "verify host:" + host);
- if (null == host) {
- host = httpsURLConnection.getURL().getHost();
- }
- return HttpsURLConnection.getDefaultHostnameVerifier().verify(host, session);
- }
- });
- }
- } else {
- Log.e(TAG, "no corresponding ip found, return null");
- return null;
- }
- return conn;
- }
- }
网络请求 + HTTPDNS
- <image>
并没有提供默认的图片适配器实现,所以用户必须自行实现才能完成图片请求逻辑,具体步骤分为以下几步:
- WEEX
第一步:自定义图片请求适配器,实现
接口
- IWXImgLoaderAdapter
- public class HttpDnsImageAdapter implements IWXImgLoaderAdapter {
- @Override
- public void setImage(final String url, final ImageView view, WXImageQuality quality, final WXImageStrategy strategy) {
- ......
- }
- }
第二步:在
初始化时注册该图片适配器:
- WEEX
- private void initWeex() {
- InitConfig config=new InitConfig.Builder()
- .setImgAdapter(new HttpDnsImageAdapter())
- .build();
- WXSDKEngine.initialize(this,config);
- ......
- }
所以同
一样,我们只需在
- WXHttpdnsAdatper
植入
- HttpDnsImageAdapter
逻辑即可。具体代码可参考:
- HTTPDNS
- /**
- * Created by liyazhou on 2017/10/22.
- */
- public class WXHttpDnsImageAdapter implements IWXImgLoaderAdapter {
- @Override
- public void setImage(final String url, final ImageView view, WXImageQuality quality, final WXImageStrategy strategy) {
- Log.e(TAG, "img url:" + url);
- execute(new Runnable() {
- @Override
- public void run() {
- ......
- HttpURLConnection conn = null;
- try {
- conn = createConnection(url);
- ....
- // 将得到的数据转化成InputStream
- InputStream is = conn.getInputStream();
- // 将InputStream转换成Bitmap
- final Bitmap bitmap = BitmapFactory.decodeStream(is);
- WXSDKManager.getInstance().postOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setImageBitmap(bitmap);
- }
- }, 0);
- ......
- }
- });
- }
- protected HttpURLConnection createConnection(String originalUrl) throws IOException {
- mHttpDnsService = HttpDnsManager.getInstance().getHttpDnsService();
- if (mHttpDnsService == null) {
- URL url = new URL(originalUrl);
- return (HttpURLConnection) url.openConnection();
- } else {
- return httpDnsRequest(originalUrl, null);
- }
- }
- private HttpURLConnection httpDnsRequest(String path, String reffer) {
- HttpURLConnection httpDnsConn = null;
- HttpURLConnection originalConn = null;
- URL url = null;
- try {
- url = new URL(path);
- originalConn = (HttpURLConnection) url.openConnection();
- // 异步接口获取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);
- httpDnsConn = (HttpURLConnection) new URL(newUrl).openConnection();
- // 设置HTTP请求头Host域
- httpDnsConn.setRequestProperty("Host", url.getHost());
- // HTTPS场景
- if (httpDnsConn instanceof HttpsURLConnection) {
- final HttpsURLConnection httpsURLConnection = (HttpsURLConnection)httpDnsConn;
- WXTlsSniSocketFactory sslSocketFactory = new WXTlsSniSocketFactory((HttpsURLConnection) httpDnsConn);
- // sni场景,创建SSLScocket解决SNI场景下的证书问题
- httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
- // https场景,证书校验
- httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- String host = httpsURLConnection.getRequestProperty("Host");
- if (null == host) {
- host = httpsURLConnection.getURL().getHost();
- }
- return HttpsURLConnection.getDefaultHostnameVerifier().verify(host, session);
- }
- });
- }
- } else {
- return originalConn;
- }
- ......
- int code = httpDnsConn.getResponseCode();// Network block
- if (needRedirect(code)) {
- String location = httpDnsConn.getHeaderField("Location");
- if (location == null) {
- location = httpDnsConn.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 httpDnsRequest(location, path);
- } else {
- return originalConn;
- }
- }
- return originalConn;
- }
- }
上述方案详细代码建:WeexAndroid
来源: http://click.aliyun.com/m/34304/