diff --git a/guru_analytics/CHANGELOG.md b/guru_analytics/CHANGELOG.md index d653056..1aa4ae5 100644 --- a/guru_analytics/CHANGELOG.md +++ b/guru_analytics/CHANGELOG.md @@ -1,3 +1,39 @@ +## v1.0.3 + +##### BugFix +- bugfix snapshotAnalyticsAudit uploaded > total + +## v1.0.2 + +##### Feature +- init step callback +- snapshotAnalyticsAudit() + +##### BugFix +- bugfix http exception + +## v1.0.1 + +##### Feature +- init add uploadIpAddress +- CronetHelper init try catch + +## v1.0.0 + +##### Feature +- CustomDns add cache +- init add isEnableCronet +- UserProperty guru_anm cronet/default +- PendingEvent 初始化成功前添加的埋点时间戳校正 +- initialize uploadEventBaseUrl fromat check + +## v0.3.2 + +##### Feature +- add fun peakUserProperties +- add fun getUserProperties +- add fun setEnableUpload + ## v0.3.1 ##### Feature diff --git a/guru_analytics/app/src/main/java/com/example/guruanalytics/MainActivity.kt b/guru_analytics/app/src/main/java/com/example/guruanalytics/MainActivity.kt index fa026db..4641835 100644 --- a/guru_analytics/app/src/main/java/com/example/guruanalytics/MainActivity.kt +++ b/guru_analytics/app/src/main/java/com/example/guruanalytics/MainActivity.kt @@ -7,9 +7,11 @@ import android.widget.TextView import guru.core.analytics.GuruAnalytics import guru.core.analytics.data.db.model.EventPriority import guru.core.analytics.data.model.AnalyticsOptions -import guru.core.analytics.handler.AnalyticsCode class MainActivity : AppCompatActivity() { + + private var enableUpload = true + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -36,6 +38,8 @@ class MainActivity : AppCompatActivity() { .setXAppId("test_x_app_id") .setXDeviceInfo("test_x_device_info") .setMainProcess("com.example.guruanalytics") + .isEnableCronet(true) + .setUploadIpAddress(listOf("3.210.96.186", "34.196.69.199")) .build() findViewById(R.id.tvLogEvent).setOnClickListener { @@ -76,9 +80,31 @@ class MainActivity : AppCompatActivity() { GuruAnalytics.INSTANCE.setUploadEventBaseUrl(this, "https://www.castbox.fm/") } + val tvEnable = findViewById(R.id.tvEnable) + tvEnable?.setOnClickListener { + val enable = !enableUpload + GuruAnalytics.INSTANCE.setEnableUpload(enable) + enableUpload = enable + tvEnable.text = if (enableUpload) "Enable Upload" else "Disable Upload" + } + GuruAnalytics.INSTANCE.setScreen("main") GuruAnalytics.INSTANCE.setAdId("AD_ID_01") GuruAnalytics.INSTANCE.setUserProperty("uid", "110051") + GuruAnalytics.INSTANCE.setUserProperty("age", "12") + GuruAnalytics.INSTANCE.setUserProperty("sex", "male") + + val properties = GuruAnalytics.INSTANCE.peakUserProperties() + Log.i("peakUserProperties", "properties:$properties") + + GuruAnalytics.INSTANCE.getUserProperties { + Log.i("getUserProperties", "properties:$it") + } + + tvEnable?.postDelayed({ + val snapshot = GuruAnalytics.INSTANCE.snapshotAnalyticsAudit() + Log.i("snapshotAnalyticsAudit", "snapshot:$snapshot") + }, 10_000) } private val eventHandler: (Int, String?) -> Unit = { code, ext -> diff --git a/guru_analytics/app/src/main/res/layout/activity_main.xml b/guru_analytics/app/src/main/res/layout/activity_main.xml index 82ccbf3..d7f0fca 100644 --- a/guru_analytics/app/src/main/res/layout/activity_main.xml +++ b/guru_analytics/app/src/main/res/layout/activity_main.xml @@ -70,4 +70,13 @@ android:text="Update BaseUrl" android:layout_gravity="center_horizontal" /> + + \ No newline at end of file diff --git a/guru_analytics/guru_analytics/build.gradle b/guru_analytics/guru_analytics/build.gradle index 6c89681..8713ac8 100644 --- a/guru_analytics/guru_analytics/build.gradle +++ b/guru_analytics/guru_analytics/build.gradle @@ -53,7 +53,9 @@ dependencies { implementation okhttpDependencies - implementation process + implementation processDependencies implementation workerDependencies + + implementation cronetDependencies } \ No newline at end of file diff --git a/guru_analytics/guru_analytics/dependencies.gradle b/guru_analytics/guru_analytics/dependencies.gradle index 35cffd3..eed5c13 100644 --- a/guru_analytics/guru_analytics/dependencies.gradle +++ b/guru_analytics/guru_analytics/dependencies.gradle @@ -21,6 +21,8 @@ ext { preferenceVersion = '1.2.0' processVersion = '2.4.0' workVersion = '2.7.1' + cronetOkhttpVersion = '0.1.0' + playServicesCronetVersion = '18.0.1' kaptDependencies = [ "androidx.room:room-compiler:$roomVersion", @@ -54,7 +56,12 @@ ext { "androidx.work:work-rxjava2:$workVersion" ] - process = [ + processDependencies = [ "androidx.lifecycle:lifecycle-process:$processVersion" ] + + cronetDependencies = [ + "com.google.net.cronet:cronet-okhttp:$cronetOkhttpVersion", + "com.google.android.gms:play-services-cronet:$playServicesCronetVersion" + ] } \ No newline at end of file diff --git a/guru_analytics/guru_analytics/maven-publish.gradle b/guru_analytics/guru_analytics/maven-publish.gradle index a306d6d..a6e084e 100644 --- a/guru_analytics/guru_analytics/maven-publish.gradle +++ b/guru_analytics/guru_analytics/maven-publish.gradle @@ -18,7 +18,7 @@ publishing { // Repositories *to* which Gradle can publish artifacts maven(MavenPublication) { groupId 'guru.core.analytics' artifactId 'guru_analytics' - version '0.3.1' // Your package version + version '1.0.3' // Your package version // artifact publishArtifact //Example: *./target/myJavaClasses.jar* // artifact "build/outputs/aar/aar-test-release.aar"//aar包的目录 afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) } diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/Constants.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/Constants.kt index f9cfbb1..ccf7187 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/Constants.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/Constants.kt @@ -56,6 +56,12 @@ object Constants { const val COIN = "coin" const val EXP = "exp" const val HP = "hp" + const val GURU_ANM = "guru_anm" + } + + object AnmState { + const val CRONET = "cronet" + const val DEFAULT = "default" } object DeviceType { diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/GuruAnalytics.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/GuruAnalytics.kt index 874092f..9b4ceae 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/GuruAnalytics.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/GuruAnalytics.kt @@ -4,7 +4,6 @@ import android.content.Context import guru.core.analytics.data.db.model.EventStatistic import guru.core.analytics.data.model.AnalyticsInfo import guru.core.analytics.data.model.AnalyticsOptions -import guru.core.analytics.handler.AnalyticsCode import guru.core.analytics.impl.GuruAnalyticsImpl import java.io.File @@ -21,13 +20,15 @@ abstract class GuruAnalytics { eventExpiredInDays: Int? = 7, debug: Boolean = false, persistableLog: Boolean = true, - listener: ((Int, String?) -> Unit)? = null, + eventHandlerCallback: ((Int, String?) -> Unit)? = null, isInitPeriodicWork: Boolean = false, uploadEventBaseUrl: String? = null, fgEventPeriodInSeconds: Long? = null, xAppId: String? = null, xDeviceInfo: String? = null, mainProcess: String? = null, + isEnableCronet: Boolean? = null, + uploadIpAddress: List? = null, ) abstract fun setUploadEventBaseUrl(context: Context, updateEventBaseUrl: String) @@ -71,6 +72,17 @@ abstract class GuruAnalytics { abstract fun removeUserProperties(keys: Set) + abstract fun getUserProperties(callback: (Map) -> Unit) + + /** + * setUserProperty后立即获取, 可能无法获取到最新设置的值 + */ + abstract fun peakUserProperties(): Map + + abstract fun setEnableUpload(enable: Boolean) + + abstract fun snapshotAnalyticsAudit(): String + companion object { val INSTANCE: GuruAnalytics by lazy() { GuruAnalyticsImpl() @@ -116,6 +128,12 @@ abstract class GuruAnalytics { fun setMainProcess(process: String) = apply { analyticsInfo.mainProcess = process } + fun isEnableCronet(isEnableCronet: Boolean) = + apply { analyticsInfo.isEnableCronet = isEnableCronet } + + fun setUploadIpAddress(uploadIpAddress: List) = + apply { analyticsInfo.uploadIpAddress = uploadIpAddress } + fun build(): GuruAnalytics { analyticsInfo.run { INSTANCE.initialize( @@ -133,6 +151,8 @@ abstract class GuruAnalytics { xAppId, xDeviceInfo, mainProcess, + isEnableCronet, + uploadIpAddress, ) } return INSTANCE diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/ServiceLocator.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/ServiceLocator.kt index ff0bdde..864599e 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/ServiceLocator.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/ServiceLocator.kt @@ -1,20 +1,28 @@ package guru.core.analytics.data.api import android.content.Context +import android.net.Uri import android.os.SystemClock -import guru.core.analytics.data.api.logging.LoggingInterceptor +import guru.core.analytics.data.api.cronet.CastboxCronetInterceptor import guru.core.analytics.data.api.dns.CustomDns import guru.core.analytics.data.api.dns.GoogleDnsApi import guru.core.analytics.data.api.dns.GoogleDnsApiHost import guru.core.analytics.data.api.logging.Level +import guru.core.analytics.data.api.logging.LoggingInterceptor import guru.core.analytics.data.local.PreferencesManager import guru.core.analytics.handler.AnalyticsCode import guru.core.analytics.handler.EventHandler import guru.core.analytics.utils.AndroidUtils import guru.core.analytics.utils.DateTimeUtils import guru.core.analytics.utils.GsonUtil -import okhttp3.* +import okhttp3.Dispatcher +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Protocol +import okhttp3.Request import okhttp3.Response +import okhttp3.ResponseBody import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.internal.platform.Platform import retrofit2.Converter @@ -35,6 +43,8 @@ object ServiceLocator { private val headerParams = mutableMapOf() + private var uploadIpAddress: List? = null + fun addHeaderParam(key: String, value: String?) { if (value.isNullOrBlank()) return headerParams[key] = value @@ -44,20 +54,24 @@ object ServiceLocator { this.debug = debug } - fun provideGuruRepository(context: Context, baseUrl: String? = null): GuruRepository { + fun setUploadIpAddress(ipList: List?) { + uploadIpAddress = ipList + } + + fun provideGuruRepository(context: Context, baseUri: Uri? = null): GuruRepository { synchronized(this) { return guruRepository - ?: createQuotesRepository(context, baseUrl).apply { guruRepository = this } + ?: createQuotesRepository(context, baseUri).apply { guruRepository = this } } } - private fun createQuotesRepository(context: Context, baseUrl: String? = null): GuruRepository { - return DefaultGuruRepository(provideAnalyticsApi(context, baseUrl)) + private fun createQuotesRepository(context: Context, baseUri: Uri? = null): GuruRepository { + return DefaultGuruRepository(provideAnalyticsApi(context, baseUri)) } - private fun provideAnalyticsApi(context: Context, baseUrl: String? = null): AnalyticsApi { - val finalBaseUrl = if (!baseUrl.isNullOrEmpty()) { - baseUrl + private fun provideAnalyticsApi(context: Context, baseUri: Uri? = null): AnalyticsApi { + val finalBaseUrl = if (baseUri != null) { + baseUri.toString() } else { val cacheBaseUrl = PreferencesManager.getInstance(context).uploadEventBaseUrl if (cacheBaseUrl.isNullOrEmpty()) AnalyticsApiHost.BASE_URL else cacheBaseUrl @@ -114,20 +128,22 @@ object ServiceLocator { maxRequests = 128 maxRequestsPerHost = 10 }) - .dns(CustomDns(context)) + .dns(CustomDns(context, uploadIpAddress)) .connectTimeout(20L, TimeUnit.SECONDS) .readTimeout(readTimeOut, TimeUnit.SECONDS) .writeTimeout(writeTimeOut, TimeUnit.SECONDS) .addInterceptor(createCacheControlInterceptor(context)) .addInterceptor(createAnalyticsApiInterceptor()) .addInterceptor(createLoggingInterceptor()) + .addInterceptor(createCronetInterceptor()) return builder.build() } private fun createDnsOkHttpClient(context: Context): OkHttpClient { val builder = OkHttpClient.Builder() .addInterceptor(createCacheControlInterceptor(context)) - builder.addInterceptor(createLoggingInterceptor()) + .addInterceptor(createLoggingInterceptor()) + .addInterceptor(createCronetInterceptor()) return builder.build() } @@ -140,6 +156,14 @@ object ServiceLocator { .build() } + /** + * Add the Cronet interceptor last, otherwise the subsequent interceptors will be skipped. + * https://github.com/google/cronet-transport-for-okhttp + */ + private fun createCronetInterceptor(): Interceptor { + return CastboxCronetInterceptor() + } + private fun createCacheControlInterceptor(context: Context) = Interceptor { chain -> try { val originalResponse = chain.proceed(chain.request()) @@ -162,10 +186,7 @@ object ServiceLocator { } } catch (e: Exception) { EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_CACHE_CONTROL, e.message) - return@Interceptor exceptionResponse( - e, - chain.request() - ) + throw e } } @@ -174,7 +195,6 @@ object ServiceLocator { val request = chain.request() val startTime = SystemClock.elapsedRealtime() val builder = request.newBuilder() - .addHeader(CONTENT_TYPE, "application/json") .addHeader(CONTENT_ENCODING, "gzip") .addHeader(X_EVENT_TYPE, "event") headerParams.forEach { @@ -182,17 +202,13 @@ object ServiceLocator { } val newRequest = builder.build() try { - val response = chain.proceed(newRequest) - val responseTime = (SystemClock.elapsedRealtime() - startTime) / 2 - calibrationTime(responseTime, response.headers) - if (response.isSuccessful) { - successResponse(response) - } else { - httpErrorResponse(response) + chain.proceed(newRequest).also { response -> + val responseTime = (SystemClock.elapsedRealtime() - startTime) / 2 + calibrationTime(responseTime, response.headers) } } catch (e: Exception) { EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_API, e.message) - return@Interceptor exceptionResponse(e, newRequest) + throw e } } } @@ -205,40 +221,6 @@ object ServiceLocator { DateTimeUtils.initServerTime(date.time + responseTime) minResponseTime = responseTime } - - private fun successResponse(response: Response): Response { - val body: ResponseBody? - var bodyString: String? - response.body.let { - bodyString = it?.string() - body = bodyString?.toResponseBody(it!!.contentType()) - } - return response.newBuilder() - .body(body) - .build() - } - - private fun httpErrorResponse(response: Response): Response { - val body = - "{\"code\": ${response.code}, \"msg\": \"${response.message}\", \"data\": null}".toResponseBody() - response.close() - EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_RESPONSE, response.code.toString()) - return response.newBuilder() - .code(1) - .body(body) - .build() - } - - private fun exceptionResponse(e: Exception, request: Request): Response { - val content = "{\"code\": -1, \"msg\": \"${e.message}\", \"data\": null}" - return Response.Builder() - .code(1) - .request(request) - .protocol(Protocol.HTTP_1_1) - .message("${e.message}") - .body(content.toResponseBody()) - .build() - } } const val CONTENT_TYPE = "Content-Type" diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/cronet/CastboxCronetInterceptor.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/cronet/CastboxCronetInterceptor.kt new file mode 100644 index 0000000..5869cc3 --- /dev/null +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/cronet/CastboxCronetInterceptor.kt @@ -0,0 +1,27 @@ +package guru.core.analytics.data.api.cronet + +import guru.core.analytics.handler.AnalyticsCode +import guru.core.analytics.handler.EventHandler +import okhttp3.Interceptor +import okhttp3.Response + + +class CastboxCronetInterceptor : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + return if (CronetHelper.cronetInterceptor != null) { + return try { + CronetHelper.cronetInterceptor!!.intercept(chain) + } catch (e: Exception) { + EventHandler.INSTANCE.notifyEventHandler( + AnalyticsCode.ERROR_CRONET_INTERCEPTOR, + e.message + ) + throw e + } + } else { + chain.proceed(originalRequest) + } + } +} \ No newline at end of file diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/cronet/CronetHelper.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/cronet/CronetHelper.kt new file mode 100644 index 0000000..b8a4612 --- /dev/null +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/cronet/CronetHelper.kt @@ -0,0 +1,37 @@ +package guru.core.analytics.data.api.cronet + +import android.content.Context +import com.google.android.gms.net.CronetProviderInstaller +import com.google.net.cronet.okhttptransport.CronetInterceptor +import guru.core.analytics.handler.AnalyticsCode +import guru.core.analytics.handler.EventHandler +import org.chromium.net.CronetEngine + +object CronetHelper { + + var cronetInterceptor: CronetInterceptor? = null + private set + + fun init(context: Context, isEnable: Boolean?, callback:((Boolean) -> Unit)? = null) { + if (isEnable == true) { + try { + CronetProviderInstaller.installProvider(context).addOnSuccessListener { + val cronetEngine = CronetEngine.Builder(context).build() + cronetInterceptor = CronetInterceptor.newBuilder(cronetEngine).build() + callback?.invoke(true) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.CRONET_INIT_SUCCESS) + }.addOnFailureListener { + cronetInterceptor = null + callback?.invoke(false) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.CRONET_INIT_FAIL, it.message) + } + } catch (e: Exception) { + callback?.invoke(false) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.CRONET_INIT_EXCEPTION, e.message) + } + } else { + cronetInterceptor = null + } + } + +} \ No newline at end of file diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/dns/CustomDns.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/dns/CustomDns.kt index 1036908..c6a3ea0 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/dns/CustomDns.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/api/dns/CustomDns.kt @@ -1,25 +1,44 @@ package guru.core.analytics.data.api.dns import android.content.Context +import com.google.gson.reflect.TypeToken import guru.core.analytics.data.api.ServiceLocator +import guru.core.analytics.data.local.PreferencesManager import guru.core.analytics.handler.AnalyticsCode import guru.core.analytics.handler.EventHandler +import guru.core.analytics.utils.GsonUtil import kotlinx.coroutines.runBlocking import okhttp3.Dns import java.net.InetAddress import java.net.UnknownHostException -class CustomDns(private val context: Context) : Dns { +class CustomDns(private val context: Context, private val uploadIpAddress: List? = null) : Dns { + + private val cachedHostAddress by lazy { + val hostAddressJson = PreferencesManager.getInstance(context).hostAddressJson + return@lazy runCatching { + if (!hostAddressJson.isNullOrBlank()) { + val mapType = object: TypeToken>>() {}.type + GsonUtil.gson.fromJson(hostAddressJson, mapType) as? MutableMap> + } else null + }.getOrNull() ?: mutableMapOf() + } override fun lookup(hostname: String): List { return try { - Dns.SYSTEM.lookup(hostname) + Dns.SYSTEM.lookup(hostname).also { list -> + cacheHostAddress(hostname, list.map { it.hostAddress }) + } } catch (e: UnknownHostException) { try { lookupByGoogleDns(hostname) } catch (e: UnknownHostException) { - EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_DNS, e.message) - lookupByRemoteConfig(hostname) + try { + lookupByCacheAndRemote(hostname) + } catch (e: UnknownHostException) { + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_DNS, e.message) + lookupByRemoteConfig(hostname) + } } } } @@ -32,7 +51,11 @@ class CustomDns(private val context: Context) : Dns { ?.mapNotNull { it.data } } if (!ipList.isNullOrEmpty()) { - return ipList.map { InetAddress.getByAddress(convert(it)) } + cacheHostAddress(hostname, ipList) + val resultIpList = ipList.mapNotNull { convert(it) } + if (resultIpList.isNotEmpty()) { + return resultIpList + } } throw UnknownHostException("Broken Google dns lookup of $hostname") } @@ -41,13 +64,44 @@ class CustomDns(private val context: Context) : Dns { // val dnsConfig = RemoteConfig.getDnsConfig() // val ipList = dnsConfig?.get(hostname) // if (ipList != null && ipList.isNotEmpty()) { -// return ipList.map { InetAddress.getByAddress(convert(it)) } +// return ipList.mapNotNull { convert(it) } // } throw UnknownHostException("Broken remote config lookup of $hostname") } - private fun convert(ip: String): ByteArray { - val ipArray = ip.split(".").map { Integer.parseInt(it).toByte() } - return ipArray.toByteArray() + private fun convert(ip: String): InetAddress? { + return runCatching { + val byteArr = ip.split(".").map { Integer.parseInt(it).toByte() }.toByteArray() + InetAddress.getByAddress(byteArr) + }.getOrNull() + } + + private fun cacheHostAddress(hostname: String, hostAddressList: List) { + val addressList = hostAddressList.filterNotNull() + if (addressList.isEmpty()) return + try { + val cacheHostAddressList = cachedHostAddress[hostname]?.sorted() + if (addressList.sorted() != cacheHostAddressList) { + cachedHostAddress[hostname] = addressList + PreferencesManager.getInstance(context).hostAddressJson = GsonUtil.gson.toJson(cachedHostAddress) + } + } catch (e: Exception) { + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_DNS_CACHE, e.message) + } + } + + private fun lookupByCacheAndRemote(hostname: String): List { + val ipList = uploadIpAddress?.toMutableList() ?: mutableListOf() + val cacheIpList = cachedHostAddress[hostname] + if (!cacheIpList.isNullOrEmpty()) { + ipList.addAll(cacheIpList) + } + if (ipList.isNotEmpty()) { + val resultIpList = ipList.mapNotNull { convert(it) } + if (resultIpList.isNotEmpty()) { + return resultIpList + } + } + throw UnknownHostException("Broken cache dns lookup of $hostname") } } \ No newline at end of file diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/local/PreferencesManager.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/local/PreferencesManager.kt index e3f7ddd..41301ec 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/local/PreferencesManager.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/local/PreferencesManager.kt @@ -48,4 +48,5 @@ class PreferencesManager private constructor( var eventCountUploaded: Int? by bind("event_count_uploaded", 0) var uploadEventBaseUrl: String? by bind("update_event_base_url", "") var totalDurationFgEvent: Long? by bind("total_duration_fg_event", 0L) + var hostAddressJson: String? by bind("host_address", "") } diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/AnalyticsInfo.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/AnalyticsInfo.kt index fdfe00a..9bd4057 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/AnalyticsInfo.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/AnalyticsInfo.kt @@ -14,4 +14,6 @@ internal data class AnalyticsInfo( var xAppId: String? = null, var xDeviceInfo: String? = null, var mainProcess: String? = null, + var isEnableCronet: Boolean? = null, + var uploadIpAddress: List? = null, ) diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/GuruAnalyticsAuditSnapshot.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/GuruAnalyticsAuditSnapshot.kt new file mode 100644 index 0000000..1920b68 --- /dev/null +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/model/GuruAnalyticsAuditSnapshot.kt @@ -0,0 +1,59 @@ +package guru.core.analytics.data.model + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class GuruAnalyticsAuditSnapshot( + @SerializedName("initialized") val initialized: Boolean = false, + @SerializedName("engineInitialized") val engineInitialized: Boolean = false, + @SerializedName("useCronet") val useCronet: Boolean = false, + @SerializedName("eventDispatcherStarted") val eventDispatcherStarted: Boolean = false, + @SerializedName("fgHelperInitialized") val fgHelperInitialized: Boolean = false, + @SerializedName("connectionState") val connectionState: Boolean = false, + @SerializedName("total") val total: Int = 0, + @SerializedName("deleted") val deleted: Int = 0, + @SerializedName("uploaded") val uploaded: Int = 0, + @SerializedName("sessionUploaded") val sessionUploaded: Int = 0, + @SerializedName("sessionDeleted") val sessionDeleted: Int = 0, + @SerializedName("sessionTotal") val sessionTotal: Int = 0, + @SerializedName("uploadReady") val uploadReady: Boolean = false, +) + +object GuruAnalyticsAudit { + var initialized: Boolean = false + var engineInitialized: Boolean = false + var useCronet: Boolean = false + var eventDispatcherStarted: Boolean = false + var fgHelperInitialized: Boolean = false + var connectionState: Boolean = false + + // 整体的 + var total: Int = 0 + var deleted: Int = 0 + var uploaded: Int = 0 + + // 当前这一次 + var sessionUploaded: Int = 0 + var sessionDeleted: Int = 0 + var sessionTotal: Int = 0 + + var uploadReady: Boolean = false + + fun snapshot() = GuruAnalyticsAuditSnapshot( + initialized = initialized, + engineInitialized = engineInitialized, + useCronet = useCronet, + eventDispatcherStarted = eventDispatcherStarted, + fgHelperInitialized = fgHelperInitialized, + connectionState = connectionState, + total = total, + deleted = deleted, + uploaded = uploaded, + sessionUploaded = sessionUploaded, + sessionDeleted = sessionDeleted, + sessionTotal = sessionTotal, + uploadReady = uploadReady + ) + +} \ No newline at end of file diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/store/EventInfoStore.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/store/EventInfoStore.kt index 3c490ef..1497779 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/store/EventInfoStore.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/data/store/EventInfoStore.kt @@ -10,6 +10,7 @@ import guru.core.analytics.utils.DateTimeUtils import guru.core.analytics.utils.GsonUtil import io.reactivex.subjects.BehaviorSubject import java.util.* +import java.util.concurrent.ConcurrentHashMap object EventInfoStore { @@ -18,9 +19,11 @@ object EventInfoStore { UUID.randomUUID().toString() } - private val propertiesSubject: BehaviorSubject> = + private val propertiesSubject: BehaviorSubject> = BehaviorSubject.createDefault( - linkedMapOf() + ConcurrentHashMap().apply { + this[Constants.Properties.GURU_ANM] = Constants.AnmState.DEFAULT + } ) private val supplementEventParamsSubject: BehaviorSubject> = @@ -39,8 +42,8 @@ object EventInfoStore { idsSubject.onNext(value) } - private var properties: LinkedHashMap - get() = propertiesSubject.value ?: linkedMapOf() + var properties: ConcurrentHashMap + get() = propertiesSubject.value ?: ConcurrentHashMap() set(value) { propertiesSubject.onNext(value) } @@ -104,7 +107,12 @@ object EventInfoStore { } } - fun deriveEvent(event: EventItem, priority: Int = EventPriority.DEFAULT, uploading: Boolean = false): EventEntity { + fun deriveEvent( + event: EventItem, + priority: Int = EventPriority.DEFAULT, + uploading: Boolean = false, + elapsed: Long = 0 + ): EventEntity { val eventMap = mutableMapOf() if (!event.itemCategory.isNullOrBlank()) { eventMap[Constants.Event.ITEM_CATEGORY] = ParamValue(s = event.itemCategory) @@ -124,8 +132,9 @@ object EventInfoStore { eventMap[entry.key] = createParamValue(entry.value) } val eventId = UUID.randomUUID().toString() + val eventTs = DateTimeUtils.eventAt() - elapsed val eventData = Event( - timestamp = DateTimeUtils.eventAt(), + timestamp = eventTs, info = ids, event = event.eventName, param = eventMap, @@ -139,7 +148,7 @@ object EventInfoStore { json = eventJson, ext = "", status = if (uploading) 1 else 0, - at = DateTimeUtils.eventAt(), + at = eventTs, version = Constants.VERSION, event = event.eventName, priority = priority diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/handler/EventHandlerCode.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/handler/EventHandlerCode.kt index 4b880b9..f4b6cae 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/handler/EventHandlerCode.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/handler/EventHandlerCode.kt @@ -9,11 +9,15 @@ enum class AnalyticsCode(val code: Int) { UPLOAD_SUCCESS(13), // 上报事件成功 UPLOAD_FAIL(14), // 上报事件失败 PERIODIC_WORK_ENQUEUE(15), // 开启PeriodicWork + ENABLE_UPLOAD(16), // 修改是否允许上传埋点状态 NETWORK_AVAILABLE(21), // 网络状态可用 NETWORK_LOST(22), // 网络状态不可用 LIFECYCLE_START(23), // app可见 LIFECYCLE_PAUSE(24), // app不可见 + CRONET_INIT_SUCCESS(25), // 开启Cronet成功 + CRONET_INIT_FAIL(26), // 开启Cronet失败 + CRONET_INIT_EXCEPTION(27), // 开启Cronet报错 ERROR_API(101), // 调用api出错 ERROR_RESPONSE(102), // api返回结果错误 @@ -22,7 +26,20 @@ enum class AnalyticsCode(val code: Int) { ERROR_LOAD_MARK(105), // 从数据库取事件以及更改事件状态为正在上报出错 ERROR_DNS(106), // dns 错误 ERROR_ZIP(107), // zip 错误 + ERROR_DNS_CACHE(108), // zip 错误 + ERROR_CRONET_INTERCEPTOR(109),// cronet拦截器 EVENT_FIRST_OPEN(1001), // first_open 事件 EVENT_FG(1002), // fg 事件 + + // 初始化进度 + INIT_STEP_1(100001), + INIT_STEP_2(100002), + INIT_STEP_3(100003), + INIT_STEP_4(100004), + INIT_STEP_5(100005), + INIT_STEP_6(100006), + INIT_STEP_7(100007), + INIT_STEP_8(100008), + INIT_STEP_9(100009), } \ No newline at end of file diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventDispatcher.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventDispatcher.kt index 097d069..bac5996 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventDispatcher.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventDispatcher.kt @@ -1,16 +1,26 @@ package guru.core.analytics.impl +import android.os.SystemClock import guru.core.analytics.data.db.GuruAnalyticsDatabase import guru.core.analytics.data.model.AnalyticsOptions import guru.core.analytics.data.model.EventItem +import guru.core.analytics.data.model.GuruAnalyticsAudit import guru.core.analytics.data.store.EventInfoStore import timber.log.Timber import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicBoolean + +class PendingEvent( + val item: EventItem, + val options: AnalyticsOptions +) { + val at = SystemClock.elapsedRealtime() +} + object EventDispatcher : EventDeliver { - private val pendingEvents = ConcurrentLinkedQueue>() + private val pendingEvents = ConcurrentLinkedQueue() private val started = AtomicBoolean(false) @@ -18,8 +28,12 @@ object EventDispatcher : EventDeliver { Timber.d("EventDispatcher dispatchPendingEvent ${pendingEvents.size}!") if (pendingEvents.isNotEmpty()) { while (true) { - val pair = pendingEvents.poll() ?: return - val event = EventInfoStore.deriveEvent(pair.first, priority = pair.second.priority) + val pendingEvent = pendingEvents.poll() ?: return + val event = EventInfoStore.deriveEvent( + pendingEvent.item, + priority = pendingEvent.options.priority, + elapsed = SystemClock.elapsedRealtime() - pendingEvent.at + ) GuruAnalyticsDatabase.getInstance().eventDao().addEvent(event) } } @@ -29,6 +43,7 @@ object EventDispatcher : EventDeliver { if (started.compareAndSet(false, true)) { Timber.d("EventDispatcher started!") dispatchPendingEvent() + GuruAnalyticsAudit.eventDispatcherStarted = true } } @@ -40,7 +55,7 @@ object EventDispatcher : EventDeliver { GuruAnalyticsDatabase.getInstance().eventDao().addEvent(event) } else { Timber.d("EventDispatcher deliverEvent pending!") - pendingEvents.offer(item to options) + pendingEvents.offer(PendingEvent(item, options)) } } diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventEngine.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventEngine.kt index b722c35..a5010cf 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventEngine.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/EventEngine.kt @@ -1,6 +1,7 @@ package guru.core.analytics.impl import android.content.Context +import android.net.Uri import guru.core.analytics.Constants import guru.core.analytics.GuruAnalytics import guru.core.analytics.data.api.GuruRepository @@ -12,6 +13,7 @@ import guru.core.analytics.data.db.model.EventStatistic import guru.core.analytics.data.local.PreferencesManager import guru.core.analytics.data.model.AnalyticsOptions import guru.core.analytics.data.model.EventItem +import guru.core.analytics.data.model.GuruAnalyticsAudit import guru.core.analytics.data.store.EventInfoStore import guru.core.analytics.handler.AnalyticsCode import guru.core.analytics.handler.EventHandler @@ -39,10 +41,10 @@ internal class EventEngine internal constructor( private val batchLimit: Int = DEFAULT_BATCH_LIMIT, private val uploadPeriodInSeconds: Long = DEFAULT_UPLOAD_PERIOD_IN_SECONDS, private val eventExpiredInDays: Int = DEFAULT_EVENT_EXPIRED_IN_DAYS, - private val uploadEventBaseUrl: String? = null, + private val uploadEventBaseUri: Uri? = null, ) : EventDeliver { private val guruRepository: GuruRepository by lazy { - ServiceLocator.provideGuruRepository(context.applicationContext, baseUrl = uploadEventBaseUrl) + ServiceLocator.provideGuruRepository(context.applicationContext, baseUri = uploadEventBaseUri) } private val preferencesManager by lazy { @@ -83,6 +85,13 @@ internal class EventEngine internal constructor( private val forceUploadSubject: PublishSubject = PublishSubject.create() private val started = AtomicBoolean(false) + private var enableUpload = true + + fun setEnableUpload(enable: Boolean) { + enableUpload = enable + val extMap = mapOf("enable" to enable) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ENABLE_UPLOAD, extMap) + } fun start(startUploadDelay: Long?) { if (started.compareAndSet(false, true)) { @@ -96,6 +105,7 @@ internal class EventEngine internal constructor( val extMap = mapOf("startUploadDelayInSecond" to startUploadDelay) EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.STATE_START_WORK, extMap) }, startUploadDelay ?: 0, TimeUnit.SECONDS) + GuruAnalyticsAudit.engineInitialized = true } } @@ -146,6 +156,7 @@ internal class EventEngine internal constructor( logDebug("pendingEvent ${it.size}") } val networkFlowable = ConnectionStateMonitor.connectStateFlowable.doOnNext { + GuruAnalyticsAudit.connectionState = it logDebug("network $it") } val forceFlowable = forceUploadSubject.toFlowable(BackpressureStrategy.DROP) @@ -157,6 +168,7 @@ internal class EventEngine internal constructor( logDebug("pollEvent filter $it") return@filter it } + .filter { enableUpload } .flatMap { uploadEvents(500) } .subscribe() ) @@ -174,7 +186,10 @@ internal class EventEngine internal constructor( // 记录删除的事件数量 if (pair.first > 0) { val eventCountDeleted = preferencesManager.eventCountDeleted ?: 0 - preferencesManager.eventCountDeleted = eventCountDeleted + pair.first + val deleted = eventCountDeleted + pair.first + preferencesManager.eventCountDeleted = deleted + GuruAnalyticsAudit.deleted = deleted + GuruAnalyticsAudit.sessionDeleted += pair.first val extMap = mapOf( "expiredCount" to pair.first, @@ -192,6 +207,7 @@ internal class EventEngine internal constructor( internal fun uploadEvents(count: Int): Flowable> { val eventDao = GuruAnalyticsDatabase.getInstance().eventDao() + GuruAnalyticsAudit.uploadReady = true logDebug("uploadEvents: $count") return Flowable.just(count).map { eventDao.loadAndMarkUploadEvents(it) } .onErrorReturn { @@ -232,7 +248,10 @@ internal class EventEngine internal constructor( .doOnSuccess { // 记录上传成功的数量 val eventCountUploaded = preferencesManager.eventCountUploaded ?: 0 - preferencesManager.eventCountUploaded = eventCountUploaded + entities.size + val uploaded = eventCountUploaded + entities.size + preferencesManager.eventCountUploaded = uploaded + GuruAnalyticsAudit.uploaded = uploaded + GuruAnalyticsAudit.sessionUploaded += entities.size val extMap = mapOf( "count" to entities.size, @@ -288,7 +307,10 @@ internal class EventEngine internal constructor( private fun increaseEventCount() { val eventCountAll = preferencesManager.eventCountAll ?: 0 - preferencesManager.eventCountAll = eventCountAll + 1 + val total = eventCountAll + 1 + preferencesManager.eventCountAll = total + GuruAnalyticsAudit.total = total + GuruAnalyticsAudit.sessionTotal ++ } override fun deliverProperty(name: String, value: String) { diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/FgHelper.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/FgHelper.kt index c0adef1..0f44fbf 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/FgHelper.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/FgHelper.kt @@ -5,6 +5,7 @@ import android.os.SystemClock import guru.core.analytics.Constants import guru.core.analytics.GuruAnalytics import guru.core.analytics.data.local.PreferencesManager +import guru.core.analytics.data.model.GuruAnalyticsAudit import guru.core.analytics.handler.AnalyticsCode import guru.core.analytics.handler.EventHandler import io.reactivex.Flowable @@ -75,6 +76,7 @@ internal class FgHelper(context: Context) { }, { Timber.tag("FgHelper").e(it) }) + GuruAnalyticsAudit.fgHelperInitialized = true } fun stop() { diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/GuruAnalyticsImpl.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/GuruAnalyticsImpl.kt index d6cffaa..b7ae06a 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/GuruAnalyticsImpl.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/impl/GuruAnalyticsImpl.kt @@ -1,16 +1,19 @@ package guru.core.analytics.impl import android.content.Context +import android.net.Uri import androidx.annotation.RequiresPermission import androidx.work.* import guru.core.analytics.Constants import guru.core.analytics.GuruAnalytics import guru.core.analytics.data.api.ServiceLocator +import guru.core.analytics.data.api.cronet.CronetHelper import guru.core.analytics.data.db.GuruAnalyticsDatabase import guru.core.analytics.data.db.model.EventStatistic import guru.core.analytics.data.local.PreferencesManager import guru.core.analytics.data.model.AnalyticsOptions import guru.core.analytics.data.model.EventItem +import guru.core.analytics.data.model.GuruAnalyticsAudit import guru.core.analytics.data.store.DeviceInfoStore import guru.core.analytics.data.store.EventInfoStore import guru.core.analytics.handler.AnalyticsCode @@ -18,6 +21,7 @@ import guru.core.analytics.handler.EventHandler import guru.core.analytics.log.PersistentTree import guru.core.analytics.utils.AndroidUtils import guru.core.analytics.utils.EventChecker +import guru.core.analytics.utils.GsonUtil import guru.core.analytics.utils.SystemProperties import timber.log.Timber import java.io.File @@ -75,24 +79,55 @@ internal class GuruAnalyticsImpl : GuruAnalytics() { fgEventPeriodInSeconds: Long?, xAppId: String?, xDeviceInfo: String?, - mainProcess: String? + mainProcess: String?, + isEnableCronet: Boolean?, + uploadIpAddress: List?, ) { if (initialized.compareAndSet(false, true)) { delivers.add(EventDispatcher) eventHandlerCallback?.let { EventHandler.INSTANCE.addEventHandler(it) } + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_1) + val debugApp = SystemProperties.read("debug.guru.analytics.app") val forceDebug = debugApp == context.packageName if (forceDebug || persistableLog) { Timber.plant(PersistentTree(context, debug = debug)) } - debugMode = debug - Timber.d("[$internalVersion]initialize batchLimit:$batchLimit uploadPeriodInSecond:$uploadPeriodInSeconds startUploadDelayInSecond:$startUploadDelayInSecond eventExpiredInDays:$eventExpiredInDays debug:$debug") + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_2) + + val debugUrl = SystemProperties.read("debug.guru.analytics.url") + + debugMode = forceDebug || debug + Timber.d("[$internalVersion]initialize batchLimit:$batchLimit uploadPeriodInSecond:$uploadPeriodInSeconds startUploadDelayInSecond:$startUploadDelayInSecond eventExpiredInDays:$eventExpiredInDays debug:$debugMode debugUrl:$debugUrl") GuruAnalyticsDatabase.initialize(context.applicationContext) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_3) + + val baseUrl = if (forceDebug && debugUrl.isNotEmpty()) debugUrl else uploadEventBaseUrl DeviceInfoStore.setDeviceInfo(context) ServiceLocator.setDebug(debug) ServiceLocator.addHeaderParam("X-APP-ID", xAppId) ServiceLocator.addHeaderParam("X-DEVICE-INFO", xDeviceInfo) + ServiceLocator.setUploadIpAddress(uploadIpAddress) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_4) + + CronetHelper.init(context, isEnableCronet) { + if (it) { + setUserProperty(Constants.Properties.GURU_ANM, Constants.AnmState.CRONET) + GuruAnalyticsAudit.useCronet = true + } + } + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_5) + + var uploadEventBaseUri: Uri? = null + if (!baseUrl.isNullOrBlank()) { + uploadEventBaseUri = + kotlin.runCatching { Uri.parse(baseUrl) }.getOrNull() + if (uploadEventBaseUri?.scheme?.startsWith("http") != true) { + throw IllegalArgumentException("initialize updateEventBaseUrl:${baseUrl} incorrect format") + } + } + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_6) engine = EventEngine( context, @@ -102,10 +137,11 @@ internal class GuruAnalyticsImpl : GuruAnalytics() { eventExpiredInDays = EventEngine.DEFAULT_EVENT_EXPIRED_IN_DAYS.coerceAtLeast( eventExpiredInDays ?: EventEngine.DEFAULT_EVENT_EXPIRED_IN_DAYS ), - uploadEventBaseUrl = uploadEventBaseUrl, + uploadEventBaseUri = uploadEventBaseUri ).apply { - start(startUploadDelayInSecond) delivers.add(this) + start(startUploadDelayInSecond) + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_7) } if (isInitPeriodicWork) { val process = AndroidUtils.getProcessName(context) @@ -120,6 +156,7 @@ internal class GuruAnalyticsImpl : GuruAnalytics() { } } } + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_8) val extMap = mapOf( "version_code" to internalVersion, @@ -127,12 +164,17 @@ internal class GuruAnalyticsImpl : GuruAnalytics() { "uploadPeriodInSecond" to uploadPeriodInSeconds, "startUploadDelayInSecond" to startUploadDelayInSecond, "eventExpiredInDays" to eventExpiredInDays, + "uploadEventBaseUri" to uploadEventBaseUri.toString(), + "enabledCronet" to (isEnableCronet ?: false), + "unloadIpAddress" to (uploadIpAddress?.joinToString("|") ?: ""), "debug" to debug, ) EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.STATE_INITIALIZED, extMap) if (!uploadEventBaseUrl.isNullOrEmpty()) { - PreferencesManager.getInstance(context).uploadEventBaseUrl = uploadEventBaseUrl + PreferencesManager.getInstance(context).uploadEventBaseUrl = baseUrl } + EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_9) + GuruAnalyticsAudit.initialized = true } } @@ -265,6 +307,25 @@ internal class GuruAnalyticsImpl : GuruAnalytics() { removeProperties(keys) } + override fun getUserProperties(callback: (Map) -> Unit) { + deliverExecutor.execute { + callback.invoke(EventInfoStore.properties) + } + } + + override fun peakUserProperties(): Map { + return EventInfoStore.properties + } + + override fun setEnableUpload(enable: Boolean) { + engine?.setEnableUpload(enable) + } + + override fun snapshotAnalyticsAudit(): String { + val snapshot = GuruAnalyticsAudit.snapshot() + return GsonUtil.gson.toJson(snapshot) + } + private fun deliverEvent(item: EventItem, options: AnalyticsOptions) { Timber.tag("GuruAnalytics").d("deliverEvent ${item.eventName}!") deliverExecutor.execute { diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/ApiParamUtils.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/ApiParamUtils.kt index 7a60440..b99db8f 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/ApiParamUtils.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/ApiParamUtils.kt @@ -11,6 +11,7 @@ object ApiParamUtils { * 组装接口上传需要的json参数 */ fun generateApiParam(events: List): String { + /// todo: 版本需要根据当前的更新 val deviceInfoJson = GsonUtil.gson.toJson(DeviceInfoStore.deviceInfo) return "{\"version\":${Constants.VERSION},\"events\":[${events.joinToString(",") { it.json }}],\"deviceInfo\":$deviceInfoJson}" } diff --git a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/GZipUtils.kt b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/GZipUtils.kt index ffd5c00..dcc1fca 100644 --- a/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/GZipUtils.kt +++ b/guru_analytics/guru_analytics/src/main/java/guru/core/analytics/utils/GZipUtils.kt @@ -19,7 +19,7 @@ object GZipUtils { out.toByteArray() } catch (e: IOException) { EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_ZIP, e.message) - null + throw e } }