Reputation: 1912
I wanted to make a method that determine if the application is started for the very first time, no matter the current version of the application. People suggest that we should use SharedPreferences
as seen from this qustion. Below is the function that determine if application is started for the very first time.
companion object {
const val APP_LAUNCH_FIRST_TIME: Int = 0 // first start ever
const val APP_LAUNCH_FIRST_TIME_VERSION: Int = 1 // first start in this version (when app is updated)
const val APP_LAUNCH_NORMAL: Int = 2 // normal app start
/**
* Method that checks if the application is started for the very first time, or for the first time
* of the updated version, or just normal start.
*/
fun checkForFirstAppStart(context: Context): Int {
val sharedPreferencesVersionTag = "last_app_version"
val sharedPreferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(context)
var appStart = APP_LAUNCH_NORMAL
try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val lastVersionCode = sharedPreferences.getLong(sharedPreferencesVersionTag, -1L)
val currentVersionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
appStart = when {
lastVersionCode == -1L -> APP_LAUNCH_FIRST_TIME
lastVersionCode < currentVersionCode -> APP_LAUNCH_FIRST_TIME_VERSION
lastVersionCode > currentVersionCode -> APP_LAUNCH_NORMAL
else -> APP_LAUNCH_NORMAL
}
// Update version in preferences
sharedPreferences.edit().putLong(sharedPreferencesVersionTag, currentVersionCode).commit()
} catch (e: PackageManager.NameNotFoundException) {
// Unable to determine current app version from package manager. Defensively assuming normal app start
}
return appStart
}
}
Now in my MainActivity I make the check in this way, but strangely enough I always end up inside the if statement, although appLaunch is different from MainActivityHelper.APP_LAUNCH_FIRST_TIME
val appLaunch = MainActivityHelper.checkForFirstAppStart(this)
if (appLaunch == MainActivityHelper.APP_LAUNCH_FIRST_TIME) {
val c = 299_792_458L
}
Here we see that appLaunch is 2
Here we see that MainActivityHelper.APP_LAUNCH_FIRST_TIME is 0
I am in the main thread I check using Thread.currentThread()
, and when I add watches in the debugger (appLaunch == MainActivityHelper.APP_LAUNCH_FIRST_TIME
) I get false.
So I suggest that there is some delay, and by the time the if check is made the result is changed?
Upvotes: 0
Views: 89
Reputation: 28856
There's nothing wrong with the code. I tested it and it works as intended. I get all three return values depending on the circumstances. I simplified the code a bit but the original code should nevertheless works.
enum class AppLaunch {
LAUNCH_FIRST_TIME, // first start ever
FIRST_TIME_VERSION, // first start in this version (when app is updated)
NORMAL // normal app start
}
/**
* Method that checks if the application is started for the very first time, or for the first time
* of the updated version, or just normal start.
*/
fun checkForFirstAppStart(context: Context): AppLaunch {
val sharedPreferencesVersionTag = "last_app_version"
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
return try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val lastVersionCode = sharedPreferences.getLong(sharedPreferencesVersionTag, -1L)
val currentVersionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
// Update version in preferences
sharedPreferences.edit().putLong(sharedPreferencesVersionTag, currentVersionCode).commit()
when (lastVersionCode) {
-1L -> AppLaunch.LAUNCH_FIRST_TIME
in 0L until currentVersionCode -> AppLaunch.FIRST_TIME_VERSION
else -> AppLaunch.NORMAL
}
} catch (e: PackageManager.NameNotFoundException) {
// Unable to determine current app version from package manager. Defensively assuming normal app start
AppLaunch.NORMAL
}
}
I experimented a bit and the issue you see looks like a bug in Android Studio. If the code in the if statement is a NOP (no operation) then the debugger seems to stop there. If the code does have a side effect, the debugger doesn't stop. Things like this can be infuriating but with Android, Android Studio and the tooling, bugs like this are pretty common (unfortunately).
if (appLaunch == APP_LAUNCH_FIRST_TIME) {
val c = 299_792_458L
}
translates to the following byte code:
L3 (the if statement)
LINENUMBER 32 L3
ILOAD 4
IFNE L4
L5
LINENUMBER 33 L5
LDC 299792458
LSTORE 2
Converting c
to a var
var c = 1L
if (appLaunch == APP_LAUNCH_FIRST_TIME) {
c = 299_792_458L
}
results in identical byte code so it's certainly not a code problem but an issue with Android Studio.
Update
If you need fast writes with enums you can use something like this:
fun appLaunchById(id: Int, def: AppLaunch = AppLaunch.NORMAL) = AppLaunch.values().find { it.id == id } ?: def
enum class AppLaunch(val id: Int) {
LAUNCH_FIRST_TIME(0), // first start ever
FIRST_TIME_VERSION(1), // first start in this version (when app is updated)
NORMAL(2); // normal app start
}
^^^ writes an Int so fast and short. Reading is certainly not super fast though.
Update 2
Generic version of the enum solution:
inline fun <reified T : Enum<*>> enumById(hash: Int, def: T) = enumValues<T>()
.find { it.hashCode() == hash }
?: def
enum class AppLaunch {
LAUNCH_FIRST_TIME, // first start ever
FIRST_TIME_VERSION, // first start in this version (when app is updated)
NORMAL // normal app start
}
Usage:
val enum = enumById(value.hashCode(), AppLaunch.NORMAL)
Upvotes: 1