Android端WEEX + HTTPDNS 最佳实践

来源:互联网 发布:软件过程 编辑:程序博客网 时间:2024/06/18 16:29

由于WebView并未暴露处设置DNS的接口,因而在WebView场景下使用HttpDns存在很多无法限制,但如果接入WEEX,则可以较好地植入HTTPDNS,本文主要介绍在WEEX场景下接入HTTPDNS的方案细节。

我有几张阿里云幸运券分享给你,用券购买或者升级阿里云相应产品会有特惠惊喜哦!把想要买的产品的幸运券都领走吧!快下手,马上就要抢光了。

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.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;    }}

1.2 <image>网络请求 + HTTPDNS

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/

原创粉丝点击