init mogo httpdns sdk

This commit is contained in:
tongchenfei
2021-01-19 19:38:46 +08:00
parent d5075235a7
commit 37a607d5df
26 changed files with 880 additions and 1 deletions

View File

@@ -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
}
}

View File

@@ -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()
}
}

View File

@@ -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 ?: ""
}
}

View File

@@ -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)

View File

@@ -0,0 +1,7 @@
package com.mogo.cloud.httpdns.listener
import com.mogo.cloud.httpdns.bean.HttpDnsSimpleLocation
interface IHttpDnsCurrentLocation {
fun getCurrentLocation(): HttpDnsSimpleLocation?
}

View File

@@ -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()
}

View File

@@ -0,0 +1,5 @@
package com.mogo.cloud.httpdns.listener
interface OnAddressChangedListener {
fun onAddressChanged(address: Map<String, String>?)
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}