init mogo httpdns sdk
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
package com.mogo.cloud.httpdns
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Message
|
||||
import android.util.ArrayMap
|
||||
import com.mogo.cloud.httpdns.listener.IMogoHttpDns
|
||||
import com.mogo.cloud.httpdns.listener.OnAddressChangedListener
|
||||
import com.mogo.cloud.httpdns.util.ApiManager
|
||||
import com.mogo.cloud.httpdns.util.L
|
||||
import com.mogo.cloud.httpdns.util.NetWorkUtil
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* 1. 本地每15min查询一次
|
||||
* 2. 网络状态发生变化时候,重置15min轮询,并查询一次
|
||||
*
|
||||
* @author tongchenfei
|
||||
*/
|
||||
internal class HttpDnsHelper(private val builder: MogoHttpDnsConfig) : Handler.Callback,IMogoHttpDns {
|
||||
companion object {
|
||||
const val HTTP_DNS_TYPE_HTTP = 0
|
||||
const val HTTP_DNS_TYPE_WS = 1
|
||||
const val HTTP_DNS_TYPE_IM = 2
|
||||
|
||||
private const val MSG_REQUEST_IP_PORT = 1001
|
||||
|
||||
private const val TAG = "HttpDnsHelper"
|
||||
}
|
||||
|
||||
private val netWorkUtil = NetWorkUtil()
|
||||
private var localConnectStateCache = false
|
||||
private val workThread = HandlerThread("mogo-http-dns-work-thread")
|
||||
private val handler: Handler
|
||||
private var addressMap: Map<String, String>? = null
|
||||
|
||||
var addressChangedListener: OnAddressChangedListener? = null
|
||||
|
||||
private val apiManager: ApiManager
|
||||
private var isInit = false
|
||||
|
||||
private var defaultUrl: String? = null
|
||||
|
||||
init {
|
||||
L.isDebug = builder.showDebugLog()
|
||||
defaultUrl = builder.getDefaultUrl()
|
||||
L.d(TAG, "init===")
|
||||
checkParams()
|
||||
isInit = true
|
||||
L.d(TAG, "params available, prepare to start check net status")
|
||||
workThread.start()
|
||||
handler = Handler(workThread.looper, this)
|
||||
apiManager = ApiManager(builder.getSn()!!, builder.getEnv(), builder.getAppKey())
|
||||
handler.sendEmptyMessageDelayed(MSG_REQUEST_IP_PORT, builder.getLoopCheckDelay())
|
||||
netWorkUtil.registerStatusCallback(builder.getContext()!!) {
|
||||
if (it && !localConnectStateCache) {
|
||||
// 网络状态可用,且本地记录的连接状态为false,开始请求
|
||||
handler.removeMessages(MSG_REQUEST_IP_PORT)
|
||||
// 立即执行
|
||||
handler.sendEmptyMessage(MSG_REQUEST_IP_PORT)
|
||||
}
|
||||
localConnectStateCache = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHttpDnsAddressFromNet() {
|
||||
|
||||
val nAddress = apiManager.requestHttpDns(builder.getCurrentLocation()!!)
|
||||
if (addressChangedListener == null) {
|
||||
L.d(TAG, "addressChangeList is null")
|
||||
}
|
||||
if (addressMap == null) {
|
||||
addressMap = nAddress
|
||||
addressChangedListener?.onAddressChanged(addressMap)
|
||||
} else if (nAddress != null) {
|
||||
addressChangedListener?.onAddressChanged(mapDiff(nAddress, addressMap!!))
|
||||
addressMap = nAddress
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile
|
||||
private var isRequest = false
|
||||
|
||||
private val requestCache = ArrayList<String>()
|
||||
|
||||
private val requestLock = Any()
|
||||
|
||||
override fun getHttpDnsAddress(type: Int, _host: String): String? {
|
||||
val host = _host.toLowerCase(Locale.getDefault())
|
||||
if (isInit) {
|
||||
requestCache.add("$type-$host")
|
||||
synchronized(requestLock) {
|
||||
L.d(TAG, "getHttpDnsAddress: $type-$host \n thread: ${Thread.currentThread().name} isRequest: $isRequest")
|
||||
if (!isRequest) {
|
||||
isRequest = true
|
||||
L.d(TAG, "prepare to get http dns from net thread: ${Thread.currentThread().name}")
|
||||
getHttpDnsAddressFromNet()
|
||||
}
|
||||
}
|
||||
addressMap?.let {
|
||||
val builder = StringBuilder()
|
||||
for (key in it.keys) {
|
||||
builder.append(key).append(" : ").append(it[key]).append("\n")
|
||||
}
|
||||
L.d(TAG, "getHttpDnsAddress over $type-$host ${builder.toString()}")
|
||||
requestCache.remove("$type-$host")
|
||||
L.d(TAG, "requestCache.size: ${requestCache.size}")
|
||||
if (requestCache.isEmpty()) {
|
||||
isRequest = false
|
||||
}
|
||||
return if (it["$type-$host"] == null) {
|
||||
defaultUrl ?: host
|
||||
} else {
|
||||
it["$type-$host"]
|
||||
}
|
||||
}
|
||||
L.d(TAG, "getHttpDnsAddress over addressMap is null")
|
||||
} else {
|
||||
L.d(TAG, "not init over")
|
||||
throw IllegalStateException("Http dns not init")
|
||||
|
||||
}
|
||||
return defaultUrl ?: host
|
||||
}
|
||||
|
||||
override fun getHttpDnsCachedAddress(type: Int, _host: String): String? {
|
||||
val host = _host.toLowerCase(Locale.getDefault())
|
||||
L.d(TAG, "getHttpDnsCachedAddress: $type-$host")
|
||||
return addressMap?.get("$type-$host")
|
||||
}
|
||||
|
||||
override fun getAllAddress(): Map<String, String>? {
|
||||
return addressMap
|
||||
}
|
||||
|
||||
override fun handleMessage(msg: Message): Boolean {
|
||||
if (msg.what == MSG_REQUEST_IP_PORT) {
|
||||
L.d(TAG, "http dns loop check")
|
||||
if (isInit) {
|
||||
// 网络接口请求
|
||||
getHttpDnsAddressFromNet()
|
||||
handler.sendEmptyMessageDelayed(MSG_REQUEST_IP_PORT, builder.getLoopCheckDelay())
|
||||
} else {
|
||||
L.d(TAG, "not init over")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
netWorkUtil.release()
|
||||
}
|
||||
|
||||
private fun mapDiff(new: Map<String, String>, old: Map<String, String>): Map<String, String> {
|
||||
val diff = ArrayMap<String, String>()
|
||||
for (key in new.keys) {
|
||||
if (old[key] == null) {
|
||||
diff[key] = new[key]
|
||||
} else if (old[key] != new[key]) {
|
||||
diff[key] = new[key]
|
||||
}
|
||||
}
|
||||
for (key in old.keys) {
|
||||
if (!new.containsKey(key)) {
|
||||
diff[key] = null
|
||||
}
|
||||
}
|
||||
for (i in diff.keys) {
|
||||
L.d(TAG, "diff key: $i")
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
private fun checkParams(): Boolean {
|
||||
if (builder.getContext() == null) {
|
||||
L.e(TAG, "no context")
|
||||
throw IllegalArgumentException("can not find context in builder")
|
||||
}
|
||||
if (builder.getSn() == null) {
|
||||
L.e(TAG, "no sn")
|
||||
throw IllegalArgumentException("can not find sn in builder")
|
||||
}
|
||||
if (builder.getCurrentLocation() == null) {
|
||||
L.e(TAG, "no current location")
|
||||
throw IllegalArgumentException("can not find currentLocation in builder")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.mogo.cloud.httpdns
|
||||
|
||||
import com.mogo.cloud.httpdns.listener.IMogoHttpDns
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
|
||||
object MogoHttpDnsClient : IMogoHttpDns {
|
||||
private var httpDnsHelper:HttpDnsHelper? = null
|
||||
|
||||
fun init(config: MogoHttpDnsConfig) {
|
||||
if(httpDnsHelper == null) {
|
||||
httpDnsHelper = HttpDnsHelper(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 先从本地缓存中根据type和host获取ip:port,如果本地缓存中没有,再通过网络获取
|
||||
*/
|
||||
fun getHttpDnsAddressUseCacheIfNecessary(type: Int, _host: String):String?{
|
||||
return getHttpDnsCachedAddress(type, _host) ?: return getHttpDnsAddress(type, _host)
|
||||
}
|
||||
|
||||
override fun getHttpDnsAddress(type: Int, _host: String): String? {
|
||||
if (httpDnsHelper == null) {
|
||||
throw IllegalStateException("MogoHttpDnsClient init error")
|
||||
}
|
||||
return httpDnsHelper!!.getHttpDnsAddress(type,_host)
|
||||
}
|
||||
|
||||
override fun getHttpDnsCachedAddress(type: Int, _host: String): String? {
|
||||
if (httpDnsHelper == null) {
|
||||
throw IllegalStateException("MogoHttpDnsClient init error")
|
||||
}
|
||||
return httpDnsHelper!!.getHttpDnsCachedAddress(type, _host)
|
||||
}
|
||||
|
||||
override fun getAllAddress(): Map<String, String>? {
|
||||
if (httpDnsHelper == null) {
|
||||
throw IllegalStateException("MogoHttpDnsClient init error")
|
||||
}
|
||||
return httpDnsHelper!!.getAllAddress()
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
if (httpDnsHelper == null) {
|
||||
throw IllegalStateException("MogoHttpDnsClient init error")
|
||||
}
|
||||
httpDnsHelper!!.release()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.mogo.cloud.httpdns
|
||||
|
||||
import android.content.Context
|
||||
import com.mogo.cloud.httpdns.listener.IHttpDnsCurrentLocation
|
||||
|
||||
class MogoHttpDnsConfig {
|
||||
|
||||
companion object{
|
||||
/**
|
||||
* 研发环境
|
||||
*/
|
||||
const val HTTP_DNS_ENV_DEV = 1
|
||||
|
||||
/**
|
||||
* 测试环境
|
||||
*/
|
||||
const val HTTP_DNS_ENV_QA = 2
|
||||
|
||||
/**
|
||||
* 演示环境
|
||||
*/
|
||||
const val HTTP_DNS_ENV_DEMO = 4
|
||||
|
||||
/**
|
||||
* 线上环境
|
||||
*/
|
||||
const val HTTP_DNS_ENV_RELEASE = 3
|
||||
}
|
||||
private var mSn: String? = null
|
||||
private var mCurrentLocation: IHttpDnsCurrentLocation? = null
|
||||
private var mShowDebugLog: Boolean = false
|
||||
private var mLoopCheckDelay: Long = 15 * 60 * 1000
|
||||
private var mContext: Context? = null
|
||||
private var mEnv = HTTP_DNS_ENV_RELEASE
|
||||
private var mDefaultUrl:String? = null
|
||||
|
||||
private var mAppKey:String? = null
|
||||
|
||||
fun setContext(context: Context): MogoHttpDnsConfig {
|
||||
mContext = context
|
||||
return this
|
||||
}
|
||||
|
||||
fun getContext(): Context? {
|
||||
return mContext
|
||||
}
|
||||
|
||||
fun setSn(sn: String): MogoHttpDnsConfig {
|
||||
mSn = sn
|
||||
return this
|
||||
}
|
||||
|
||||
fun getSn(): String? {
|
||||
return mSn
|
||||
}
|
||||
|
||||
fun setShowDebugLog(showDebugLog: Boolean): MogoHttpDnsConfig {
|
||||
mShowDebugLog = showDebugLog
|
||||
return this
|
||||
}
|
||||
|
||||
fun showDebugLog(): Boolean {
|
||||
return mShowDebugLog
|
||||
}
|
||||
|
||||
fun setLoopCheckDelay(loopCheckDelay: Long): MogoHttpDnsConfig {
|
||||
mLoopCheckDelay = loopCheckDelay
|
||||
return this
|
||||
}
|
||||
|
||||
fun getLoopCheckDelay(): Long {
|
||||
return mLoopCheckDelay
|
||||
}
|
||||
|
||||
fun setCurrentLocation(currentLocation: IHttpDnsCurrentLocation): MogoHttpDnsConfig {
|
||||
mCurrentLocation = currentLocation
|
||||
return this
|
||||
}
|
||||
|
||||
fun getCurrentLocation(): IHttpDnsCurrentLocation? {
|
||||
return mCurrentLocation
|
||||
}
|
||||
|
||||
fun setEnv(env:Int):MogoHttpDnsConfig{
|
||||
mEnv = env
|
||||
return this
|
||||
}
|
||||
|
||||
fun getEnv():Int{
|
||||
return mEnv
|
||||
}
|
||||
|
||||
fun setDefaultUrl(defaultUrl:String):MogoHttpDnsConfig{
|
||||
mDefaultUrl = defaultUrl
|
||||
return this
|
||||
}
|
||||
|
||||
fun getDefaultUrl():String?{
|
||||
return mDefaultUrl
|
||||
}
|
||||
|
||||
fun setAppKey(appKey: String): MogoHttpDnsConfig {
|
||||
mAppKey = appKey
|
||||
return this
|
||||
}
|
||||
|
||||
fun getAppKey():String{
|
||||
return mAppKey ?: ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mogo.cloud.httpdns.bean
|
||||
|
||||
/**
|
||||
* httpDns需要的定位信息,需要使用高德定位的经纬度坐标和cityCode
|
||||
*
|
||||
* @author tongchenfei
|
||||
*/
|
||||
data class HttpDnsSimpleLocation(val cityCode:String,val lat:Double,val lon:Double)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mogo.cloud.httpdns.listener
|
||||
|
||||
import com.mogo.cloud.httpdns.bean.HttpDnsSimpleLocation
|
||||
|
||||
interface IHttpDnsCurrentLocation {
|
||||
fun getCurrentLocation(): HttpDnsSimpleLocation?
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mogo.cloud.httpdns.listener
|
||||
|
||||
import com.mogo.cloud.httpdns.HttpDnsHelper.Companion.HTTP_DNS_TYPE_HTTP
|
||||
|
||||
interface IMogoHttpDns {
|
||||
/**
|
||||
* 根据类型和host获取IP,直接通过网络请求获取全部路由表
|
||||
* 同时多线程多次请求会忽略部分网络请求,一定程度减少接口请求次数
|
||||
*
|
||||
* @param type [HTTP_DNS_TYPE_HTTP]用于请求http接口
|
||||
* [HTTP_DNS_TYPE_WS]用于webSocket长连接
|
||||
* 若增加新类型,可自行设置
|
||||
* @return 类型对应的ip:port,不带协议头以及path,请自行补齐
|
||||
* 特别注意,若此type没有对应的ip:port,本方法会返回null
|
||||
*/
|
||||
fun getHttpDnsAddress(type: Int, _host: String): String?
|
||||
|
||||
/**
|
||||
* 根据类型和host获取本地cache的路由表
|
||||
*
|
||||
* @param type [HTTP_DNS_TYPE_HTTP]用于请求http接口
|
||||
* [HTTP_DNS_TYPE_WS]用于webSocket长连接
|
||||
* 若增加新类型,可自行设置
|
||||
*
|
||||
* @return 类型对应的ip:port,不带协议头以及path,请自行补齐
|
||||
* 特别注意,若此type没有对应的ip:port,本方法会返回null
|
||||
*/
|
||||
fun getHttpDnsCachedAddress(type: Int, _host: String): String?
|
||||
|
||||
/**
|
||||
* 获取全部路由表
|
||||
*
|
||||
* @return 返回本地缓存全部路由表,如果没有则返回null
|
||||
*/
|
||||
fun getAllAddress(): Map<String, String>?
|
||||
|
||||
fun release()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.mogo.cloud.httpdns.listener
|
||||
|
||||
interface OnAddressChangedListener {
|
||||
fun onAddressChanged(address: Map<String, String>?)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.mogo.cloud.httpdns.util
|
||||
|
||||
import android.util.ArrayMap
|
||||
import com.mogo.cloud.httpdns.listener.IHttpDnsCurrentLocation
|
||||
import org.json.JSONObject
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
|
||||
private const val TAG = "ApiManager"
|
||||
|
||||
/**
|
||||
* 接口请求管理类
|
||||
*
|
||||
* @author tongchenfei
|
||||
*/
|
||||
class ApiManager(private val sn:String,private val env:Int,private val appKey:String) {
|
||||
|
||||
fun requestHttpDns(_location: IHttpDnsCurrentLocation):Map<String,String>? {
|
||||
val location = _location.getCurrentLocation() ?: return null
|
||||
var client: HttpURLConnection? = null
|
||||
var bufferedReader: BufferedReader? = null
|
||||
try {
|
||||
client =
|
||||
URL("http://dzt.zhidaozhixing.com/yycp-httpdns-service/api/httpDns/getIpAndPort").openConnection() as HttpURLConnection
|
||||
client.requestMethod = "POST"
|
||||
client.doInput = true
|
||||
client.doOutput = true
|
||||
client.useCaches = false
|
||||
client.setRequestProperty("Content-type", "application/json")
|
||||
client.setRequestProperty("Charset", "UTF-8")
|
||||
client.setRequestProperty("app-key", appKey)
|
||||
client.readTimeout = 1000
|
||||
client.connectTimeout = 1000
|
||||
client.connect()
|
||||
val params =
|
||||
"{\"sn\":\"$sn\",\"cityCode\":\"${location.cityCode}\",\"lat\":${location.lat},\"lon\":${location.lon},\"env\":$env}"
|
||||
L.d(TAG, "params: $params")
|
||||
client.outputStream.write(params.toByteArray())
|
||||
client.outputStream.flush()
|
||||
client.outputStream.close()
|
||||
L.d(TAG, "response: ${client.responseCode}")
|
||||
bufferedReader = BufferedReader(InputStreamReader(client.inputStream))
|
||||
val builder = StringBuilder()
|
||||
while (true) {
|
||||
val line = bufferedReader.readLine()
|
||||
if (line != null) {
|
||||
builder.append(line)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
val msg = builder.toString()
|
||||
bufferedReader.close()
|
||||
client.disconnect()
|
||||
L.d(TAG, msg)
|
||||
val json = JSONObject(msg)
|
||||
val jsonArray = json.optJSONObject("result")?.optJSONArray("urls")
|
||||
jsonArray?.apply {
|
||||
val map = ArrayMap<String, String>(length())
|
||||
for (i in 0 until length()) {
|
||||
val item = optJSONObject(i)
|
||||
val type = item.optInt("type")
|
||||
val url = item.optString("url")
|
||||
val host = item.optString("domain").toLowerCase(Locale.getDefault())
|
||||
|
||||
map["$type-$host"] = url
|
||||
}
|
||||
return map
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
L.e(TAG, e.message ?: "exception===")
|
||||
bufferedReader?.close()
|
||||
client?.disconnect()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.mogo.cloud.httpdns.util
|
||||
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* 简单实现日志工具类
|
||||
*
|
||||
* @author tongchenfei
|
||||
*/
|
||||
object L {
|
||||
var isDebug = true
|
||||
fun d(tag: String, msg: String) {
|
||||
if (isDebug) {
|
||||
Log.d(tag, msg)
|
||||
// println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
fun e(tag: String, msg: String) {
|
||||
Log.e(tag, msg)
|
||||
// println(msg)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.mogo.cloud.httpdns.util
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.ConnectivityManager
|
||||
|
||||
private const val TAG = "NetWorkUtil"
|
||||
|
||||
/**
|
||||
* 网络状态工具类
|
||||
* 监听和获取网络状态
|
||||
*
|
||||
* @author tongchenfei
|
||||
*/
|
||||
class NetWorkUtil {
|
||||
private var statusCallback: ((Boolean) -> Unit)? = null
|
||||
private var connectivityManager: ConnectivityManager? = null
|
||||
|
||||
private var context: Context? = null
|
||||
|
||||
fun registerStatusCallback(context: Context, statusCallback: (Boolean) -> Unit) {
|
||||
this.statusCallback = statusCallback
|
||||
this.context = context
|
||||
connectivityManager =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val intentFilter = IntentFilter()
|
||||
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE")
|
||||
context.registerReceiver(receiver, intentFilter)
|
||||
}
|
||||
|
||||
|
||||
private val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
connectivityManager?.apply {
|
||||
if (activeNetworkInfo == null) {
|
||||
L.d(TAG, "activityNetworkInfo is null")
|
||||
statusCallback?.invoke(false)
|
||||
} else {
|
||||
if (activeNetworkInfo!!.type == -1) {
|
||||
L.d(TAG, "activityNetworkInfo.type == -1")
|
||||
statusCallback?.invoke(false)
|
||||
} else {
|
||||
L.d(TAG, "activityNetworkInfo.type: ${activeNetworkInfo!!.type}")
|
||||
statusCallback?.invoke(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun release() {
|
||||
context?.unregisterReceiver(receiver)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user