Merge pull request #2388 from medici/pulseRelatedChanges

Clean up + Mutex Lock
This commit is contained in:
rahul bhat
2022-02-07 18:18:42 +05:30
committed by GitHub Enterprise
11 changed files with 92 additions and 76 deletions

View File

@@ -25,7 +25,6 @@ import com.naviapp.payment.razorpay.RazorpayPayment
import com.naviapp.registration.SplashActivity
import com.naviapp.utils.USER_EXTERNAL_ID
import com.naviapp.utils.getAppName
import java.lang.Exception
/**
* Helper class to initialize and set user data in SDKs
@@ -47,7 +46,9 @@ object NaviSDKHelper {
BuildConfig.VERSION_NAME,
BuildConfig.VERSION_CODE.toString()
),
appsFlyerKey = BuildConfig.APPSFLYER_KEY),
appsFlyerKey = BuildConfig.APPSFLYER_KEY,
flavor = BuildConfig.FLAVOR
),
inAppOptOutScreens
)
HyperSnapSDK.init(

View File

@@ -3,7 +3,8 @@ package com.navi.analytics.model
data class AnalyticsConfiguration(
val appsFlyerKey: String,
val moengageData: MoengageData,
val appInfo: AppInfo
val appInfo: AppInfo,
val flavor:String
)
data class MoengageData(

View File

@@ -40,9 +40,11 @@ object NaviTrackEvent {
.setAppName(analyticsConfiguration.appInfo.appName)
.setAppVersion(analyticsConfiguration.appInfo.appVersionCode)
.setAppVersionName(analyticsConfiguration.appInfo.appVersionName)
.setFlavor(analyticsConfiguration.flavor)
.build()
PulseHelper.init(pulseConfig, appContext)
FirebaseCrashlytics.getInstance().setCustomKey(FIELD_DEVICE_ID, PulseSDKConfig.getDeviceId())
FirebaseCrashlytics.getInstance()
.setCustomKey(FIELD_DEVICE_ID, PulseSDKConfig.getDeviceId())
}
fun startScreen(screenName: String?, isNeededForAppsflyer: Boolean = true) {

View File

@@ -12,8 +12,8 @@ object PulseHelper {
fun init(sdkConfig: PulseSDKConfig.Configuration, context: Context) {
applicationContext = context
PulseRetrofitProvider.init(applicationContext)
PulseSDKConfig.init(context, sdkConfig)
PulseRetrofitProvider.init(applicationContext)
PulseEventManager.startSyncEvents(context)
trackEvent("pulse_sdk_init")
}

View File

@@ -3,6 +3,7 @@ package com.navi.pulse
import android.content.Context
import android.os.Build
import android.provider.Settings
import android.text.TextUtils
import com.navi.pulse.util.PulseConstants
import com.navi.pulse.util.PulseConstants.OS_ANDROID
import com.navi.pulse.util.PulseDeviceUtils
@@ -26,7 +27,8 @@ object PulseSDKConfig {
val appVersion: String,
val appVersionName: String,
val eventBatchSize: Int,
val eventDelayInSeconds: Int
val eventDelayInSeconds: Int,
val flavor: String,
) {
class Builder {
private var appName: String = ""
@@ -34,11 +36,15 @@ object PulseSDKConfig {
private var appVersionName: String = ""
private var eventBatchSize: Int = PulseConstants.DEFAULT_EVENT_BATCH_SIZE
private var eventDelayInSeconds: Int = PulseConstants.DEFAULT_EVENT_DELAY_IN_SECONDS
private var flavor: String = PulseConstants.PROD
fun setAppName(appName: String) = apply { this.appName = appName }
fun setAppVersion(appVersion: String) = apply { this.appVersion = appVersion }
fun setAppVersionName(appVersionName: String) =
apply { this.appVersionName = appVersionName }
fun setFlavor(flavor: String) =
apply { this.flavor = flavor }
fun setEventBatchSize(eventBatchSize: Int) =
apply { this.eventBatchSize = eventBatchSize }
@@ -53,7 +59,8 @@ object PulseSDKConfig {
appVersion,
appVersionName,
eventBatchSize,
eventDelayInSeconds
eventDelayInSeconds,
flavor
)
}
}
@@ -146,4 +153,8 @@ object PulseSDKConfig {
fun getAppVersionName(): String {
return configuration.appVersionName
}
fun isProd(): Boolean {
return TextUtils.equals(configuration.flavor, PulseConstants.PROD)
}
}

View File

@@ -21,6 +21,8 @@ import com.navi.pulse.util.PulseUtils
import com.navi.pulse.worker.SyncEventsWorker
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.lang.reflect.Type
import java.util.*
import java.util.concurrent.Executors
@@ -31,6 +33,8 @@ import kotlin.concurrent.fixedRateTimer
object PulseEventManager {
private val coroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private var timer: Timer? = null
private val pulseNetworkRepository = PulseNetworkRepository()
private val mutex = Mutex()
fun trackEvent(
eventName: String,
@@ -53,69 +57,67 @@ object PulseEventManager {
PulseSDKConfig.getEventsDelayInMilliseconds()
) {
runBlocking {
AddEventTask.resetEventCount()
sendEventsToServer(applicationContext, TIMER_THREAD_NAME)
}
}
startPeriodicSyncTask(applicationContext)
}
@Synchronized
suspend fun sendEventsToServer(applicationContext: Context, source:String?): Boolean {
PulseLogger.log(
LOG_TAG,
"Sync started !"
)
PulseLogger.log(
LOG_TAG,
"in sendEventsToServer Thread Name " + Thread.currentThread().name
)
val db = PulseDatabaseHelper.getPulseDatabase(applicationContext)
val pulseDAO = db.pulseDao()
val pulseEvents = pulseDAO.fetchEvents(PulseSDKConfig.getEventBatchSize())
if (!pulseEvents.isNullOrEmpty()) {
try {
val detailsList = pulseEvents.map { it.details }
val listType: Type = object : TypeToken<ArrayList<EventData?>?>() {}.type
val events: ArrayList<EventData> =
Gson().fromJson(detailsList.toString(), listType)
val pulseRequest =
PulseRequest(
events = events,
batchTimeStamp = System.currentTimeMillis().toString(),
source = source
suspend fun sendEventsToServer(applicationContext: Context, source: String?): Boolean {
mutex.withLock {
PulseLogger.log(
LOG_TAG,
"Sync started !"
)
PulseLogger.log(
LOG_TAG,
"in sendEventsToServer Thread Name " + Thread.currentThread().name
)
val db = PulseDatabaseHelper.getPulseDatabase(applicationContext)
val pulseDAO = db.pulseDao()
val pulseEvents = pulseDAO.fetchEvents(PulseSDKConfig.getEventBatchSize())
if (!pulseEvents.isNullOrEmpty()) {
try {
val detailsList = pulseEvents.map { it.details }
val listType: Type = object : TypeToken<ArrayList<EventData?>?>() {}.type
val events: ArrayList<EventData> =
Gson().fromJson(detailsList.toString(), listType)
val pulseRequest =
PulseRequest(
events = events,
batchTimeStamp = System.currentTimeMillis().toString(),
source = source
)
PulseLogger.log(LOG_TAG, "Event payload ready")
val response = pulseNetworkRepository.sendEvents(
PulseSDKConfig.getPostUrl(),
pulseRequest
)
PulseLogger.log(LOG_TAG, "Event payload ready")
val response = PulseNetworkRepository().sendEvents(
PulseSDKConfig.getPostUrl(),
pulseRequest
)
PulseLogger.log(LOG_TAG, pulseRequest.toString())
PulseLogger.log(LOG_TAG, "Event Batch Size -> " + pulseEvents.size)
return if (response.isSuccessful && response.body()?.code == CODE_API_SUCCESS) {
pulseDAO.deleteEvents(pulseEvents.map { it.eventId })
PulseLogger.log(LOG_TAG, "Events Batch Sent and Deleted from DB")
true
} else {
PulseLogger.log(
LOG_TAG,
"Events Batch Failed ! Reason - " + response.body()?.message
)
false
PulseLogger.log(LOG_TAG, pulseRequest.toString())
PulseLogger.log(LOG_TAG, "Event Batch Size -> " + pulseEvents.size)
return if (response.isSuccessful && response.body()?.code == CODE_API_SUCCESS) {
pulseDAO.deleteEvents(pulseEvents.map { it.eventId })
PulseLogger.log(LOG_TAG, "Events Batch Sent and Deleted from DB")
true
} else {
PulseLogger.log(
LOG_TAG,
"Events Batch Failed ! Reason - " + response.body()?.message
)
false
}
} catch (e: Exception) {
PulseLogger.log(LOG_TAG, "Error in Sending Data" + e.message)
return false
}
} catch (e: Exception) {
PulseLogger.log(LOG_TAG, "Error in Sending Data" + e.message)
} else {
PulseLogger.log(LOG_TAG, "No More Events to Send !")
return false
}
} else {
PulseLogger.log(LOG_TAG, "No More Events to Send !")
return false
}
}
fun isSyncTimerRunning(): Boolean {
return timer != null
}
private fun startPeriodicSyncTask(
applicationContext: Context
) {

View File

@@ -1,7 +1,7 @@
package com.navi.pulse.network
import android.content.Context
import com.navi.pulse.BuildConfig
import com.navi.pulse.PulseSDKConfig
import com.readystatesoftware.chuck.ChuckInterceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
@@ -29,7 +29,7 @@ object PulseRetrofitProvider {
}
private fun getRetrofit(): Retrofit {
val baseUrl = if (BuildConfig.DEBUG) BASE_URL_DEBUG else BASE_URL_PROD
val baseUrl = if (PulseSDKConfig.isProd()) BASE_URL_PROD else BASE_URL_DEBUG
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())

View File

@@ -16,11 +16,14 @@ class AddEventTask(
) : PulseTask {
companion object {
private var eventCount: AtomicInteger = AtomicInteger(0)
fun resetEventCount() {
eventCount = AtomicInteger(0)
}
}
@WorkerThread
override fun execute(): Boolean {
val db = PulseDatabaseHelper.getPulseDatabase(context)
val db = PulseDatabaseHelper.getPulseDatabase(context)
val pulseDAO = db.pulseDao()
return try {
pulseDAO.insertEvent(event)
@@ -37,7 +40,11 @@ class AddEventTask(
private fun checkIfSyncIsNeeded() {
if (eventCount.get() >= PulseSDKConfig.getEventBatchSize()) {
eventCount = AtomicInteger(0)
PulseLogger.log(
PulseConstants.LOG_TAG,
"Event Count greater than Threshold - " + eventCount.get()
)
resetEventCount()
PulseLogger.log(
PulseConstants.LOG_TAG,
"Inside checkIfSyncIsNeeded - ".plus(Thread.currentThread().name)

View File

@@ -23,6 +23,6 @@ class SyncEventTask(private val context: Context) : PulseTask {
}
override fun getTaskName(): String {
return PulseConstants.SYNC_EVENT_TASK
return SYNC_EVENT_TASK
}
}

View File

@@ -16,4 +16,5 @@ object PulseConstants {
const val REPEAT_INTERVAL_WORKER = 6L
const val EVENT_DB_NAME = "navi-pulse"
const val FIELD_DEVICE_ID = "device_id"
const val PROD = "prod"
}

View File

@@ -4,25 +4,16 @@ import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.navi.pulse.event.PulseEventManager
import com.navi.pulse.util.PulseConstants
import com.navi.pulse.util.PulseConstants.SYNC_EVENTS_WORKER
import com.navi.pulse.util.PulseLogger
/**
* This Worker's only job is to wake up the app,
* which in turn wakes up the sync process @see PulseEventManager.startSyncEvents
*/
class SyncEventsWorker(val context: Context, workerParameters: WorkerParameters) :
CoroutineWorker(context, workerParameters) {
override suspend fun doWork(): Result {
PulseEventManager.trackEvent("heartbeat", null, context)
if (PulseEventManager.isSyncTimerRunning()) {
PulseLogger.log(PulseConstants.LOG_TAG, "Cancelling Worker as Timer is already running")
return Result.failure()
}
// This code doesn't work as Worker starts the Sync Timer and above check returns from worker.
PulseEventManager.trackEvent("worker_worked", null, context)
PulseLogger.log(PulseConstants.LOG_TAG, "In doWork() of SyncEventsWorker")
PulseLogger.log(PulseConstants.LOG_TAG, Thread.currentThread().name)
val eventsSent = PulseEventManager.sendEventsToServer(context, SYNC_EVENTS_WORKER)
return if (eventsSent) Result.success()
else Result.failure()
return Result.success()
}
}