parent
5927987ff5
commit
3803d53afe
|
|
@ -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
|
## v0.3.1
|
||||||
|
|
||||||
##### Feature
|
##### Feature
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ import android.widget.TextView
|
||||||
import guru.core.analytics.GuruAnalytics
|
import guru.core.analytics.GuruAnalytics
|
||||||
import guru.core.analytics.data.db.model.EventPriority
|
import guru.core.analytics.data.db.model.EventPriority
|
||||||
import guru.core.analytics.data.model.AnalyticsOptions
|
import guru.core.analytics.data.model.AnalyticsOptions
|
||||||
import guru.core.analytics.handler.AnalyticsCode
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private var enableUpload = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
@ -36,6 +38,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
.setXAppId("test_x_app_id")
|
.setXAppId("test_x_app_id")
|
||||||
.setXDeviceInfo("test_x_device_info")
|
.setXDeviceInfo("test_x_device_info")
|
||||||
.setMainProcess("com.example.guruanalytics")
|
.setMainProcess("com.example.guruanalytics")
|
||||||
|
.isEnableCronet(true)
|
||||||
|
.setUploadIpAddress(listOf("3.210.96.186", "34.196.69.199"))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
findViewById<TextView>(R.id.tvLogEvent).setOnClickListener {
|
findViewById<TextView>(R.id.tvLogEvent).setOnClickListener {
|
||||||
|
|
@ -76,9 +80,31 @@ class MainActivity : AppCompatActivity() {
|
||||||
GuruAnalytics.INSTANCE.setUploadEventBaseUrl(this, "https://www.castbox.fm/")
|
GuruAnalytics.INSTANCE.setUploadEventBaseUrl(this, "https://www.castbox.fm/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val tvEnable = findViewById<TextView>(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.setScreen("main")
|
||||||
GuruAnalytics.INSTANCE.setAdId("AD_ID_01")
|
GuruAnalytics.INSTANCE.setAdId("AD_ID_01")
|
||||||
GuruAnalytics.INSTANCE.setUserProperty("uid", "110051")
|
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 ->
|
private val eventHandler: (Int, String?) -> Unit = { code, ext ->
|
||||||
|
|
|
||||||
|
|
@ -70,4 +70,13 @@
|
||||||
android:text="Update BaseUrl"
|
android:text="Update BaseUrl"
|
||||||
android:layout_gravity="center_horizontal" />
|
android:layout_gravity="center_horizontal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEnable"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:text="Enable Upload"
|
||||||
|
android:layout_gravity="center_horizontal" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
@ -53,7 +53,9 @@ dependencies {
|
||||||
|
|
||||||
implementation okhttpDependencies
|
implementation okhttpDependencies
|
||||||
|
|
||||||
implementation process
|
implementation processDependencies
|
||||||
|
|
||||||
implementation workerDependencies
|
implementation workerDependencies
|
||||||
|
|
||||||
|
implementation cronetDependencies
|
||||||
}
|
}
|
||||||
|
|
@ -21,6 +21,8 @@ ext {
|
||||||
preferenceVersion = '1.2.0'
|
preferenceVersion = '1.2.0'
|
||||||
processVersion = '2.4.0'
|
processVersion = '2.4.0'
|
||||||
workVersion = '2.7.1'
|
workVersion = '2.7.1'
|
||||||
|
cronetOkhttpVersion = '0.1.0'
|
||||||
|
playServicesCronetVersion = '18.0.1'
|
||||||
|
|
||||||
kaptDependencies = [
|
kaptDependencies = [
|
||||||
"androidx.room:room-compiler:$roomVersion",
|
"androidx.room:room-compiler:$roomVersion",
|
||||||
|
|
@ -54,7 +56,12 @@ ext {
|
||||||
"androidx.work:work-rxjava2:$workVersion"
|
"androidx.work:work-rxjava2:$workVersion"
|
||||||
]
|
]
|
||||||
|
|
||||||
process = [
|
processDependencies = [
|
||||||
"androidx.lifecycle:lifecycle-process:$processVersion"
|
"androidx.lifecycle:lifecycle-process:$processVersion"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
cronetDependencies = [
|
||||||
|
"com.google.net.cronet:cronet-okhttp:$cronetOkhttpVersion",
|
||||||
|
"com.google.android.gms:play-services-cronet:$playServicesCronetVersion"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ publishing { // Repositories *to* which Gradle can publish artifacts
|
||||||
maven(MavenPublication) {
|
maven(MavenPublication) {
|
||||||
groupId 'guru.core.analytics'
|
groupId 'guru.core.analytics'
|
||||||
artifactId 'guru_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 publishArtifact //Example: *./target/myJavaClasses.jar*
|
||||||
// artifact "build/outputs/aar/aar-test-release.aar"//aar包的目录
|
// artifact "build/outputs/aar/aar-test-release.aar"//aar包的目录
|
||||||
afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
|
afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,12 @@ object Constants {
|
||||||
const val COIN = "coin"
|
const val COIN = "coin"
|
||||||
const val EXP = "exp"
|
const val EXP = "exp"
|
||||||
const val HP = "hp"
|
const val HP = "hp"
|
||||||
|
const val GURU_ANM = "guru_anm"
|
||||||
|
}
|
||||||
|
|
||||||
|
object AnmState {
|
||||||
|
const val CRONET = "cronet"
|
||||||
|
const val DEFAULT = "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
object DeviceType {
|
object DeviceType {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
||||||
import guru.core.analytics.data.db.model.EventStatistic
|
import guru.core.analytics.data.db.model.EventStatistic
|
||||||
import guru.core.analytics.data.model.AnalyticsInfo
|
import guru.core.analytics.data.model.AnalyticsInfo
|
||||||
import guru.core.analytics.data.model.AnalyticsOptions
|
import guru.core.analytics.data.model.AnalyticsOptions
|
||||||
import guru.core.analytics.handler.AnalyticsCode
|
|
||||||
import guru.core.analytics.impl.GuruAnalyticsImpl
|
import guru.core.analytics.impl.GuruAnalyticsImpl
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
@ -21,13 +20,15 @@ abstract class GuruAnalytics {
|
||||||
eventExpiredInDays: Int? = 7,
|
eventExpiredInDays: Int? = 7,
|
||||||
debug: Boolean = false,
|
debug: Boolean = false,
|
||||||
persistableLog: Boolean = true,
|
persistableLog: Boolean = true,
|
||||||
listener: ((Int, String?) -> Unit)? = null,
|
eventHandlerCallback: ((Int, String?) -> Unit)? = null,
|
||||||
isInitPeriodicWork: Boolean = false,
|
isInitPeriodicWork: Boolean = false,
|
||||||
uploadEventBaseUrl: String? = null,
|
uploadEventBaseUrl: String? = null,
|
||||||
fgEventPeriodInSeconds: Long? = null,
|
fgEventPeriodInSeconds: Long? = null,
|
||||||
xAppId: String? = null,
|
xAppId: String? = null,
|
||||||
xDeviceInfo: String? = null,
|
xDeviceInfo: String? = null,
|
||||||
mainProcess: String? = null,
|
mainProcess: String? = null,
|
||||||
|
isEnableCronet: Boolean? = null,
|
||||||
|
uploadIpAddress: List<String>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
abstract fun setUploadEventBaseUrl(context: Context, updateEventBaseUrl: String)
|
abstract fun setUploadEventBaseUrl(context: Context, updateEventBaseUrl: String)
|
||||||
|
|
@ -71,6 +72,17 @@ abstract class GuruAnalytics {
|
||||||
|
|
||||||
abstract fun removeUserProperties(keys: Set<String>)
|
abstract fun removeUserProperties(keys: Set<String>)
|
||||||
|
|
||||||
|
abstract fun getUserProperties(callback: (Map<String, String>) -> Unit)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setUserProperty后立即获取, 可能无法获取到最新设置的值
|
||||||
|
*/
|
||||||
|
abstract fun peakUserProperties(): Map<String, String>
|
||||||
|
|
||||||
|
abstract fun setEnableUpload(enable: Boolean)
|
||||||
|
|
||||||
|
abstract fun snapshotAnalyticsAudit(): String
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val INSTANCE: GuruAnalytics by lazy() {
|
val INSTANCE: GuruAnalytics by lazy() {
|
||||||
GuruAnalyticsImpl()
|
GuruAnalyticsImpl()
|
||||||
|
|
@ -116,6 +128,12 @@ abstract class GuruAnalytics {
|
||||||
fun setMainProcess(process: String) =
|
fun setMainProcess(process: String) =
|
||||||
apply { analyticsInfo.mainProcess = process }
|
apply { analyticsInfo.mainProcess = process }
|
||||||
|
|
||||||
|
fun isEnableCronet(isEnableCronet: Boolean) =
|
||||||
|
apply { analyticsInfo.isEnableCronet = isEnableCronet }
|
||||||
|
|
||||||
|
fun setUploadIpAddress(uploadIpAddress: List<String>) =
|
||||||
|
apply { analyticsInfo.uploadIpAddress = uploadIpAddress }
|
||||||
|
|
||||||
fun build(): GuruAnalytics {
|
fun build(): GuruAnalytics {
|
||||||
analyticsInfo.run {
|
analyticsInfo.run {
|
||||||
INSTANCE.initialize(
|
INSTANCE.initialize(
|
||||||
|
|
@ -133,6 +151,8 @@ abstract class GuruAnalytics {
|
||||||
xAppId,
|
xAppId,
|
||||||
xDeviceInfo,
|
xDeviceInfo,
|
||||||
mainProcess,
|
mainProcess,
|
||||||
|
isEnableCronet,
|
||||||
|
uploadIpAddress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return INSTANCE
|
return INSTANCE
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,28 @@
|
||||||
package guru.core.analytics.data.api
|
package guru.core.analytics.data.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import android.os.SystemClock
|
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.CustomDns
|
||||||
import guru.core.analytics.data.api.dns.GoogleDnsApi
|
import guru.core.analytics.data.api.dns.GoogleDnsApi
|
||||||
import guru.core.analytics.data.api.dns.GoogleDnsApiHost
|
import guru.core.analytics.data.api.dns.GoogleDnsApiHost
|
||||||
import guru.core.analytics.data.api.logging.Level
|
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.data.local.PreferencesManager
|
||||||
import guru.core.analytics.handler.AnalyticsCode
|
import guru.core.analytics.handler.AnalyticsCode
|
||||||
import guru.core.analytics.handler.EventHandler
|
import guru.core.analytics.handler.EventHandler
|
||||||
import guru.core.analytics.utils.AndroidUtils
|
import guru.core.analytics.utils.AndroidUtils
|
||||||
import guru.core.analytics.utils.DateTimeUtils
|
import guru.core.analytics.utils.DateTimeUtils
|
||||||
import guru.core.analytics.utils.GsonUtil
|
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.Response
|
||||||
|
import okhttp3.ResponseBody
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
import okhttp3.internal.platform.Platform
|
import okhttp3.internal.platform.Platform
|
||||||
import retrofit2.Converter
|
import retrofit2.Converter
|
||||||
|
|
@ -35,6 +43,8 @@ object ServiceLocator {
|
||||||
|
|
||||||
private val headerParams = mutableMapOf<String, String>()
|
private val headerParams = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
private var uploadIpAddress: List<String>? = null
|
||||||
|
|
||||||
fun addHeaderParam(key: String, value: String?) {
|
fun addHeaderParam(key: String, value: String?) {
|
||||||
if (value.isNullOrBlank()) return
|
if (value.isNullOrBlank()) return
|
||||||
headerParams[key] = value
|
headerParams[key] = value
|
||||||
|
|
@ -44,20 +54,24 @@ object ServiceLocator {
|
||||||
this.debug = debug
|
this.debug = debug
|
||||||
}
|
}
|
||||||
|
|
||||||
fun provideGuruRepository(context: Context, baseUrl: String? = null): GuruRepository {
|
fun setUploadIpAddress(ipList: List<String>?) {
|
||||||
|
uploadIpAddress = ipList
|
||||||
|
}
|
||||||
|
|
||||||
|
fun provideGuruRepository(context: Context, baseUri: Uri? = null): GuruRepository {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
return guruRepository
|
return guruRepository
|
||||||
?: createQuotesRepository(context, baseUrl).apply { guruRepository = this }
|
?: createQuotesRepository(context, baseUri).apply { guruRepository = this }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQuotesRepository(context: Context, baseUrl: String? = null): GuruRepository {
|
private fun createQuotesRepository(context: Context, baseUri: Uri? = null): GuruRepository {
|
||||||
return DefaultGuruRepository(provideAnalyticsApi(context, baseUrl))
|
return DefaultGuruRepository(provideAnalyticsApi(context, baseUri))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun provideAnalyticsApi(context: Context, baseUrl: String? = null): AnalyticsApi {
|
private fun provideAnalyticsApi(context: Context, baseUri: Uri? = null): AnalyticsApi {
|
||||||
val finalBaseUrl = if (!baseUrl.isNullOrEmpty()) {
|
val finalBaseUrl = if (baseUri != null) {
|
||||||
baseUrl
|
baseUri.toString()
|
||||||
} else {
|
} else {
|
||||||
val cacheBaseUrl = PreferencesManager.getInstance(context).uploadEventBaseUrl
|
val cacheBaseUrl = PreferencesManager.getInstance(context).uploadEventBaseUrl
|
||||||
if (cacheBaseUrl.isNullOrEmpty()) AnalyticsApiHost.BASE_URL else cacheBaseUrl
|
if (cacheBaseUrl.isNullOrEmpty()) AnalyticsApiHost.BASE_URL else cacheBaseUrl
|
||||||
|
|
@ -114,20 +128,22 @@ object ServiceLocator {
|
||||||
maxRequests = 128
|
maxRequests = 128
|
||||||
maxRequestsPerHost = 10
|
maxRequestsPerHost = 10
|
||||||
})
|
})
|
||||||
.dns(CustomDns(context))
|
.dns(CustomDns(context, uploadIpAddress))
|
||||||
.connectTimeout(20L, TimeUnit.SECONDS)
|
.connectTimeout(20L, TimeUnit.SECONDS)
|
||||||
.readTimeout(readTimeOut, TimeUnit.SECONDS)
|
.readTimeout(readTimeOut, TimeUnit.SECONDS)
|
||||||
.writeTimeout(writeTimeOut, TimeUnit.SECONDS)
|
.writeTimeout(writeTimeOut, TimeUnit.SECONDS)
|
||||||
.addInterceptor(createCacheControlInterceptor(context))
|
.addInterceptor(createCacheControlInterceptor(context))
|
||||||
.addInterceptor(createAnalyticsApiInterceptor())
|
.addInterceptor(createAnalyticsApiInterceptor())
|
||||||
.addInterceptor(createLoggingInterceptor())
|
.addInterceptor(createLoggingInterceptor())
|
||||||
|
.addInterceptor(createCronetInterceptor())
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createDnsOkHttpClient(context: Context): OkHttpClient {
|
private fun createDnsOkHttpClient(context: Context): OkHttpClient {
|
||||||
val builder = OkHttpClient.Builder()
|
val builder = OkHttpClient.Builder()
|
||||||
.addInterceptor(createCacheControlInterceptor(context))
|
.addInterceptor(createCacheControlInterceptor(context))
|
||||||
builder.addInterceptor(createLoggingInterceptor())
|
.addInterceptor(createLoggingInterceptor())
|
||||||
|
.addInterceptor(createCronetInterceptor())
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,6 +156,14 @@ object ServiceLocator {
|
||||||
.build()
|
.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 ->
|
private fun createCacheControlInterceptor(context: Context) = Interceptor { chain ->
|
||||||
try {
|
try {
|
||||||
val originalResponse = chain.proceed(chain.request())
|
val originalResponse = chain.proceed(chain.request())
|
||||||
|
|
@ -162,10 +186,7 @@ object ServiceLocator {
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_CACHE_CONTROL, e.message)
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_CACHE_CONTROL, e.message)
|
||||||
return@Interceptor exceptionResponse(
|
throw e
|
||||||
e,
|
|
||||||
chain.request()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +195,6 @@ object ServiceLocator {
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
val startTime = SystemClock.elapsedRealtime()
|
val startTime = SystemClock.elapsedRealtime()
|
||||||
val builder = request.newBuilder()
|
val builder = request.newBuilder()
|
||||||
.addHeader(CONTENT_TYPE, "application/json")
|
|
||||||
.addHeader(CONTENT_ENCODING, "gzip")
|
.addHeader(CONTENT_ENCODING, "gzip")
|
||||||
.addHeader(X_EVENT_TYPE, "event")
|
.addHeader(X_EVENT_TYPE, "event")
|
||||||
headerParams.forEach {
|
headerParams.forEach {
|
||||||
|
|
@ -182,17 +202,13 @@ object ServiceLocator {
|
||||||
}
|
}
|
||||||
val newRequest = builder.build()
|
val newRequest = builder.build()
|
||||||
try {
|
try {
|
||||||
val response = chain.proceed(newRequest)
|
chain.proceed(newRequest).also { response ->
|
||||||
val responseTime = (SystemClock.elapsedRealtime() - startTime) / 2
|
val responseTime = (SystemClock.elapsedRealtime() - startTime) / 2
|
||||||
calibrationTime(responseTime, response.headers)
|
calibrationTime(responseTime, response.headers)
|
||||||
if (response.isSuccessful) {
|
|
||||||
successResponse(response)
|
|
||||||
} else {
|
|
||||||
httpErrorResponse(response)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_API, e.message)
|
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)
|
DateTimeUtils.initServerTime(date.time + responseTime)
|
||||||
minResponseTime = 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"
|
const val CONTENT_TYPE = "Content-Type"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,25 +1,44 @@
|
||||||
package guru.core.analytics.data.api.dns
|
package guru.core.analytics.data.api.dns
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
import guru.core.analytics.data.api.ServiceLocator
|
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.AnalyticsCode
|
||||||
import guru.core.analytics.handler.EventHandler
|
import guru.core.analytics.handler.EventHandler
|
||||||
|
import guru.core.analytics.utils.GsonUtil
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
|
||||||
class CustomDns(private val context: Context) : Dns {
|
class CustomDns(private val context: Context, private val uploadIpAddress: List<String>? = null) : Dns {
|
||||||
|
|
||||||
|
private val cachedHostAddress by lazy {
|
||||||
|
val hostAddressJson = PreferencesManager.getInstance(context).hostAddressJson
|
||||||
|
return@lazy runCatching {
|
||||||
|
if (!hostAddressJson.isNullOrBlank()) {
|
||||||
|
val mapType = object: TypeToken<Map<String, List<String>>>() {}.type
|
||||||
|
GsonUtil.gson.fromJson(hostAddressJson, mapType) as? MutableMap<String, List<String>>
|
||||||
|
} else null
|
||||||
|
}.getOrNull() ?: mutableMapOf()
|
||||||
|
}
|
||||||
|
|
||||||
override fun lookup(hostname: String): List<InetAddress> {
|
override fun lookup(hostname: String): List<InetAddress> {
|
||||||
return try {
|
return try {
|
||||||
Dns.SYSTEM.lookup(hostname)
|
Dns.SYSTEM.lookup(hostname).also { list ->
|
||||||
|
cacheHostAddress(hostname, list.map { it.hostAddress })
|
||||||
|
}
|
||||||
} catch (e: UnknownHostException) {
|
} catch (e: UnknownHostException) {
|
||||||
try {
|
try {
|
||||||
lookupByGoogleDns(hostname)
|
lookupByGoogleDns(hostname)
|
||||||
} catch (e: UnknownHostException) {
|
} catch (e: UnknownHostException) {
|
||||||
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_DNS, e.message)
|
try {
|
||||||
lookupByRemoteConfig(hostname)
|
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 }
|
?.mapNotNull { it.data }
|
||||||
}
|
}
|
||||||
if (!ipList.isNullOrEmpty()) {
|
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")
|
throw UnknownHostException("Broken Google dns lookup of $hostname")
|
||||||
}
|
}
|
||||||
|
|
@ -41,13 +64,44 @@ class CustomDns(private val context: Context) : Dns {
|
||||||
// val dnsConfig = RemoteConfig.getDnsConfig()
|
// val dnsConfig = RemoteConfig.getDnsConfig()
|
||||||
// val ipList = dnsConfig?.get(hostname)
|
// val ipList = dnsConfig?.get(hostname)
|
||||||
// if (ipList != null && ipList.isNotEmpty()) {
|
// 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")
|
throw UnknownHostException("Broken remote config lookup of $hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convert(ip: String): ByteArray {
|
private fun convert(ip: String): InetAddress? {
|
||||||
val ipArray = ip.split(".").map { Integer.parseInt(it).toByte() }
|
return runCatching {
|
||||||
return ipArray.toByteArray()
|
val byteArr = ip.split(".").map { Integer.parseInt(it).toByte() }.toByteArray()
|
||||||
|
InetAddress.getByAddress(byteArr)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cacheHostAddress(hostname: String, hostAddressList: List<String?>) {
|
||||||
|
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<InetAddress> {
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,4 +48,5 @@ class PreferencesManager private constructor(
|
||||||
var eventCountUploaded: Int? by bind("event_count_uploaded", 0)
|
var eventCountUploaded: Int? by bind("event_count_uploaded", 0)
|
||||||
var uploadEventBaseUrl: String? by bind("update_event_base_url", "")
|
var uploadEventBaseUrl: String? by bind("update_event_base_url", "")
|
||||||
var totalDurationFgEvent: Long? by bind("total_duration_fg_event", 0L)
|
var totalDurationFgEvent: Long? by bind("total_duration_fg_event", 0L)
|
||||||
|
var hostAddressJson: String? by bind("host_address", "")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,6 @@ internal data class AnalyticsInfo(
|
||||||
var xAppId: String? = null,
|
var xAppId: String? = null,
|
||||||
var xDeviceInfo: String? = null,
|
var xDeviceInfo: String? = null,
|
||||||
var mainProcess: String? = null,
|
var mainProcess: String? = null,
|
||||||
|
var isEnableCronet: Boolean? = null,
|
||||||
|
var uploadIpAddress: List<String>? = null,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import guru.core.analytics.utils.DateTimeUtils
|
||||||
import guru.core.analytics.utils.GsonUtil
|
import guru.core.analytics.utils.GsonUtil
|
||||||
import io.reactivex.subjects.BehaviorSubject
|
import io.reactivex.subjects.BehaviorSubject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
object EventInfoStore {
|
object EventInfoStore {
|
||||||
|
|
||||||
|
|
@ -18,9 +19,11 @@ object EventInfoStore {
|
||||||
UUID.randomUUID().toString()
|
UUID.randomUUID().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val propertiesSubject: BehaviorSubject<LinkedHashMap<String, String>> =
|
private val propertiesSubject: BehaviorSubject<ConcurrentHashMap<String, String>> =
|
||||||
BehaviorSubject.createDefault(
|
BehaviorSubject.createDefault(
|
||||||
linkedMapOf()
|
ConcurrentHashMap<String, String>().apply {
|
||||||
|
this[Constants.Properties.GURU_ANM] = Constants.AnmState.DEFAULT
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
private val supplementEventParamsSubject: BehaviorSubject<Map<String, Any>> =
|
private val supplementEventParamsSubject: BehaviorSubject<Map<String, Any>> =
|
||||||
|
|
@ -39,8 +42,8 @@ object EventInfoStore {
|
||||||
idsSubject.onNext(value)
|
idsSubject.onNext(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var properties: LinkedHashMap<String, String>
|
var properties: ConcurrentHashMap<String, String>
|
||||||
get() = propertiesSubject.value ?: linkedMapOf()
|
get() = propertiesSubject.value ?: ConcurrentHashMap()
|
||||||
set(value) {
|
set(value) {
|
||||||
propertiesSubject.onNext(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<String, ParamValue>()
|
val eventMap = mutableMapOf<String, ParamValue>()
|
||||||
if (!event.itemCategory.isNullOrBlank()) {
|
if (!event.itemCategory.isNullOrBlank()) {
|
||||||
eventMap[Constants.Event.ITEM_CATEGORY] = ParamValue(s = event.itemCategory)
|
eventMap[Constants.Event.ITEM_CATEGORY] = ParamValue(s = event.itemCategory)
|
||||||
|
|
@ -124,8 +132,9 @@ object EventInfoStore {
|
||||||
eventMap[entry.key] = createParamValue(entry.value)
|
eventMap[entry.key] = createParamValue(entry.value)
|
||||||
}
|
}
|
||||||
val eventId = UUID.randomUUID().toString()
|
val eventId = UUID.randomUUID().toString()
|
||||||
|
val eventTs = DateTimeUtils.eventAt() - elapsed
|
||||||
val eventData = Event(
|
val eventData = Event(
|
||||||
timestamp = DateTimeUtils.eventAt(),
|
timestamp = eventTs,
|
||||||
info = ids,
|
info = ids,
|
||||||
event = event.eventName,
|
event = event.eventName,
|
||||||
param = eventMap,
|
param = eventMap,
|
||||||
|
|
@ -139,7 +148,7 @@ object EventInfoStore {
|
||||||
json = eventJson,
|
json = eventJson,
|
||||||
ext = "",
|
ext = "",
|
||||||
status = if (uploading) 1 else 0,
|
status = if (uploading) 1 else 0,
|
||||||
at = DateTimeUtils.eventAt(),
|
at = eventTs,
|
||||||
version = Constants.VERSION,
|
version = Constants.VERSION,
|
||||||
event = event.eventName,
|
event = event.eventName,
|
||||||
priority = priority
|
priority = priority
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,15 @@ enum class AnalyticsCode(val code: Int) {
|
||||||
UPLOAD_SUCCESS(13), // 上报事件成功
|
UPLOAD_SUCCESS(13), // 上报事件成功
|
||||||
UPLOAD_FAIL(14), // 上报事件失败
|
UPLOAD_FAIL(14), // 上报事件失败
|
||||||
PERIODIC_WORK_ENQUEUE(15), // 开启PeriodicWork
|
PERIODIC_WORK_ENQUEUE(15), // 开启PeriodicWork
|
||||||
|
ENABLE_UPLOAD(16), // 修改是否允许上传埋点状态
|
||||||
|
|
||||||
NETWORK_AVAILABLE(21), // 网络状态可用
|
NETWORK_AVAILABLE(21), // 网络状态可用
|
||||||
NETWORK_LOST(22), // 网络状态不可用
|
NETWORK_LOST(22), // 网络状态不可用
|
||||||
LIFECYCLE_START(23), // app可见
|
LIFECYCLE_START(23), // app可见
|
||||||
LIFECYCLE_PAUSE(24), // 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_API(101), // 调用api出错
|
||||||
ERROR_RESPONSE(102), // api返回结果错误
|
ERROR_RESPONSE(102), // api返回结果错误
|
||||||
|
|
@ -22,7 +26,20 @@ enum class AnalyticsCode(val code: Int) {
|
||||||
ERROR_LOAD_MARK(105), // 从数据库取事件以及更改事件状态为正在上报出错
|
ERROR_LOAD_MARK(105), // 从数据库取事件以及更改事件状态为正在上报出错
|
||||||
ERROR_DNS(106), // dns 错误
|
ERROR_DNS(106), // dns 错误
|
||||||
ERROR_ZIP(107), // zip 错误
|
ERROR_ZIP(107), // zip 错误
|
||||||
|
ERROR_DNS_CACHE(108), // zip 错误
|
||||||
|
ERROR_CRONET_INTERCEPTOR(109),// cronet拦截器
|
||||||
|
|
||||||
EVENT_FIRST_OPEN(1001), // first_open 事件
|
EVENT_FIRST_OPEN(1001), // first_open 事件
|
||||||
EVENT_FG(1002), // fg 事件
|
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),
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,26 @@
|
||||||
package guru.core.analytics.impl
|
package guru.core.analytics.impl
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
import guru.core.analytics.data.db.GuruAnalyticsDatabase
|
import guru.core.analytics.data.db.GuruAnalyticsDatabase
|
||||||
import guru.core.analytics.data.model.AnalyticsOptions
|
import guru.core.analytics.data.model.AnalyticsOptions
|
||||||
import guru.core.analytics.data.model.EventItem
|
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.data.store.EventInfoStore
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
|
||||||
|
class PendingEvent(
|
||||||
|
val item: EventItem,
|
||||||
|
val options: AnalyticsOptions
|
||||||
|
) {
|
||||||
|
val at = SystemClock.elapsedRealtime()
|
||||||
|
}
|
||||||
|
|
||||||
object EventDispatcher : EventDeliver {
|
object EventDispatcher : EventDeliver {
|
||||||
|
|
||||||
private val pendingEvents = ConcurrentLinkedQueue<Pair<EventItem, AnalyticsOptions>>()
|
private val pendingEvents = ConcurrentLinkedQueue<PendingEvent>()
|
||||||
|
|
||||||
private val started = AtomicBoolean(false)
|
private val started = AtomicBoolean(false)
|
||||||
|
|
||||||
|
|
@ -18,8 +28,12 @@ object EventDispatcher : EventDeliver {
|
||||||
Timber.d("EventDispatcher dispatchPendingEvent ${pendingEvents.size}!")
|
Timber.d("EventDispatcher dispatchPendingEvent ${pendingEvents.size}!")
|
||||||
if (pendingEvents.isNotEmpty()) {
|
if (pendingEvents.isNotEmpty()) {
|
||||||
while (true) {
|
while (true) {
|
||||||
val pair = pendingEvents.poll() ?: return
|
val pendingEvent = pendingEvents.poll() ?: return
|
||||||
val event = EventInfoStore.deriveEvent(pair.first, priority = pair.second.priority)
|
val event = EventInfoStore.deriveEvent(
|
||||||
|
pendingEvent.item,
|
||||||
|
priority = pendingEvent.options.priority,
|
||||||
|
elapsed = SystemClock.elapsedRealtime() - pendingEvent.at
|
||||||
|
)
|
||||||
GuruAnalyticsDatabase.getInstance().eventDao().addEvent(event)
|
GuruAnalyticsDatabase.getInstance().eventDao().addEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +43,7 @@ object EventDispatcher : EventDeliver {
|
||||||
if (started.compareAndSet(false, true)) {
|
if (started.compareAndSet(false, true)) {
|
||||||
Timber.d("EventDispatcher started!")
|
Timber.d("EventDispatcher started!")
|
||||||
dispatchPendingEvent()
|
dispatchPendingEvent()
|
||||||
|
GuruAnalyticsAudit.eventDispatcherStarted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,7 +55,7 @@ object EventDispatcher : EventDeliver {
|
||||||
GuruAnalyticsDatabase.getInstance().eventDao().addEvent(event)
|
GuruAnalyticsDatabase.getInstance().eventDao().addEvent(event)
|
||||||
} else {
|
} else {
|
||||||
Timber.d("EventDispatcher deliverEvent pending!")
|
Timber.d("EventDispatcher deliverEvent pending!")
|
||||||
pendingEvents.offer(item to options)
|
pendingEvents.offer(PendingEvent(item, options))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package guru.core.analytics.impl
|
package guru.core.analytics.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import guru.core.analytics.Constants
|
import guru.core.analytics.Constants
|
||||||
import guru.core.analytics.GuruAnalytics
|
import guru.core.analytics.GuruAnalytics
|
||||||
import guru.core.analytics.data.api.GuruRepository
|
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.local.PreferencesManager
|
||||||
import guru.core.analytics.data.model.AnalyticsOptions
|
import guru.core.analytics.data.model.AnalyticsOptions
|
||||||
import guru.core.analytics.data.model.EventItem
|
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.data.store.EventInfoStore
|
||||||
import guru.core.analytics.handler.AnalyticsCode
|
import guru.core.analytics.handler.AnalyticsCode
|
||||||
import guru.core.analytics.handler.EventHandler
|
import guru.core.analytics.handler.EventHandler
|
||||||
|
|
@ -39,10 +41,10 @@ internal class EventEngine internal constructor(
|
||||||
private val batchLimit: Int = DEFAULT_BATCH_LIMIT,
|
private val batchLimit: Int = DEFAULT_BATCH_LIMIT,
|
||||||
private val uploadPeriodInSeconds: Long = DEFAULT_UPLOAD_PERIOD_IN_SECONDS,
|
private val uploadPeriodInSeconds: Long = DEFAULT_UPLOAD_PERIOD_IN_SECONDS,
|
||||||
private val eventExpiredInDays: Int = DEFAULT_EVENT_EXPIRED_IN_DAYS,
|
private val eventExpiredInDays: Int = DEFAULT_EVENT_EXPIRED_IN_DAYS,
|
||||||
private val uploadEventBaseUrl: String? = null,
|
private val uploadEventBaseUri: Uri? = null,
|
||||||
) : EventDeliver {
|
) : EventDeliver {
|
||||||
private val guruRepository: GuruRepository by lazy {
|
private val guruRepository: GuruRepository by lazy {
|
||||||
ServiceLocator.provideGuruRepository(context.applicationContext, baseUrl = uploadEventBaseUrl)
|
ServiceLocator.provideGuruRepository(context.applicationContext, baseUri = uploadEventBaseUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val preferencesManager by lazy {
|
private val preferencesManager by lazy {
|
||||||
|
|
@ -83,6 +85,13 @@ internal class EventEngine internal constructor(
|
||||||
private val forceUploadSubject: PublishSubject<Boolean> = PublishSubject.create()
|
private val forceUploadSubject: PublishSubject<Boolean> = PublishSubject.create()
|
||||||
|
|
||||||
private val started = AtomicBoolean(false)
|
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?) {
|
fun start(startUploadDelay: Long?) {
|
||||||
if (started.compareAndSet(false, true)) {
|
if (started.compareAndSet(false, true)) {
|
||||||
|
|
@ -96,6 +105,7 @@ internal class EventEngine internal constructor(
|
||||||
val extMap = mapOf("startUploadDelayInSecond" to startUploadDelay)
|
val extMap = mapOf("startUploadDelayInSecond" to startUploadDelay)
|
||||||
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.STATE_START_WORK, extMap)
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.STATE_START_WORK, extMap)
|
||||||
}, startUploadDelay ?: 0, TimeUnit.SECONDS)
|
}, startUploadDelay ?: 0, TimeUnit.SECONDS)
|
||||||
|
GuruAnalyticsAudit.engineInitialized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,6 +156,7 @@ internal class EventEngine internal constructor(
|
||||||
logDebug("pendingEvent ${it.size}")
|
logDebug("pendingEvent ${it.size}")
|
||||||
}
|
}
|
||||||
val networkFlowable = ConnectionStateMonitor.connectStateFlowable.doOnNext {
|
val networkFlowable = ConnectionStateMonitor.connectStateFlowable.doOnNext {
|
||||||
|
GuruAnalyticsAudit.connectionState = it
|
||||||
logDebug("network $it")
|
logDebug("network $it")
|
||||||
}
|
}
|
||||||
val forceFlowable = forceUploadSubject.toFlowable(BackpressureStrategy.DROP)
|
val forceFlowable = forceUploadSubject.toFlowable(BackpressureStrategy.DROP)
|
||||||
|
|
@ -157,6 +168,7 @@ internal class EventEngine internal constructor(
|
||||||
logDebug("pollEvent filter $it")
|
logDebug("pollEvent filter $it")
|
||||||
return@filter it
|
return@filter it
|
||||||
}
|
}
|
||||||
|
.filter { enableUpload }
|
||||||
.flatMap { uploadEvents(500) }
|
.flatMap { uploadEvents(500) }
|
||||||
.subscribe()
|
.subscribe()
|
||||||
)
|
)
|
||||||
|
|
@ -174,7 +186,10 @@ internal class EventEngine internal constructor(
|
||||||
// 记录删除的事件数量
|
// 记录删除的事件数量
|
||||||
if (pair.first > 0) {
|
if (pair.first > 0) {
|
||||||
val eventCountDeleted = preferencesManager.eventCountDeleted ?: 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(
|
val extMap = mapOf(
|
||||||
"expiredCount" to pair.first,
|
"expiredCount" to pair.first,
|
||||||
|
|
@ -192,6 +207,7 @@ internal class EventEngine internal constructor(
|
||||||
|
|
||||||
internal fun uploadEvents(count: Int): Flowable<List<EventEntity>> {
|
internal fun uploadEvents(count: Int): Flowable<List<EventEntity>> {
|
||||||
val eventDao = GuruAnalyticsDatabase.getInstance().eventDao()
|
val eventDao = GuruAnalyticsDatabase.getInstance().eventDao()
|
||||||
|
GuruAnalyticsAudit.uploadReady = true
|
||||||
logDebug("uploadEvents: $count")
|
logDebug("uploadEvents: $count")
|
||||||
return Flowable.just(count).map { eventDao.loadAndMarkUploadEvents(it) }
|
return Flowable.just(count).map { eventDao.loadAndMarkUploadEvents(it) }
|
||||||
.onErrorReturn {
|
.onErrorReturn {
|
||||||
|
|
@ -232,7 +248,10 @@ internal class EventEngine internal constructor(
|
||||||
.doOnSuccess {
|
.doOnSuccess {
|
||||||
// 记录上传成功的数量
|
// 记录上传成功的数量
|
||||||
val eventCountUploaded = preferencesManager.eventCountUploaded ?: 0
|
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(
|
val extMap = mapOf(
|
||||||
"count" to entities.size,
|
"count" to entities.size,
|
||||||
|
|
@ -288,7 +307,10 @@ internal class EventEngine internal constructor(
|
||||||
|
|
||||||
private fun increaseEventCount() {
|
private fun increaseEventCount() {
|
||||||
val eventCountAll = preferencesManager.eventCountAll ?: 0
|
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) {
|
override fun deliverProperty(name: String, value: String) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import android.os.SystemClock
|
||||||
import guru.core.analytics.Constants
|
import guru.core.analytics.Constants
|
||||||
import guru.core.analytics.GuruAnalytics
|
import guru.core.analytics.GuruAnalytics
|
||||||
import guru.core.analytics.data.local.PreferencesManager
|
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.AnalyticsCode
|
||||||
import guru.core.analytics.handler.EventHandler
|
import guru.core.analytics.handler.EventHandler
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
|
|
@ -75,6 +76,7 @@ internal class FgHelper(context: Context) {
|
||||||
}, {
|
}, {
|
||||||
Timber.tag("FgHelper").e(it)
|
Timber.tag("FgHelper").e(it)
|
||||||
})
|
})
|
||||||
|
GuruAnalyticsAudit.fgHelperInitialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
package guru.core.analytics.impl
|
package guru.core.analytics.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import androidx.annotation.RequiresPermission
|
import androidx.annotation.RequiresPermission
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import guru.core.analytics.Constants
|
import guru.core.analytics.Constants
|
||||||
import guru.core.analytics.GuruAnalytics
|
import guru.core.analytics.GuruAnalytics
|
||||||
import guru.core.analytics.data.api.ServiceLocator
|
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.GuruAnalyticsDatabase
|
||||||
import guru.core.analytics.data.db.model.EventStatistic
|
import guru.core.analytics.data.db.model.EventStatistic
|
||||||
import guru.core.analytics.data.local.PreferencesManager
|
import guru.core.analytics.data.local.PreferencesManager
|
||||||
import guru.core.analytics.data.model.AnalyticsOptions
|
import guru.core.analytics.data.model.AnalyticsOptions
|
||||||
import guru.core.analytics.data.model.EventItem
|
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.DeviceInfoStore
|
||||||
import guru.core.analytics.data.store.EventInfoStore
|
import guru.core.analytics.data.store.EventInfoStore
|
||||||
import guru.core.analytics.handler.AnalyticsCode
|
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.log.PersistentTree
|
||||||
import guru.core.analytics.utils.AndroidUtils
|
import guru.core.analytics.utils.AndroidUtils
|
||||||
import guru.core.analytics.utils.EventChecker
|
import guru.core.analytics.utils.EventChecker
|
||||||
|
import guru.core.analytics.utils.GsonUtil
|
||||||
import guru.core.analytics.utils.SystemProperties
|
import guru.core.analytics.utils.SystemProperties
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -75,24 +79,55 @@ internal class GuruAnalyticsImpl : GuruAnalytics() {
|
||||||
fgEventPeriodInSeconds: Long?,
|
fgEventPeriodInSeconds: Long?,
|
||||||
xAppId: String?,
|
xAppId: String?,
|
||||||
xDeviceInfo: String?,
|
xDeviceInfo: String?,
|
||||||
mainProcess: String?
|
mainProcess: String?,
|
||||||
|
isEnableCronet: Boolean?,
|
||||||
|
uploadIpAddress: List<String>?,
|
||||||
) {
|
) {
|
||||||
if (initialized.compareAndSet(false, true)) {
|
if (initialized.compareAndSet(false, true)) {
|
||||||
delivers.add(EventDispatcher)
|
delivers.add(EventDispatcher)
|
||||||
eventHandlerCallback?.let { EventHandler.INSTANCE.addEventHandler(it) }
|
eventHandlerCallback?.let { EventHandler.INSTANCE.addEventHandler(it) }
|
||||||
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_1)
|
||||||
|
|
||||||
val debugApp = SystemProperties.read("debug.guru.analytics.app")
|
val debugApp = SystemProperties.read("debug.guru.analytics.app")
|
||||||
val forceDebug = debugApp == context.packageName
|
val forceDebug = debugApp == context.packageName
|
||||||
if (forceDebug || persistableLog) {
|
if (forceDebug || persistableLog) {
|
||||||
Timber.plant(PersistentTree(context, debug = debug))
|
Timber.plant(PersistentTree(context, debug = debug))
|
||||||
}
|
}
|
||||||
debugMode = debug
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_2)
|
||||||
Timber.d("[$internalVersion]initialize batchLimit:$batchLimit uploadPeriodInSecond:$uploadPeriodInSeconds startUploadDelayInSecond:$startUploadDelayInSecond eventExpiredInDays:$eventExpiredInDays debug:$debug")
|
|
||||||
|
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)
|
GuruAnalyticsDatabase.initialize(context.applicationContext)
|
||||||
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_3)
|
||||||
|
|
||||||
|
val baseUrl = if (forceDebug && debugUrl.isNotEmpty()) debugUrl else uploadEventBaseUrl
|
||||||
|
|
||||||
DeviceInfoStore.setDeviceInfo(context)
|
DeviceInfoStore.setDeviceInfo(context)
|
||||||
ServiceLocator.setDebug(debug)
|
ServiceLocator.setDebug(debug)
|
||||||
ServiceLocator.addHeaderParam("X-APP-ID", xAppId)
|
ServiceLocator.addHeaderParam("X-APP-ID", xAppId)
|
||||||
ServiceLocator.addHeaderParam("X-DEVICE-INFO", xDeviceInfo)
|
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(
|
engine = EventEngine(
|
||||||
context,
|
context,
|
||||||
|
|
@ -102,10 +137,11 @@ internal class GuruAnalyticsImpl : GuruAnalytics() {
|
||||||
eventExpiredInDays = EventEngine.DEFAULT_EVENT_EXPIRED_IN_DAYS.coerceAtLeast(
|
eventExpiredInDays = EventEngine.DEFAULT_EVENT_EXPIRED_IN_DAYS.coerceAtLeast(
|
||||||
eventExpiredInDays ?: EventEngine.DEFAULT_EVENT_EXPIRED_IN_DAYS
|
eventExpiredInDays ?: EventEngine.DEFAULT_EVENT_EXPIRED_IN_DAYS
|
||||||
),
|
),
|
||||||
uploadEventBaseUrl = uploadEventBaseUrl,
|
uploadEventBaseUri = uploadEventBaseUri
|
||||||
).apply {
|
).apply {
|
||||||
start(startUploadDelayInSecond)
|
|
||||||
delivers.add(this)
|
delivers.add(this)
|
||||||
|
start(startUploadDelayInSecond)
|
||||||
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_7)
|
||||||
}
|
}
|
||||||
if (isInitPeriodicWork) {
|
if (isInitPeriodicWork) {
|
||||||
val process = AndroidUtils.getProcessName(context)
|
val process = AndroidUtils.getProcessName(context)
|
||||||
|
|
@ -120,6 +156,7 @@ internal class GuruAnalyticsImpl : GuruAnalytics() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.INIT_STEP_8)
|
||||||
|
|
||||||
val extMap = mapOf(
|
val extMap = mapOf(
|
||||||
"version_code" to internalVersion,
|
"version_code" to internalVersion,
|
||||||
|
|
@ -127,12 +164,17 @@ internal class GuruAnalyticsImpl : GuruAnalytics() {
|
||||||
"uploadPeriodInSecond" to uploadPeriodInSeconds,
|
"uploadPeriodInSecond" to uploadPeriodInSeconds,
|
||||||
"startUploadDelayInSecond" to startUploadDelayInSecond,
|
"startUploadDelayInSecond" to startUploadDelayInSecond,
|
||||||
"eventExpiredInDays" to eventExpiredInDays,
|
"eventExpiredInDays" to eventExpiredInDays,
|
||||||
|
"uploadEventBaseUri" to uploadEventBaseUri.toString(),
|
||||||
|
"enabledCronet" to (isEnableCronet ?: false),
|
||||||
|
"unloadIpAddress" to (uploadIpAddress?.joinToString("|") ?: ""),
|
||||||
"debug" to debug,
|
"debug" to debug,
|
||||||
)
|
)
|
||||||
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.STATE_INITIALIZED, extMap)
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.STATE_INITIALIZED, extMap)
|
||||||
if (!uploadEventBaseUrl.isNullOrEmpty()) {
|
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)
|
removeProperties(keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUserProperties(callback: (Map<String, String>) -> Unit) {
|
||||||
|
deliverExecutor.execute {
|
||||||
|
callback.invoke(EventInfoStore.properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun peakUserProperties(): Map<String, String> {
|
||||||
|
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) {
|
private fun deliverEvent(item: EventItem, options: AnalyticsOptions) {
|
||||||
Timber.tag("GuruAnalytics").d("deliverEvent ${item.eventName}!")
|
Timber.tag("GuruAnalytics").d("deliverEvent ${item.eventName}!")
|
||||||
deliverExecutor.execute {
|
deliverExecutor.execute {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ object ApiParamUtils {
|
||||||
* 组装接口上传需要的json参数
|
* 组装接口上传需要的json参数
|
||||||
*/
|
*/
|
||||||
fun generateApiParam(events: List<EventEntity>): String {
|
fun generateApiParam(events: List<EventEntity>): String {
|
||||||
|
/// todo: 版本需要根据当前的更新
|
||||||
val deviceInfoJson = GsonUtil.gson.toJson(DeviceInfoStore.deviceInfo)
|
val deviceInfoJson = GsonUtil.gson.toJson(DeviceInfoStore.deviceInfo)
|
||||||
return "{\"version\":${Constants.VERSION},\"events\":[${events.joinToString(",") { it.json }}],\"deviceInfo\":$deviceInfoJson}"
|
return "{\"version\":${Constants.VERSION},\"events\":[${events.joinToString(",") { it.json }}],\"deviceInfo\":$deviceInfoJson}"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ object GZipUtils {
|
||||||
out.toByteArray()
|
out.toByteArray()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_ZIP, e.message)
|
EventHandler.INSTANCE.notifyEventHandler(AnalyticsCode.ERROR_ZIP, e.message)
|
||||||
null
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue