阿里sdk
2
EdgeHttpDNS/sdk/android/app/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/build
|
||||
/libs/alicloud-android-httpdns-*.aar
|
||||
84
EdgeHttpDNS/sdk/android/app/build.gradle
Normal file
@@ -0,0 +1,84 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.aliyun.ams.httpdns.demo'
|
||||
compileSdkVersion 34
|
||||
buildToolsVersion "30.0.2"
|
||||
defaultConfig {
|
||||
applicationId "com.aliyun.ams.httpdns.demo2"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
forTest {
|
||||
// 注意这里的配置,并不是需要编译forTest的app,而是避免httpdns-sdk在AndroidStudio改为end2end运行测试时 BuildVariants报错
|
||||
initWith release
|
||||
debuggable true
|
||||
}
|
||||
}
|
||||
|
||||
variantFilter { variant ->
|
||||
def names = variant.flavors*.name
|
||||
def type = variant.buildType.name
|
||||
// To check for a certain build type, use variant.buildType.name == "<buildType>"
|
||||
if ((names.contains("normal") && type.contains("forTest"))
|
||||
|| (names.contains("intl") && type.contains("forTest"))
|
||||
|| (names.contains("end2end") && type.contains("release"))
|
||||
|| (names.contains("end2end") && type.contains("debug"))
|
||||
) {
|
||||
// Gradle ignores any variants that satisfy the conditions above.
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
all {
|
||||
jvmArgs '-noverify'
|
||||
systemProperty 'robolectric.logging.enable', true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "version"
|
||||
|
||||
productFlavors {
|
||||
normal {
|
||||
|
||||
}
|
||||
|
||||
intl {
|
||||
|
||||
}
|
||||
|
||||
end2end {
|
||||
// 注意这里的配置,并不是需要编译end2end的app,而是避免httpdns-sdk在AndroidStudio改为end2end运行测试时 BuildVariants报错
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(':httpdns-sdk')
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:3.9.0")
|
||||
}
|
||||
30
EdgeHttpDNS/sdk/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/liyazhou/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
-dontwarn okhttp3.**
|
||||
-dontwarn okio.**
|
||||
-dontwarn com.alibaba.sdk.android.httpdns.test.**
|
||||
-dontwarn com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector
|
||||
-keep class com.aliyun.ams.ipdetector.Inet64Util{*;}
|
||||
27
EdgeHttpDNS/sdk/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".HttpDnsActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".SDNSActivity"
|
||||
android:exported="false" />
|
||||
<activity android:name=".WebViewActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,356 @@
|
||||
package com.aliyun.ams.httpdns.demo;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.DegradationFilter;
|
||||
import com.alibaba.sdk.android.httpdns.NetType;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingBean;
|
||||
import com.aliyun.ams.httpdns.demo.base.BaseActivity;
|
||||
import com.aliyun.ams.httpdns.demo.http.HttpUrlConnectionRequest;
|
||||
import com.aliyun.ams.httpdns.demo.okhttp.OkHttpRequest;
|
||||
import com.aliyun.ams.httpdns.demo.utils.ThreadUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* @author zonglin.nzl
|
||||
* @date 8/30/22
|
||||
*/
|
||||
public class HttpDnsActivity extends BaseActivity {
|
||||
|
||||
public static final String SCHEMA_HTTPS = "https://";
|
||||
public static final String SCHEMA_HTTP = "http://";
|
||||
|
||||
public static final String TAOBAO_URL = "www.taobao.com";
|
||||
public static final String ALIYUN_URL = "www.aliyun.com";
|
||||
|
||||
public static final String[] hosts = new String[]{
|
||||
TAOBAO_URL, ALIYUN_URL
|
||||
};
|
||||
|
||||
public static String getUrl(String schema, String host) {
|
||||
return schema + host;
|
||||
}
|
||||
|
||||
/**
|
||||
* 要请求的schema
|
||||
*/
|
||||
private String schema = SCHEMA_HTTPS;
|
||||
/**
|
||||
* 要请求的域名
|
||||
*/
|
||||
private String host = ALIYUN_URL;
|
||||
/**
|
||||
* 要解析的ip类型
|
||||
*/
|
||||
private RequestIpType requestIpType = RequestIpType.v4;
|
||||
|
||||
private HttpUrlConnectionRequest httpUrlConnectionRequest;
|
||||
private OkHttpRequest okHttpRequest;
|
||||
private NetworkRequest networkRequest = httpUrlConnectionRequest;
|
||||
|
||||
private ExecutorService worker = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
httpUrlConnectionRequest = new HttpUrlConnectionRequest(this);
|
||||
okHttpRequest = new OkHttpRequest(this);
|
||||
networkRequest = httpUrlConnectionRequest;
|
||||
|
||||
addFourButton("切换实例", v -> {
|
||||
MyApp.getInstance().changeHolder();
|
||||
sendLog("httpdns实例已切换");
|
||||
}, "获取配置", v -> {
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getCurrentConfig());
|
||||
sendLog("要解析的域名是" + host);
|
||||
sendLog("要解析的ip类型是" + requestIpType.name());
|
||||
sendLog("要模拟请求的url是" + getUrl(schema, host));
|
||||
sendLog("模拟请求的网络框架是" + (networkRequest == httpUrlConnectionRequest ? " HttpUrlConnection" : "OkHttp"));
|
||||
}, "清除配置缓存", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().cleanSp();
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "配置缓存清除, 重启生效");
|
||||
}, "清除日志", v -> cleanLog());
|
||||
|
||||
addTwoButton("开启https", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setEnableHttps(true);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "开启https");
|
||||
}, "关闭https", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setEnableHttps(false);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "关闭https");
|
||||
});
|
||||
|
||||
addTwoButton("允许过期IP", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setEnableExpiredIp(true);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "允许过期IP");
|
||||
}, "不允许过期IP", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setEnableExpiredIp(false);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "不允许过期IP");
|
||||
});
|
||||
|
||||
|
||||
addTwoButton("允许持久化缓存", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setEnableCacheIp(true);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "允许持久化缓存");
|
||||
}, "不允许持久化缓存", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setEnableCacheIp(false);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "不允许持久化缓存");
|
||||
});
|
||||
|
||||
addThreeButton("设置中国大陆", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setRegion(null);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "切换到中国大陆");
|
||||
}, "设置中国香港", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setRegion("hk");
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "切换到中国香港");
|
||||
}, "设置新加坡", v -> {
|
||||
MyApp.getInstance().getCurrentHolder().setRegion("sg");
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "切换到新加坡");
|
||||
});
|
||||
|
||||
addEditTextButton("超时时长 ms", "设置超时ms", view -> {
|
||||
EditText et = (EditText) view;
|
||||
int timeout = Integer.parseInt(et.getEditableText().toString());
|
||||
MyApp.getInstance().getCurrentHolder().setTimeout(timeout);
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "设置超时 " + timeout);
|
||||
});
|
||||
|
||||
addTwoButton("开启降级", v -> {
|
||||
// 注意:降级过滤器现在需要通过InitConfig设置,重启应用生效
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "降级功能需要通过InitConfig配置");
|
||||
}, "关闭降级", v -> {
|
||||
// 注意:降级过滤器现在需要通过InitConfig设置,重启应用生效
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "降级功能需要通过InitConfig配置");
|
||||
});
|
||||
|
||||
addTwoButton("开启网络变化后预解析", v -> {
|
||||
// 注意:此功能现在需要通过InitConfig设置,重启应用生效
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "网络变化后预解析需要通过InitConfig配置");
|
||||
}, "关闭网络变化后预解析", v -> {
|
||||
// 注意:此功能现在需要通过InitConfig设置,重启应用生效
|
||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "网络变化后预解析需要通过InitConfig配置");
|
||||
});
|
||||
|
||||
addView(R.layout.item_autocomplete_edittext_button, view -> {
|
||||
final AutoCompleteTextView actvOne = view.findViewById(R.id.actvOne);
|
||||
final EditText etOne = view.findViewById(R.id.etOne);
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
|
||||
actvOne.setHint("请输入域名");
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(),
|
||||
android.R.layout.simple_dropdown_item_1line, hosts);
|
||||
actvOne.setAdapter(adapter);
|
||||
|
||||
etOne.setHint("请输入自定义ttl");
|
||||
|
||||
btnOne.setText("指定域名ttl s");
|
||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String host = actvOne.getEditableText().toString();
|
||||
int ttl = Integer.parseInt(etOne.getEditableText().toString());
|
||||
MyApp.getInstance().getCurrentHolder().setHostTtl(host, ttl);
|
||||
sendLog("指定域名" + host + "的ttl为" + ttl + "秒");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
addView(R.layout.item_autocomplete_edittext_button, view -> {
|
||||
final AutoCompleteTextView actvOne = view.findViewById(R.id.actvOne);
|
||||
final EditText etOne = view.findViewById(R.id.etOne);
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
|
||||
actvOne.setHint("域名");
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(),
|
||||
android.R.layout.simple_dropdown_item_1line, hosts);
|
||||
actvOne.setAdapter(adapter);
|
||||
|
||||
etOne.setHint("请输入端口");
|
||||
|
||||
btnOne.setText("添加ipRanking配置");
|
||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String host = actvOne.getEditableText().toString();
|
||||
int port = Integer.parseInt(etOne.getEditableText().toString());
|
||||
MyApp.getInstance().getCurrentHolder().addIpProbeItem(new IPRankingBean(host, port));
|
||||
sendLog("添加域名" + host + " 探测");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
addAutoCompleteTextViewButton(hosts, "主站域名", "添加主站域名", view -> {
|
||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||
String host = actvOne.getEditableText().toString();
|
||||
MyApp.getInstance().getCurrentHolder().addHostWithFixedIp(host);
|
||||
sendLog("添加主站域名" + host);
|
||||
});
|
||||
|
||||
addAutoCompleteTextViewButton(hosts, "域名", "删除指定域名的缓存", view -> {
|
||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||
String host = actvOne.getEditableText().toString();
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(host);
|
||||
MyApp.getInstance().getService().cleanHostCache(list);
|
||||
sendLog("清除指定host的缓存" + host);
|
||||
});
|
||||
|
||||
addOneButton("清除所有缓存", v -> {
|
||||
MyApp.getInstance().getService().cleanHostCache(null);
|
||||
sendLog("清除所有缓存");
|
||||
});
|
||||
|
||||
addFourButton("获取当前网络状态", v -> {
|
||||
NetType type = HttpDnsNetworkDetector.getInstance().getNetType(getApplicationContext());
|
||||
sendLog("获取网络状态 " + type.name());
|
||||
}, "禁用网络状态缓存", v -> {
|
||||
HttpDnsNetworkDetector.getInstance().disableCache(true);
|
||||
sendLog("网络状态 禁用缓存 ");
|
||||
}, "开启网络状态缓存", v -> {
|
||||
HttpDnsNetworkDetector.getInstance().disableCache(false);
|
||||
sendLog("网络状态 开启缓存 ");
|
||||
}, "清除网络状态缓存", v -> {
|
||||
HttpDnsNetworkDetector.getInstance().cleanCache(false);
|
||||
sendLog("网络状态清除缓存 ");
|
||||
});
|
||||
|
||||
addTwoButton("禁止读取IP", v -> {
|
||||
HttpDnsNetworkDetector.getInstance().setCheckInterface(false);
|
||||
sendLog("查询网络状态时 禁止读取IP");
|
||||
}, "允许读取IP", v -> {
|
||||
HttpDnsNetworkDetector.getInstance().setCheckInterface(true);
|
||||
sendLog("查询网络状态时 允许读取IP");
|
||||
});
|
||||
|
||||
addAutoCompleteTextViewButton(hosts, "域名", "设置检测网络使用的域名", view -> {
|
||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||
String host = actvOne.getEditableText().toString();
|
||||
HttpDnsNetworkDetector.getInstance().setHostToCheckNetType(host);
|
||||
sendLog("设置检测网络状态使用的域名为" + host);
|
||||
});
|
||||
|
||||
addTwoButton("模拟请求使用https请求", v -> {
|
||||
schema = SCHEMA_HTTPS;
|
||||
sendLog("测试url使用https");
|
||||
}, "模拟请求使用http请求", v -> {
|
||||
schema = SCHEMA_HTTP;
|
||||
sendLog("测试url使用http");
|
||||
});
|
||||
|
||||
addTwoButton("HttpUrlConnection", v -> {
|
||||
networkRequest = httpUrlConnectionRequest;
|
||||
sendLog("指定网络实现方式为HttpUrlConnection");
|
||||
}, "Okhttp", v -> {
|
||||
networkRequest = okHttpRequest;
|
||||
sendLog("指定网络实现方式为okhttp");
|
||||
});
|
||||
|
||||
|
||||
addFourButton("指定v4", v -> {
|
||||
requestIpType = RequestIpType.v4;
|
||||
sendLog("要解析的IP类型指定为ipv4");
|
||||
}, "指定v6", v -> {
|
||||
requestIpType = RequestIpType.v6;
|
||||
sendLog("要解析的IP类型指定为ipv6");
|
||||
}, "都解析", v -> {
|
||||
requestIpType = RequestIpType.both;
|
||||
sendLog("要解析的IP类型指定为ipv4和ipv6");
|
||||
}, "自动判断", v -> {
|
||||
requestIpType = RequestIpType.auto;
|
||||
sendLog("要解析的IP类型根据网络情况自动判断");
|
||||
});
|
||||
|
||||
addAutoCompleteTextViewButton(hosts, "域名", "指定要解析的域名", new OnButtonClick() {
|
||||
@Override
|
||||
public void onBtnClick(View view) {
|
||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||
host = actvOne.getEditableText().toString();
|
||||
sendLog("要解析的域名" + host);
|
||||
}
|
||||
});
|
||||
|
||||
addTwoButton("异步解析", v -> worker.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendLog("开始发起网络请求");
|
||||
sendLog("网络实现方式为" + (networkRequest == httpUrlConnectionRequest ? "HttpUrlConnection" : "okhttp"));
|
||||
String url = getUrl(schema, host);
|
||||
sendLog("url is " + url);
|
||||
sendLog("httpdns 使用 异步解析api");
|
||||
sendLog("指定解析ip类型为" + requestIpType.name());
|
||||
networkRequest.updateHttpDnsConfig(true, requestIpType);
|
||||
try {
|
||||
String response = networkRequest.httpGet(url);
|
||||
if (response != null && response.length() > 30) {
|
||||
response = response.trim();
|
||||
if (response.length() > 30) {
|
||||
response = response.substring(0, 30) + "...";
|
||||
}
|
||||
}
|
||||
sendLog("请求结束 response is " + response + " 完整记录请看logcat日志");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
sendLog("请求结束 发生异常 " + e.getClass().getName() + e.getMessage() + " 完整记录请看logcat日志");
|
||||
}
|
||||
|
||||
}
|
||||
}), "同步解析", v -> worker.execute(() -> {
|
||||
sendLog("开始发起网络请求");
|
||||
sendLog("网络实现方式为" + (networkRequest == httpUrlConnectionRequest ? "HttpUrlConnection" : "okhttp"));
|
||||
String url = getUrl(schema, host);
|
||||
sendLog("url is " + url);
|
||||
sendLog("httpdns 使用 同步解析api");
|
||||
sendLog("指定解析ip类型为" + requestIpType.name());
|
||||
networkRequest.updateHttpDnsConfig(false, requestIpType);
|
||||
try {
|
||||
String response = networkRequest.httpGet(url);
|
||||
if (response != null && response.length() > 30) {
|
||||
response = response.substring(0, 30) + "...";
|
||||
}
|
||||
sendLog("请求结束 response is " + response + " 完整记录请看logcat日志");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
sendLog("请求结束 发生异常 " + e.getClass().getName() + e.getMessage() + " 完整记录请看logcat日志");
|
||||
}
|
||||
}));
|
||||
|
||||
addThreeButton("发起预解析", v -> worker.execute(() -> {
|
||||
ArrayList<String> tmp = new ArrayList<>();
|
||||
Collections.addAll(tmp, hosts);
|
||||
MyApp.getInstance().getService().setPreResolveHosts(tmp, requestIpType);
|
||||
sendLog("已发起预解析请求");
|
||||
}), "跳转SDNS测试界面",
|
||||
v -> startActivity(new Intent(HttpDnsActivity.this, SDNSActivity.class)), "跳转Webview测试界面", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(HttpDnsActivity.this, WebViewActivity.class));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
final String[] validHosts = new String[]{
|
||||
"www.aliyun.com",
|
||||
"www.taobao.com"
|
||||
};
|
||||
|
||||
addTwoButton("并发异步请求", v -> {
|
||||
ThreadUtil.multiThreadTest(validHosts, 100, 20, 10 * 60 * 1000, true, requestIpType);
|
||||
sendLog("异步api并发测试开始,大约耗时10分钟,请最后查看logcat日志,确认结果,建议关闭httpdns日志,避免日志量过大");
|
||||
}, "并发同步请求", v -> {
|
||||
ThreadUtil.multiThreadTest(validHosts, 100, 20, 10 * 60 * 1000, false, requestIpType);
|
||||
sendLog("同步api并发测试开始,大约耗时10分钟,请最后查看logcat日志,确认结果,建议关闭httpdns日志,避免日志量过大");
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
package com.aliyun.ams.httpdns.demo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.CacheTtlChanger;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDns;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
import com.alibaba.sdk.android.httpdns.InitConfig;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingBean;
|
||||
import com.aliyun.ams.httpdns.demo.utils.SpUtil;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 保存Httpdns 及 相关配置,
|
||||
* 方便修改
|
||||
*/
|
||||
public class HttpDnsHolder {
|
||||
|
||||
public static final String SP_PREFIX = "httpdns_config_";
|
||||
|
||||
public static String getSpName(String accountId) {
|
||||
return SP_PREFIX + accountId;
|
||||
}
|
||||
|
||||
public static final String KEY_EXPIRED_IP = "enableExpiredIp";
|
||||
public static final String KEY_CACHE_IP = "enableCacheIp";
|
||||
public static final String KEY_TIMEOUT = "timeout";
|
||||
public static final String KEY_HTTPS = "enableHttps";
|
||||
public static final String KEY_IP_RANKING_ITEMS = "ipProbeItems";
|
||||
public static final String KEY_REGION = "region";
|
||||
public static final String KEY_TTL_CHANGER = "cacheTtlChanger";
|
||||
public static final String KEY_HOST_NOT_CHANGE = "hostListWithFixedIp";
|
||||
|
||||
private HttpDnsService service;
|
||||
private String accountId;
|
||||
private String secret;
|
||||
private Context context;
|
||||
|
||||
private boolean enableExpiredIp;
|
||||
private boolean enableCacheIp;
|
||||
private int timeout;
|
||||
private boolean enableHttps;
|
||||
private ArrayList<IPRankingBean> ipRankingList = null;
|
||||
private String region;
|
||||
private ArrayList<String> hostListWithFixedIp;
|
||||
private HashMap<String, Integer> ttlCache;
|
||||
private final CacheTtlChanger cacheTtlChanger = new CacheTtlChanger() {
|
||||
@Override
|
||||
public int changeCacheTtl(String host, RequestIpType type, int ttl) {
|
||||
if (ttlCache != null && ttlCache.get(host) != null) {
|
||||
return ttlCache.get(host);
|
||||
}
|
||||
return ttl;
|
||||
}
|
||||
};
|
||||
|
||||
public HttpDnsHolder(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public HttpDnsHolder(String accountId, String secret) {
|
||||
this.accountId = accountId;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化httpdns的配置
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public void init(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
SpUtil.readSp(context, getSpName(accountId), new SpUtil.OnGetSp() {
|
||||
@Override
|
||||
public void onGetSp(SharedPreferences sp) {
|
||||
enableExpiredIp = sp.getBoolean(KEY_EXPIRED_IP, true);
|
||||
enableCacheIp = sp.getBoolean(KEY_CACHE_IP, false);
|
||||
timeout = sp.getInt(KEY_TIMEOUT, 5 * 1000);
|
||||
enableHttps = sp.getBoolean(KEY_HTTPS, false);
|
||||
ipRankingList = convertToProbeList(sp.getString(KEY_IP_RANKING_ITEMS, null));
|
||||
region = sp.getString(KEY_REGION, null);
|
||||
ttlCache = convertToCacheTtlData(sp.getString(KEY_TTL_CHANGER, null));
|
||||
hostListWithFixedIp = convertToStringList(sp.getString(KEY_HOST_NOT_CHANGE, null));
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化httpdns 的配置,此步骤需要在第一次获取HttpDnsService实例之前
|
||||
new InitConfig.Builder()
|
||||
.setEnableExpiredIp(enableExpiredIp)
|
||||
.setEnableCacheIp(enableCacheIp)
|
||||
.setTimeout(timeout)
|
||||
.setEnableHttps(enableHttps)
|
||||
.setIPRankingList(ipRankingList)
|
||||
.setRegion(region)
|
||||
.configCacheTtlChanger(cacheTtlChanger)
|
||||
.configHostWithFixedIp(hostListWithFixedIp)
|
||||
.buildFor(accountId);
|
||||
|
||||
getService();
|
||||
}
|
||||
|
||||
public HttpDnsService getService() {
|
||||
if (service == null) {
|
||||
if (secret != null) {
|
||||
service = HttpDns.getService(context, accountId, secret);
|
||||
} else {
|
||||
service = HttpDns.getService(context, accountId);
|
||||
}
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setEnableExpiredIp(final boolean enableExpiredIp) {
|
||||
this.enableExpiredIp = enableExpiredIp;
|
||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putBoolean(KEY_EXPIRED_IP, enableExpiredIp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setEnableCacheIp(final boolean enableCacheIp) {
|
||||
this.enableCacheIp = enableCacheIp;
|
||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putBoolean(KEY_CACHE_IP, enableCacheIp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setTimeout(final int timeout) {
|
||||
this.timeout = timeout;
|
||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putInt(KEY_TIMEOUT, timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setEnableHttps(final boolean enableHttps) {
|
||||
this.enableHttps = enableHttps;
|
||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putBoolean(KEY_HTTPS, enableHttps);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setRegion(final String region) {
|
||||
this.region = region;
|
||||
getService().setRegion(region);
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putString(KEY_REGION, region);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addHostWithFixedIp(String host) {
|
||||
if (this.hostListWithFixedIp == null) {
|
||||
this.hostListWithFixedIp = new ArrayList<>();
|
||||
}
|
||||
this.hostListWithFixedIp.add(host);
|
||||
// 重启生效
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putString(KEY_HOST_NOT_CHANGE, convertHostList(hostListWithFixedIp));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addIpProbeItem(IPRankingBean ipProbeItem) {
|
||||
if (this.ipRankingList == null) {
|
||||
this.ipRankingList = new ArrayList<>();
|
||||
}
|
||||
this.ipRankingList.add(ipProbeItem);
|
||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putString(KEY_IP_RANKING_ITEMS, convertProbeList(ipRankingList));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void setHostTtl(String host, int ttl) {
|
||||
if (ttlCache == null) {
|
||||
ttlCache = new HashMap<>();
|
||||
}
|
||||
ttlCache.put(host, ttl);
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putString(KEY_TTL_CHANGER, convertTtlCache(ttlCache));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cleanSp() {
|
||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getCurrentConfig() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("当前配置 accountId : ").append(accountId).append("\n")
|
||||
.append("是否允许过期IP : ").append(enableExpiredIp).append("\n")
|
||||
.append("是否开启本地缓存 : ").append(enableCacheIp).append("\n")
|
||||
.append("是否开启HTTPS : ").append(enableHttps).append("\n")
|
||||
.append("当前region设置 : ").append(region).append("\n")
|
||||
.append("当前超时设置 : ").append(timeout).append("\n")
|
||||
.append("当前探测配置 : ").append(convertProbeList(ipRankingList)).append("\n")
|
||||
.append("当前缓存配置 : ").append(convertTtlCache(ttlCache)).append("\n")
|
||||
.append("当前主站域名配置 : ").append(convertHostList(hostListWithFixedIp)).append("\n")
|
||||
;
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private static String convertHostList(List<String> hostListWithFixedIp) {
|
||||
if (hostListWithFixedIp == null) {
|
||||
return null;
|
||||
}
|
||||
JSONArray array = new JSONArray();
|
||||
for (String host : hostListWithFixedIp) {
|
||||
array.put(host);
|
||||
}
|
||||
return array.toString();
|
||||
}
|
||||
|
||||
private static String convertTtlCache(HashMap<String, Integer> ttlCache) {
|
||||
if (ttlCache == null) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
for (String host : ttlCache.keySet()) {
|
||||
try {
|
||||
jsonObject.put(host, ttlCache.get(host));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
private static String convertProbeList(List<IPRankingBean> ipProbeItems) {
|
||||
if (ipProbeItems == null) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
for (IPRankingBean item : ipProbeItems) {
|
||||
try {
|
||||
jsonObject.put(item.getHostName(), item.getPort());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
private static ArrayList<IPRankingBean> convertToProbeList(String json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(json);
|
||||
ArrayList<IPRankingBean> list = new ArrayList<>();
|
||||
for (Iterator<String> it = jsonObject.keys(); it.hasNext(); ) {
|
||||
String host = it.next();
|
||||
list.add(new IPRankingBean(host, jsonObject.getInt(host)));
|
||||
}
|
||||
return list;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HashMap<String, Integer> convertToCacheTtlData(String json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(json);
|
||||
HashMap<String, Integer> map = new HashMap<>();
|
||||
for (Iterator<String> it = jsonObject.keys(); it.hasNext(); ) {
|
||||
String host = it.next();
|
||||
map.put(host, jsonObject.getInt(host));
|
||||
}
|
||||
return map;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ArrayList<String> convertToStringList(String json) {
|
||||
if (json != null) {
|
||||
try {
|
||||
JSONArray array = new JSONArray(json);
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
list.add(array.getString(i));
|
||||
}
|
||||
return list;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.aliyun.ams.httpdns.demo;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
import com.alibaba.sdk.android.httpdns.ILogger;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.aliyun.ams.httpdns.demo.utils.SpUtil;
|
||||
|
||||
public class MyApp extends Application {
|
||||
|
||||
private static final String SP_NAME = "HTTPDNS_DEMO";
|
||||
private static final String KEY_INSTANCE = "KEY_INSTANCE";
|
||||
private static final String VALUE_INSTANCE_A = "A";
|
||||
private static final String VALUE_INSTANCE_B = "B";
|
||||
|
||||
public static final String TAG = "HTTPDNS DEMO";
|
||||
private static MyApp instance;
|
||||
|
||||
public static MyApp getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final HttpDnsHolder holderA = new HttpDnsHolder("请替换为测试用A实例的accountId", "请替换为测试用A实例的secret");
|
||||
private final HttpDnsHolder holderB = new HttpDnsHolder("请替换为测试用B实例的accountId", null);
|
||||
|
||||
private HttpDnsHolder current = holderA;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
instance = this;
|
||||
|
||||
// 开启logcat 日志 默认关闭, 开发测试过程中可以开启
|
||||
HttpDnsLog.enable(true);
|
||||
// 注入日志接口,接受httpdns的日志,开发测试过程中可以开启, 基础日志需要先enable才生效,一些错误日志不需要
|
||||
HttpDnsLog.setLogger(new ILogger() {
|
||||
@Override
|
||||
public void log(String msg) {
|
||||
Log.d("HttpDnsLogger", msg);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化httpdns的配置
|
||||
holderA.init(this);
|
||||
holderB.init(this);
|
||||
|
||||
SpUtil.readSp(this, SP_NAME, new SpUtil.OnGetSp() {
|
||||
@Override
|
||||
public void onGetSp(SharedPreferences sp) {
|
||||
String flag = sp.getString(KEY_INSTANCE, VALUE_INSTANCE_A);
|
||||
if (flag.equals(VALUE_INSTANCE_A)) {
|
||||
current = holderA;
|
||||
} else {
|
||||
current = holderB;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public HttpDnsHolder getCurrentHolder() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public HttpDnsHolder changeHolder() {
|
||||
if (current == holderA) {
|
||||
current = holderB;
|
||||
SpUtil.writeSp(instance, SP_NAME, new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putString(KEY_INSTANCE, VALUE_INSTANCE_B);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
current = holderA;
|
||||
SpUtil.writeSp(instance, SP_NAME, new SpUtil.OnGetSpEditor() {
|
||||
@Override
|
||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||
editor.putString(KEY_INSTANCE, VALUE_INSTANCE_A);
|
||||
}
|
||||
});
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
public HttpDnsService getService() {
|
||||
return current.getService();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.aliyun.ams.httpdns.demo;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
|
||||
public interface NetworkRequest {
|
||||
|
||||
/**
|
||||
* 设置httpdns的配置
|
||||
*/
|
||||
void updateHttpDnsConfig(boolean async, RequestIpType requestIpType);
|
||||
|
||||
/**
|
||||
* get请求
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
String httpGet(String url) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.aliyun.ams.httpdns.demo;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.aliyun.ams.httpdns.demo.base.BaseActivity;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.aliyun.ams.httpdns.demo.HttpDnsActivity.ALIYUN_URL;
|
||||
|
||||
public class SDNSActivity extends BaseActivity {
|
||||
|
||||
private final HashMap<String, String> globalParams = new HashMap<>();
|
||||
/**
|
||||
* 要请求的域名
|
||||
*/
|
||||
private String host = HttpDnsActivity.ALIYUN_URL;
|
||||
/**
|
||||
* 要解析的ip类型
|
||||
*/
|
||||
private RequestIpType requestIpType = RequestIpType.v4;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addEditTextEditTextButton("key", "value", "添加全局配置", new OnButtonClickMoreView() {
|
||||
@Override
|
||||
public void onBtnClick(View[] views) {
|
||||
EditText etOne = (EditText) views[0];
|
||||
EditText etTwo = (EditText) views[1];
|
||||
|
||||
String key = etOne.getEditableText().toString();
|
||||
String value = etTwo.getEditableText().toString();
|
||||
|
||||
globalParams.put(key, value);
|
||||
|
||||
// 注意:SDNS全局参数现在需要通过InitConfig设置,重启应用生效
|
||||
sendLog("添加全局参数 " + key + " : " + value);
|
||||
}
|
||||
});
|
||||
|
||||
addOneButton("清除全局配置", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
globalParams.clear();
|
||||
// 注意:SDNS全局参数现在需要通过InitConfig设置,重启应用生效
|
||||
sendLog("清除全局参数");
|
||||
}
|
||||
});
|
||||
|
||||
addFourButton("指定v4", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
requestIpType = RequestIpType.v4;
|
||||
sendLog("要解析的IP类型指定为ipv4");
|
||||
}
|
||||
}, "指定v6", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
requestIpType = RequestIpType.v6;
|
||||
sendLog("要解析的IP类型指定为ipv6");
|
||||
}
|
||||
}, "都解析", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
requestIpType = RequestIpType.both;
|
||||
sendLog("要解析的IP类型指定为ipv4和ipv6");
|
||||
}
|
||||
}, "自动判断", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
requestIpType = RequestIpType.auto;
|
||||
sendLog("要解析的IP类型根据网络情况自动判断");
|
||||
}
|
||||
});
|
||||
|
||||
addAutoCompleteTextViewButton(HttpDnsActivity.hosts, "域名", "指定要解析的域名", new OnButtonClick() {
|
||||
@Override
|
||||
public void onBtnClick(View view) {
|
||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||
host = actvOne.getEditableText().toString();
|
||||
sendLog("要解析的域名" + host);
|
||||
}
|
||||
});
|
||||
|
||||
addEditTextEditTextButton("key", "value", "发起请求", new OnButtonClickMoreView() {
|
||||
@Override
|
||||
public void onBtnClick(View[] views) {
|
||||
EditText etOne = (EditText) views[0];
|
||||
EditText etTwo = (EditText) views[1];
|
||||
|
||||
String key = etOne.getEditableText().toString();
|
||||
String value = etTwo.getEditableText().toString();
|
||||
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
|
||||
map.put(key, value);
|
||||
|
||||
sendLog("发起SDNS请求 requestIpType is " + requestIpType.name());
|
||||
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "测试SDNS");
|
||||
sendLog("结果 " + result);
|
||||
}
|
||||
});
|
||||
|
||||
addTwoButton("scale1参数请求", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("scale", "scale1");
|
||||
sendLog("发起SDNS请求 requestIpType is " + requestIpType.name() + " scale : scale1");
|
||||
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "测试1");
|
||||
sendLog("结果 " + result);
|
||||
}
|
||||
}, "scale2参数请求", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("scale", "scale2");
|
||||
sendLog("发起SDNS请求 requestIpType is " + requestIpType.name() + " scale : scale2");
|
||||
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "测试2");
|
||||
sendLog("结果 " + result);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package com.aliyun.ams.httpdns.demo;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
public class WebViewActivity extends Activity {
|
||||
|
||||
private WebView webView;
|
||||
private static final String targetUrl = "http://www.apple.com";
|
||||
|
||||
private static final String TAG = MyApp.TAG + "WebView";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_webview);
|
||||
|
||||
initBar();
|
||||
initHttpDnsWebView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
|
||||
webView.goBack();//返回上个页面
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);//退出Activity
|
||||
}
|
||||
|
||||
private void initBar() {
|
||||
findViewById(R.id.bar_img).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
WebViewActivity.this.finish();
|
||||
}
|
||||
});
|
||||
|
||||
((TextView) findViewById(R.id.bar_text)).setText("HTTPDNS");
|
||||
}
|
||||
|
||||
private void initHttpDnsWebView() {
|
||||
|
||||
webView = (WebView) this.findViewById(R.id.wv_container);
|
||||
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
String scheme = request.getUrl().getScheme().trim();
|
||||
String method = request.getMethod();
|
||||
Map<String, String> headerFields = request.getRequestHeaders();
|
||||
String url = request.getUrl().toString();
|
||||
Log.e(TAG, "url:" + url);
|
||||
// 无法拦截body,拦截方案只能正常处理不带body的请求;
|
||||
if ((scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))
|
||||
&& method.equalsIgnoreCase("get")) {
|
||||
try {
|
||||
URLConnection connection = recursiveRequest(url, headerFields, null);
|
||||
|
||||
if (connection == null) {
|
||||
Log.e(TAG, "connection null");
|
||||
return super.shouldInterceptRequest(view, request);
|
||||
}
|
||||
|
||||
// 注*:对于POST请求的Body数据,WebResourceRequest接口中并没有提供,这里无法处理
|
||||
String contentType = connection.getContentType();
|
||||
String mime = getMime(contentType);
|
||||
String charset = getCharset(contentType);
|
||||
HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
|
||||
int statusCode = httpURLConnection.getResponseCode();
|
||||
String response = httpURLConnection.getResponseMessage();
|
||||
Map<String, List<String>> headers = httpURLConnection.getHeaderFields();
|
||||
Set<String> headerKeySet = headers.keySet();
|
||||
Log.e(TAG, "code:" + httpURLConnection.getResponseCode());
|
||||
Log.e(TAG, "mime:" + mime + "; charset:" + charset);
|
||||
|
||||
|
||||
// 无mime类型的请求不拦截
|
||||
if (TextUtils.isEmpty(mime)) {
|
||||
Log.e(TAG, "no MIME");
|
||||
return super.shouldInterceptRequest(view, request);
|
||||
} else {
|
||||
// 二进制资源无需编码信息
|
||||
if (!TextUtils.isEmpty(charset) || (isBinaryRes(mime))) {
|
||||
WebResourceResponse resourceResponse = new WebResourceResponse(mime, charset, httpURLConnection.getInputStream());
|
||||
resourceResponse.setStatusCodeAndReasonPhrase(statusCode, response);
|
||||
Map<String, String> responseHeader = new HashMap<String, String>();
|
||||
for (String key : headerKeySet) {
|
||||
// HttpUrlConnection可能包含key为null的报头,指向该http请求状态码
|
||||
responseHeader.put(key, httpURLConnection.getHeaderField(key));
|
||||
}
|
||||
resourceResponse.setResponseHeaders(responseHeader);
|
||||
return resourceResponse;
|
||||
} else {
|
||||
Log.e(TAG, "non binary resource for " + mime);
|
||||
return super.shouldInterceptRequest(view, request);
|
||||
}
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return super.shouldInterceptRequest(view, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
// API < 21 只能拦截URL参数
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
webView.loadUrl(targetUrl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从contentType中获取MIME类型
|
||||
*
|
||||
* @param contentType
|
||||
* @return
|
||||
*/
|
||||
private String getMime(String contentType) {
|
||||
if (contentType == null) {
|
||||
return null;
|
||||
}
|
||||
return contentType.split(";")[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从contentType中获取编码信息
|
||||
*
|
||||
* @param contentType
|
||||
* @return
|
||||
*/
|
||||
private String getCharset(String contentType) {
|
||||
if (contentType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] fields = contentType.split(";");
|
||||
if (fields.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String charset = fields[1];
|
||||
if (!charset.contains("=")) {
|
||||
return null;
|
||||
}
|
||||
charset = charset.substring(charset.indexOf("=") + 1);
|
||||
return charset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否是二进制资源,二进制资源可以不需要编码信息
|
||||
*/
|
||||
private boolean isBinaryRes(String mime) {
|
||||
if (mime.startsWith("image")
|
||||
|| mime.startsWith("audio")
|
||||
|| mime.startsWith("video")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public URLConnection recursiveRequest(String path, Map<String, String> headers, String reffer) {
|
||||
HttpURLConnection conn;
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(path);
|
||||
// 异步接口获取IP
|
||||
String ip = MyApp.getInstance().getService().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);
|
||||
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, String> field : headers.entrySet()) {
|
||||
conn.setRequestProperty(field.getKey(), field.getValue());
|
||||
}
|
||||
}
|
||||
// 设置HTTP请求头Host域
|
||||
conn.setRequestProperty("Host", url.getHost());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
conn.setConnectTimeout(30000);
|
||||
conn.setReadTimeout(30000);
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
final HttpsURLConnection httpsURLConnection = (HttpsURLConnection) conn;
|
||||
WebviewTlsSniSocketFactory sslSocketFactory = new WebviewTlsSniSocketFactory(
|
||||
(HttpsURLConnection)conn);
|
||||
|
||||
// sni场景,创建SSLScocket
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
int code = conn.getResponseCode();// Network block
|
||||
if (needRedirect(code)) {
|
||||
// 原有报头中含有cookie,放弃拦截
|
||||
if (containCookie(headers)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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 recursiveRequest(location, headers, path);
|
||||
} else {
|
||||
// 无法获取location信息,让浏览器获取
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// redirect finish.
|
||||
Log.e(TAG, "redirect finish");
|
||||
return conn;
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
Log.w(TAG, "recursiveRequest MalformedURLException");
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "recursiveRequest IOException");
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "unknow exception");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private boolean needRedirect(int code) {
|
||||
return code >= 300 && code < 400;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* header中是否含有cookie
|
||||
*/
|
||||
private boolean containCookie(Map<String, String> headers) {
|
||||
for (Map.Entry<String, String> headerField : headers.entrySet()) {
|
||||
if (headerField.getKey().contains("Cookie")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static class WebviewTlsSniSocketFactory extends SSLSocketFactory {
|
||||
private final String TAG = WebviewTlsSniSocketFactory.class.getSimpleName();
|
||||
HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
private final HttpsURLConnection conn;
|
||||
|
||||
public WebviewTlsSniSocketFactory(HttpsURLConnection conn) {
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TLS layer
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException {
|
||||
String peerHost = this.conn.getRequestProperty("Host");
|
||||
if (peerHost == null)
|
||||
peerHost = host;
|
||||
Log.i(TAG, "customized createSocket. host: " + peerHost);
|
||||
InetAddress address = plainSocket.getInetAddress();
|
||||
if (autoClose) {
|
||||
// we don't need the plainSocket
|
||||
plainSocket.close();
|
||||
}
|
||||
// create and connect SSL socket, but don't do hostname/certificate verification yet
|
||||
SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);
|
||||
SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(address, port);
|
||||
|
||||
// enable TLSv1.1/1.2 if available
|
||||
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
|
||||
|
||||
// set up SNI before the handshake
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
Log.i(TAG, "Setting SNI hostname");
|
||||
sslSocketFactory.setHostname(ssl, peerHost);
|
||||
} else {
|
||||
Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
|
||||
try {
|
||||
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
|
||||
setHostnameMethod.invoke(ssl, peerHost);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "SNI not useable", e);
|
||||
}
|
||||
}
|
||||
|
||||
// verify hostname and certificate
|
||||
SSLSession session = ssl.getSession();
|
||||
|
||||
if (!hostnameVerifier.verify(peerHost, session))
|
||||
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + peerHost);
|
||||
|
||||
Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
|
||||
" using " + session.getCipherSuite());
|
||||
|
||||
return ssl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.aliyun.ams.httpdns.demo.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.aliyun.ams.httpdns.demo.MyApp;
|
||||
import com.aliyun.ams.httpdns.demo.R;
|
||||
|
||||
public class BaseActivity extends Activity {
|
||||
|
||||
public static final int MSG_WHAT_LOG = 10000;
|
||||
|
||||
private ScrollView logScrollView;
|
||||
private TextView logView;
|
||||
private LinearLayout llContainer;
|
||||
|
||||
private Handler handler;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_base);
|
||||
|
||||
handler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
switch (msg.what) {
|
||||
case MSG_WHAT_LOG:
|
||||
logView.setText(logView.getText() + "\n" + (String) msg.obj);
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logScrollView.fullScroll(View.FOCUS_DOWN);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
logScrollView = findViewById(R.id.logScrollView);
|
||||
logView = findViewById(R.id.tvConsoleText);
|
||||
llContainer = findViewById(R.id.llContainer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
handler = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送日志到界面
|
||||
*
|
||||
* @param log
|
||||
*/
|
||||
protected void sendLog(String log) {
|
||||
Log.d(MyApp.TAG, log);
|
||||
if (handler != null) {
|
||||
Message msg = handler.obtainMessage(MSG_WHAT_LOG, log);
|
||||
handler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected void cleanLog() {
|
||||
logView.setText("");
|
||||
}
|
||||
|
||||
protected void addView(int layoutId, OnViewCreated created) {
|
||||
FrameLayout container = new FrameLayout(this);
|
||||
View.inflate(this, layoutId, container);
|
||||
llContainer.addView(container, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
created.onViewCreated(container);
|
||||
}
|
||||
|
||||
protected void addOneButton(
|
||||
final String labelOne, final View.OnClickListener clickListenerOne
|
||||
) {
|
||||
addView(R.layout.item_one_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
btnOne.setOnClickListener(clickListenerOne);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void addTwoButton(
|
||||
final String labelOne, final View.OnClickListener clickListenerOne,
|
||||
final String labelTwo, final View.OnClickListener clickListenerTwo
|
||||
) {
|
||||
addView(R.layout.item_two_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
btnOne.setOnClickListener(clickListenerOne);
|
||||
|
||||
Button btnTwo = view.findViewById(R.id.btnTwo);
|
||||
btnTwo.setText(labelTwo);
|
||||
btnTwo.setOnClickListener(clickListenerTwo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void addThreeButton(
|
||||
final String labelOne, final View.OnClickListener clickListenerOne,
|
||||
final String labelTwo, final View.OnClickListener clickListenerTwo,
|
||||
final String labelThree, final View.OnClickListener clickListenerThree
|
||||
) {
|
||||
addView(R.layout.item_three_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
btnOne.setOnClickListener(clickListenerOne);
|
||||
|
||||
Button btnTwo = view.findViewById(R.id.btnTwo);
|
||||
btnTwo.setText(labelTwo);
|
||||
btnTwo.setOnClickListener(clickListenerTwo);
|
||||
|
||||
Button btnThree = view.findViewById(R.id.btnThree);
|
||||
btnThree.setText(labelThree);
|
||||
btnThree.setOnClickListener(clickListenerThree);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected void addFourButton(
|
||||
final String labelOne, final View.OnClickListener clickListenerOne,
|
||||
final String labelTwo, final View.OnClickListener clickListenerTwo,
|
||||
final String labelThree, final View.OnClickListener clickListenerThree,
|
||||
final String labelFour, final View.OnClickListener clickListenerFour
|
||||
) {
|
||||
addView(R.layout.item_four_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
btnOne.setOnClickListener(clickListenerOne);
|
||||
|
||||
Button btnTwo = view.findViewById(R.id.btnTwo);
|
||||
btnTwo.setText(labelTwo);
|
||||
btnTwo.setOnClickListener(clickListenerTwo);
|
||||
|
||||
Button btnThree = view.findViewById(R.id.btnThree);
|
||||
btnThree.setText(labelThree);
|
||||
btnThree.setOnClickListener(clickListenerThree);
|
||||
|
||||
Button btnFour = view.findViewById(R.id.btnFour);
|
||||
btnFour.setText(labelFour);
|
||||
btnFour.setOnClickListener(clickListenerFour);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void addEditTextButton(
|
||||
final String hint,
|
||||
final String labelOne, final OnButtonClick clickListenerOne
|
||||
) {
|
||||
addView(R.layout.item_edit_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
final EditText editText = view.findViewById(R.id.etOne);
|
||||
editText.setHint(hint);
|
||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickListenerOne.onBtnClick(editText);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected void addEditTextEditTextButton(
|
||||
final String hintOne, final String hintTwo,
|
||||
final String labelOne, final OnButtonClickMoreView clickListenerOne
|
||||
) {
|
||||
addView(R.layout.item_edit_edit_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
final EditText editTextOne = view.findViewById(R.id.etOne);
|
||||
editTextOne.setHint(hintOne);
|
||||
final EditText editTextTwo = view.findViewById(R.id.etTwo);
|
||||
editTextTwo.setHint(hintTwo);
|
||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickListenerOne.onBtnClick(new View[]{editTextOne, editTextTwo});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void addAutoCompleteTextViewButton(
|
||||
final String[] strings, final String hint, final String labelOne, final OnButtonClick clickListenerOne
|
||||
) {
|
||||
addView(R.layout.item_autocomplete_button, new OnViewCreated() {
|
||||
@Override
|
||||
public void onViewCreated(View view) {
|
||||
|
||||
Button btnOne = view.findViewById(R.id.btnOne);
|
||||
btnOne.setText(labelOne);
|
||||
|
||||
final AutoCompleteTextView actvOne = view.findViewById(R.id.actvOne);
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(),
|
||||
android.R.layout.simple_dropdown_item_1line, strings);
|
||||
actvOne.setAdapter(adapter);
|
||||
actvOne.setHint(hint);
|
||||
|
||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickListenerOne.onBtnClick(actvOne);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnViewCreated {
|
||||
void onViewCreated(View view);
|
||||
}
|
||||
|
||||
public interface OnButtonClick {
|
||||
void onBtnClick(View view);
|
||||
}
|
||||
|
||||
public interface OnButtonClickMoreView {
|
||||
void onBtnClick(View[] views);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
package com.aliyun.ams.httpdns.demo.http;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.NetType;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.SyncService;
|
||||
import com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector;
|
||||
import com.aliyun.ams.httpdns.demo.MyApp;
|
||||
import com.aliyun.ams.httpdns.demo.NetworkRequest;
|
||||
import com.aliyun.ams.httpdns.demo.utils.Util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* 使用HttpUrlConnection 实现请求
|
||||
*/
|
||||
public class HttpUrlConnectionRequest implements NetworkRequest {
|
||||
|
||||
public static final String TAG = MyApp.TAG + "HttpUrl";
|
||||
|
||||
private final Context context;
|
||||
private boolean async;
|
||||
private RequestIpType type;
|
||||
|
||||
public HttpUrlConnectionRequest(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHttpDnsConfig(boolean async, RequestIpType requestIpType) {
|
||||
this.async = async;
|
||||
this.type = requestIpType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String httpGet(String url) throws Exception {
|
||||
Log.d(TAG, "使用httpUrlConnection 请求" + url + " 异步接口 " + async + " ip类型 " + type.name());
|
||||
|
||||
HttpURLConnection conn = getConnection(url);
|
||||
InputStream in = null;
|
||||
BufferedReader streamReader = null;
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||
in = conn.getErrorStream();
|
||||
String errStr = null;
|
||||
if (in != null) {
|
||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
errStr = readStringFrom(streamReader).toString();
|
||||
}
|
||||
Log.d(TAG, "请求失败 " + conn.getResponseCode() + " err " + errStr);
|
||||
throw new Exception("Status Code : " + conn.getResponseCode() + " Msg : " + errStr);
|
||||
} else {
|
||||
in = conn.getInputStream();
|
||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String responseStr = readStringFrom(streamReader).toString();
|
||||
Log.d(TAG, "请求成功 " + responseStr);
|
||||
return responseStr;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpURLConnection getConnection(String url) throws IOException {
|
||||
final String host = new URL(url).getHost();
|
||||
HttpURLConnection conn = null;
|
||||
HTTPDNSResult result;
|
||||
/* 切换为新版标准api */
|
||||
if (async) {
|
||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostAsync(host, type);
|
||||
} else {
|
||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostSync(host, type);
|
||||
}
|
||||
Log.d(TAG, "httpdns 解析 " + host + " 结果为 " + result + " ttl is " + Util.getTtl(result));
|
||||
|
||||
// 这里需要根据实际情况选择使用ipv6地址 还是 ipv4地址, 下面示例的代码优先使用了ipv6地址
|
||||
if (result.getIpv6s() != null && result.getIpv6s().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v4) {
|
||||
String newUrl = url.replace(host, "[" + result.getIpv6s()[0] + "]");
|
||||
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||
conn.setRequestProperty("Host", host);
|
||||
Log.d(TAG, "使用ipv6地址 " + newUrl);
|
||||
} else if (result.getIps() != null && result.getIps().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v6) {
|
||||
String newUrl = url.replace(host, result.getIps()[0]);
|
||||
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||
conn.setRequestProperty("Host", host);
|
||||
Log.d(TAG, "使用ipv4地址 " + newUrl);
|
||||
}
|
||||
|
||||
if (conn == null) {
|
||||
Log.d(TAG, "httpdns 未返回解析结果,走localdns");
|
||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
}
|
||||
conn.setConnectTimeout(30000);
|
||||
conn.setReadTimeout(30000);
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
final HttpsURLConnection httpsURLConnection = (HttpsURLConnection) conn;
|
||||
WebviewTlsSniSocketFactory sslSocketFactory = new WebviewTlsSniSocketFactory(
|
||||
(HttpsURLConnection)conn);
|
||||
|
||||
// sni场景,创建SSLSocket
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
int code = conn.getResponseCode();// Network block
|
||||
if (needRedirect(code)) {
|
||||
//临时重定向和永久重定向location的大小写有区分
|
||||
String location = conn.getHeaderField("Location");
|
||||
if (location == null) {
|
||||
location = conn.getHeaderField("location");
|
||||
}
|
||||
if (!(location.startsWith("http://") || location
|
||||
.startsWith("https://"))) {
|
||||
//某些时候会省略host,只返回后面的path,所以需要补全url
|
||||
URL originalUrl = new URL(url);
|
||||
location = originalUrl.getProtocol() + "://"
|
||||
+ originalUrl.getHost() + location;
|
||||
}
|
||||
return getConnection(location);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
private boolean needRedirect(int code) {
|
||||
return code >= 300 && code < 400;
|
||||
}
|
||||
|
||||
static class WebviewTlsSniSocketFactory extends SSLSocketFactory {
|
||||
private final String TAG = WebviewTlsSniSocketFactory.class.getSimpleName();
|
||||
HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
private HttpsURLConnection conn;
|
||||
|
||||
public WebviewTlsSniSocketFactory(HttpsURLConnection conn) {
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TLS layer
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException {
|
||||
String peerHost = this.conn.getRequestProperty("Host");
|
||||
if (peerHost == null)
|
||||
peerHost = host;
|
||||
Log.i(TAG, "customized createSocket. host: " + peerHost);
|
||||
InetAddress address = plainSocket.getInetAddress();
|
||||
if (autoClose) {
|
||||
// we don't need the plainSocket
|
||||
plainSocket.close();
|
||||
}
|
||||
// create and connect SSL socket, but don't do hostname/certificate verification yet
|
||||
SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);
|
||||
SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(address, port);
|
||||
|
||||
// enable TLSv1.1/1.2 if available
|
||||
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
|
||||
|
||||
// set up SNI before the handshake
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
Log.i(TAG, "Setting SNI hostname");
|
||||
sslSocketFactory.setHostname(ssl, peerHost);
|
||||
} else {
|
||||
Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
|
||||
try {
|
||||
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
|
||||
setHostnameMethod.invoke(ssl, peerHost);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "SNI not useable", e);
|
||||
}
|
||||
}
|
||||
|
||||
// verify hostname and certificate
|
||||
SSLSession session = ssl.getSession();
|
||||
|
||||
if (!hostnameVerifier.verify(peerHost, session))
|
||||
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + peerHost);
|
||||
|
||||
Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
|
||||
" using " + session.getCipherSuite());
|
||||
|
||||
return ssl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stream to string
|
||||
*/
|
||||
public static StringBuilder readStringFrom(BufferedReader streamReader) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = streamReader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.aliyun.ams.httpdns.demo.okhttp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.NetType;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.SyncService;
|
||||
import com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector;
|
||||
import com.aliyun.ams.httpdns.demo.MyApp;
|
||||
import com.aliyun.ams.httpdns.demo.NetworkRequest;
|
||||
import com.aliyun.ams.httpdns.demo.utils.Util;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.Dns;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* okhttp实现网络请求
|
||||
*/
|
||||
public class OkHttpRequest implements NetworkRequest {
|
||||
|
||||
public static final String TAG = MyApp.TAG + "Okhttp";
|
||||
private final OkHttpClient client;
|
||||
|
||||
private boolean async;
|
||||
private RequestIpType type;
|
||||
|
||||
public OkHttpRequest(final Context context) {
|
||||
client = new OkHttpClient.Builder()
|
||||
// 这里配置连接池,是为了方便测试httpdns能力,正式代码请不要配置
|
||||
.connectionPool(new ConnectionPool(0, 10 * 1000, TimeUnit.MICROSECONDS))
|
||||
.dns(new Dns() {
|
||||
@Override
|
||||
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
|
||||
HTTPDNSResult result;
|
||||
/* 切换为新版标准api */
|
||||
if (async) {
|
||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostAsync(hostname, type);
|
||||
} else {
|
||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostSync(hostname, type);
|
||||
}
|
||||
Log.d(TAG, "httpdns 解析 " + hostname + " 结果为 " + result + " ttl is " + Util.getTtl(result));
|
||||
List<InetAddress> inetAddresses = new ArrayList<>();
|
||||
// 这里需要根据实际情况选择使用ipv6地址 还是 ipv4地址, 下面示例的代码优先使用了ipv6地址
|
||||
Log.d(TAG, "netType is: " + HttpDnsNetworkDetector.getInstance().getNetType(context));
|
||||
if (result.getIpv6s() != null && result.getIpv6s().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v4) {
|
||||
for (int i = 0; i < result.getIpv6s().length; i++) {
|
||||
inetAddresses.addAll(Arrays.asList(InetAddress.getAllByName(result.getIpv6s()[i])));
|
||||
}
|
||||
Log.d(TAG, "使用ipv6地址" + inetAddresses);
|
||||
} else if (result.getIps() != null && result.getIps().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v6) {
|
||||
for (int i = 0; i < result.getIps().length; i++) {
|
||||
inetAddresses.addAll(Arrays.asList(InetAddress.getAllByName(result.getIps()[i])));
|
||||
}
|
||||
Log.d(TAG, "使用ipv4地址" + inetAddresses);
|
||||
}
|
||||
if (inetAddresses.size() == 0) {
|
||||
Log.d(TAG, "httpdns 未返回IP,走localdns");
|
||||
return Dns.SYSTEM.lookup(hostname);
|
||||
}
|
||||
return inetAddresses;
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHttpDnsConfig(boolean async, RequestIpType requestIpType) {
|
||||
this.async = async;
|
||||
this.type = requestIpType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String httpGet(String url) throws Exception {
|
||||
Log.d(TAG, "使用okhttp 请求" + url + " 异步接口 " + async + " ip类型 " + type.name());
|
||||
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
|
||||
int code = response.code();
|
||||
String body = response.body().string();
|
||||
Log.d(TAG, "使用okhttp 请求结果 code " + code + " body " + body);
|
||||
if (code != HttpURLConnection.HTTP_OK) {
|
||||
throw new Exception("请求失败 code " + code + " body " + body);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.aliyun.ams.httpdns.demo.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class SpUtil {
|
||||
|
||||
public static void readSp(Context context, String name, OnGetSp onGetSp) {
|
||||
SharedPreferences sp = context.getSharedPreferences(name, Context.MODE_PRIVATE);
|
||||
onGetSp.onGetSp(sp);
|
||||
}
|
||||
|
||||
public static void writeSp(Context context, String name, OnGetSpEditor onGetSpEditor) {
|
||||
SharedPreferences sp = context.getSharedPreferences(name, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
onGetSpEditor.onGetSpEditor(editor);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public interface OnGetSp {
|
||||
void onGetSp(SharedPreferences sp);
|
||||
}
|
||||
|
||||
public interface OnGetSpEditor {
|
||||
void onGetSpEditor(SharedPreferences.Editor editor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.aliyun.ams.httpdns.demo.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.SyncService;
|
||||
|
||||
import com.aliyun.ams.httpdns.demo.MyApp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class ThreadUtil {
|
||||
|
||||
public static void multiThreadTest(final String[] validHosts, final int hostCount,
|
||||
final int threadCount, final int executeTime,
|
||||
final boolean async, final RequestIpType type) {
|
||||
int validCount = validHosts.length;
|
||||
if (validCount > hostCount) {
|
||||
validCount = hostCount;
|
||||
}
|
||||
final int tmpValidCount = validCount;
|
||||
Log.d(MyApp.TAG,
|
||||
threadCount + "线程并发,执行" + executeTime + ", 总域名" + hostCount + "个,有效"
|
||||
+ validCount + "个");
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final ArrayList<String> hosts = new ArrayList<>(hostCount);
|
||||
for (int i = 0; i < hostCount - tmpValidCount; i++) {
|
||||
hosts.add("test" + i + ".aliyun.com");
|
||||
}
|
||||
hosts.addAll(Arrays.asList(validHosts).subList(0, tmpValidCount));
|
||||
|
||||
final CountDownLatch testLatch = new CountDownLatch(threadCount);
|
||||
ExecutorService service = Executors.newFixedThreadPool(threadCount);
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
service.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
countDownLatch.countDown();
|
||||
try {
|
||||
countDownLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Random random = new Random(Thread.currentThread().getId());
|
||||
int allRequestCount = 0;
|
||||
int slowRequestCount = 0;
|
||||
int emptyResponseCount = 0;
|
||||
long maxSlowRequestTime = 0;
|
||||
long subSlowRequestTime = 0;
|
||||
int taobaoCount = 0;
|
||||
int taobaoSuccessCount = 0;
|
||||
long firstTaobaoTime = 0;
|
||||
long firstTaobaoSuccessTime = 0;
|
||||
long begin = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() - begin < executeTime) {
|
||||
String host = hosts.get(random.nextInt(hostCount));
|
||||
long startRequestTime = System.currentTimeMillis();
|
||||
HTTPDNSResult ips = null;
|
||||
if (async) {
|
||||
ips = MyApp.getInstance().getService().getIpsByHostAsync(host,
|
||||
type);
|
||||
} else {
|
||||
ips =
|
||||
((SyncService)MyApp.getInstance().getService()).getByHost(
|
||||
host, type);
|
||||
}
|
||||
long endRequestTime = System.currentTimeMillis();
|
||||
if (host.equals("www.taobao.com")) {
|
||||
if (taobaoCount == 0) {
|
||||
firstTaobaoTime = System.currentTimeMillis();
|
||||
}
|
||||
taobaoCount++;
|
||||
if (ips.getIps() != null && ips.getIps().length > 0) {
|
||||
if (taobaoSuccessCount == 0) {
|
||||
firstTaobaoSuccessTime = System.currentTimeMillis();
|
||||
}
|
||||
taobaoSuccessCount++;
|
||||
}
|
||||
}
|
||||
if (endRequestTime - startRequestTime > 100) {
|
||||
slowRequestCount++;
|
||||
subSlowRequestTime += endRequestTime - startRequestTime;
|
||||
if (maxSlowRequestTime < endRequestTime - startRequestTime) {
|
||||
maxSlowRequestTime = endRequestTime - startRequestTime;
|
||||
}
|
||||
}
|
||||
if (ips == null || ips.getIps() == null
|
||||
|| ips.getIps().length == 0) {
|
||||
emptyResponseCount++;
|
||||
}
|
||||
allRequestCount++;
|
||||
}
|
||||
|
||||
String msg = Thread.currentThread().getId() + " allRequestCount: "
|
||||
+ allRequestCount + ", slowRequestCount: " + slowRequestCount
|
||||
+ ", emptyResponseCount: " + emptyResponseCount
|
||||
+ ", maxSlowRequestTime : " + maxSlowRequestTime
|
||||
+ ", avgSlowRequestTime: " + (slowRequestCount == 0 ? 0
|
||||
: subSlowRequestTime / slowRequestCount)
|
||||
+ ", taoRequestCount: " + taobaoCount + ", "
|
||||
+ "taoSuccessRequestCount: "
|
||||
+ taobaoSuccessCount + ", firstTaoRequestTime: " + (firstTaobaoTime
|
||||
- begin) + ", firstSuccessTaoRequestTime: " + (
|
||||
firstTaobaoSuccessTime - begin);
|
||||
Log.w(MyApp.TAG, "asyncMulti " + msg);
|
||||
testLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
testLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.aliyun.ams.httpdns.demo.utils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class Util {
|
||||
/**
|
||||
* 获取ttl,
|
||||
* 此方法是用于测试自定义ttl是否生效
|
||||
*/
|
||||
public static int getTtl(HTTPDNSResult result) {
|
||||
try {
|
||||
Field ttlField = HTTPDNSResult.class.getDeclaredField("ttl");
|
||||
ttlField.setAccessible(true);
|
||||
return ttlField.getInt(result);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
@@ -0,0 +1,49 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/aliyun_bg" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/logScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvConsoleText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp" />
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#EAEAEA">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#ffffff"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:background="#f2f2f2">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/bar_img"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@mipmap/back"
|
||||
android:padding="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bar_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text=""
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bar_more"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="更多"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<WebView
|
||||
android:id="@+id/wv_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/actvOne"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/actvOne"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etOne"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/etOne" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/etOne" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/etTwo" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnTwo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnThree"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnFour"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnTwo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnThree"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnTwo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
BIN
EdgeHttpDNS/sdk/android/app/src/main/res/mipmap-hdpi/back.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
31
EdgeHttpDNS/sdk/android/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<resources>
|
||||
<string name="app_name">【阿里云HttpDns】Demo程序</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="normal_parse">普通解析</string>
|
||||
<string name="request_taobao">解析淘宝域名</string>
|
||||
<string name="request_apple">解析apple域名</string>
|
||||
<string name="request_douban">解析豆瓣域名</string>
|
||||
<string name="https_parse">HTTPS开关</string>
|
||||
<string name="timeout">设置超时</string>
|
||||
<string name="set_expired">允许过期域名</string>
|
||||
<string name="set_cache">开启持久化缓存</string>
|
||||
<string name="set_degration_filter">降级策略</string>
|
||||
<string name="set_pre_resolve">预解析</string>
|
||||
<string name="set_region">region</string>
|
||||
<string name="sync_request">同步解析</string>
|
||||
<string name="multi_sync_request">同步解析并发</string>
|
||||
<string name="multi_request">并发解析</string>
|
||||
|
||||
<string name="main_about_us">关于我们</string>
|
||||
<string name="main_helper">帮助中心</string>
|
||||
<string name="main_clear_text">清除当前消息</string>
|
||||
|
||||
<!--关于我们页面-->
|
||||
<string name="layout_aboutus_arr">All Rights Reserved.</string>
|
||||
<string name="layout_aboutus_company">阿里云(软件)有限公司版权所有</string>
|
||||
<string name="layout_aboutus_copyright">Copyright © 2009 - 2016 Aliyun.com</string>
|
||||
<string name="layout_aboutus_app_version">1.1.3</string>
|
||||
<!--帮助中心页面-->
|
||||
<string name="layout_helpus_content">Q : 什么是用户体验Demo?\nA : 用户体验Demo就是阿里云平台为您自动创建的、用来体验HttpDns服务和反馈建议用的一个小Demo,让您体验便捷、高效的HttpDns服务。\n\nQ : 如何联系我们?\nA : App Demo相关问题,请搜索钉钉群号:11777313</string>
|
||||
|
||||
</resources>
|
||||
14
EdgeHttpDNS/sdk/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme"></style>
|
||||
|
||||
<style name="button_allgrade_content">
|
||||
<item name="android:layout_margin">1dip</item>
|
||||
<item name="android:background">#ffffff</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:textColor">#413d41</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||