Android端WEEX + HTTPDNS 最佳实践
Android 实践 最佳 weex
2023-09-27 14:26:36 时间
由于WebView并未暴露处设置DNS的接口,因而在WebView场景下使用HttpDns存在很多无法限制,但如果接入WEEX,则可以较好地植入HTTPDNS,本文主要介绍在WEEX场景下接入HTTPDNS的方案细节。
在WEEX运行时环境下,所有的逻辑最终都会转换到Native Runtime中执行,网络请求也不例外。同时WEEX也提供了自定义相应实现的接口,通过重写网络请求适配器,我们可以较为简单地接入HTTPDNS。在WEEX运行环境中,主要有两种网络请求:
通过Stream进行的网络请求 标签指定的加载图片的网络请求 1 Stream网络请求 + HTTPDNSStream网络请求在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;1.2 image 网络请求 + HTTPDNS
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; }
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
Android C++系列:vector最佳实践 C++中模板本身不是类,是编译器生成类或者函数的说明,要使用模板需要编译器根据模板来创建类的实例化过程,我们代码中使用的是类,所以在使用模板是,要指定编译器将模板实例化成那种类型。
Android C++系列:string最佳实践 在C语言中我们操作String要相对麻烦些,每次字符串拼接都要重新开辟空间,再把数据拷贝进去,使用上没有那么便捷。
Android C++系列:C++最佳实践6 constexpr与decltype 上一篇介绍了const关键字,主要修饰变量,起到不可改变的常量作用。有一种值不会改变并且在编译过程就能得到计算结果的表达式我们称为常量表达式。
Android C++系列:C++最佳实践5 const 在Java中我们定义常量通常用final static TYPE variableName = xxx来实现,在C语言中我们通常用预编译宏来实现:#define MAX 100,在C++中虽然我们仍可以使用预编译宏,但是已经不推荐这么干了。
Android C++系列:C++最佳实践4多重继承与虚继承 Java和C++在语法层面比较的时候就不得不提到C++的多继承,我们知道Android是单继承,C++是多继承。在大型项目中不可避免的会用到多继承,本文分析C++多继承的一些特征。
Android C++系列:C++最佳实践3继承与访问控制 整个结构还是比较简单的,从类内部到本包到子类到外部包权限越来越小,比较好理解也比较好记忆。但是在C++中访问控制要复杂很多,因为不仅有属性和方法的访问控制,还有继承时的派生列表访问说明符。今天我们着重了解访问控制。
WEEX + HTTPDNS iOS解决方案 ## WEEX + HTTPDNS iOS解决方案 由于`WebView`并未暴露处设置DNS的接口,因而在`WebView`场景下使用`HttpDns`存在很多无法限制,但如果接入`WEEX`,则可以较好地植入`HTTPDNS`,本文主要介绍在`WEEX`场景下接入`HTTPDNS`的方案细节。 在`WEEX`运行时环境下,所有的逻辑最终都会转换到`Native Runtime`中执
Weex Android 动画揭秘 # 背景 在目前常见的交互方式中,动画扮演了一个重要的角色。 在 Weex 框架下,Weex 的动画需要屏蔽 CSS/JS 动画与 Android 动画系统的差异,并尽可能的达到60FPS。 本文阐述了在 Android 上实现高性能CSS/JS动画过程中所遇到的问题/相关数学知识及解决方案。本文使用的前端 DSL 为 Weex vue 1.0或 Weex Vue 2.0。
相关文章
- Hook android系统调用的实践
- Android Hook框架adbi的分析(3)---编译和inline Hook实践
- Android 中的adapter和作用以及常见的adapter
- Android | 说说Presentation
- Android应用性能优化最佳实践.1.4 本章小结
- 《Android 应用案例开发大全(第二版)》——2.5节辅助绘制类
- Android NDK 构建 以及一些错误
- 《OpenGL ES应用开发实践指南:Android卷》—— 1.4 创建Renderer类
- 《OpenGL ES应用开发实践指南:Android卷》—— 1.6 小结
- 《OpenGL ES应用开发实践指南:Android卷》—— 2.1 为什么选择空气曲棍球
- 《OpenGL ES应用开发实践指南:Android卷》—— 2.5 引入OpenGL管道
- 《OpenGL ES应用开发实践指南:Android卷》—— 2.6 OpenGL颜色模型
- 《OpenGL ES应用开发实践指南:Android卷》—— 2.7 小结
- 《OpenGL ES应用开发实践指南:Android卷》—— 3.4 做最后的拼接
- Android Activity启动流程, app启动流程,APK打包流程, APK安装过程
- Android单元测试与日志输出
- 【Android Studio报错】:Failed to read key AndroidDebugKey from store “C:Users13181.androiddebug.keys
- 专项:Android 内存泄露实践分析