Problem:
I'm encountering an unusual issue where the onCreate() method of my app's launcher activity (MainActivity) is being called twice, but only when I launch the app for the first time after installing it(from a Google Play Internal App Sharing link or installing signed apk file on device). This behavior results in multiple instances of MainActivity being created simultaneously.
Description:
I've thoroughly reviewed my code and can confirm that there are no accidental calls to startActivity or any explicit code that should cause onCreate() to be called more than once. I checked this but those bugs seems to be obsolete
Observations:
- This issue occurs only when the app is launched for the first time after installing.
- Subsequent launches of the app do not exhibit this behavior; onCreate() is called only once as expected.
- The problem seems to be related to the initial launch of the app after installation.
- I am testing in an Android 13 version device
Code:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xyz">
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Required to maintain app compatibility. -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:name=".framework.presentation.BaseApplication"
android:allowBackup="false"
android:fullBackupOnly="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="${enableCrashReporting}" />
<activity
android:name="xyz.framework.presentation.MainActivity"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="xyz.framework.presentation.main.RecipeActivity"
android:windowSoftInputMode="adjustResize"
android:exported="true" />
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:theme="@style/com_facebook_activity_theme"
android:exported="false" />
<activity
android:name="com.facebook.CustomTabMainActivity"
android:exported="false" />
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity"
android:theme="@android:style/Theme"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
android:theme="@android:style/Theme"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity"
android:theme="@android:style/Theme.Dialog"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
</application>
</manifest>
BaseApplication
@FlowPreview
@ExperimentalCoroutinesApi
@HiltAndroidApp
open class BaseApplication : Application()
MainActivity
@AndroidEntryPoint
class MainActivity : AppCompatActivity(), UIController {
@Inject
lateinit var editor: SharedPreferences.Editor
@Inject
lateinit var sharedPreferences: SharedPreferences
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: SplashViewModel by viewModels {
viewModelFactory
}
private val signInLauncher = registerForActivityResult(
FirebaseAuthUIActivityResultContract()
) { res ->
this.onSignInResult(res)
}
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Toast.makeText(this, "oncreate called", Toast.LENGTH_SHORT).show()
setContent {
SplashProvider()
}
viewModel.hasSyncBeenExecuted()
.observe(this) { hasSyncBeenExecuted ->
if (hasSyncBeenExecuted) {
startActivity(Intent(this, RecipeActivity::class.java))
// Use lifecycleScope for the coroutine
lifecycleScope.launch {
delay(2000) // Adjust the delay time as needed
finish()
}
}
}
if (sharedPreferences?.getString(
PreferenceKeys.USER_UID,
null
) != null && FirebaseAuth.getInstance().currentUser != null
) {
viewModel.syncCacheWithNetwork()
} else {
createSignInIntent()
}
}
private fun createSignInIntent() {
// [START auth_fui_create_intent]
// Choose authentication providers
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build()
)
Toast.makeText(this, "createSignInIntent called", Toast.LENGTH_SHORT).show()
// Create and launch sign-in intent
val signInIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setTheme(R.style.LoginTheme)
.setLogo(R.drawable.handshake) // Set logo drawable
.build()
signInLauncher.launch(signInIntent)
// [END auth_fui_create_intent]
}
override fun hideSoftKeyboard() {
if (currentFocus != null) {
val inputMethodManager = getSystemService(
Context.INPUT_METHOD_SERVICE
) as InputMethodManager
inputMethodManager
.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)
}
}
private fun onSignInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == AppCompatActivity.RESULT_OK) {
// Successfully signed in
val user = FirebaseAuth.getInstance().currentUser
// ...
editor?.putString(PreferenceKeys.USER_UID, user?.uid)
editor?.apply()
viewModel.syncCacheWithNetwork()
} else if (response != null) {
Toast.makeText(
this,
response.error?.errorCode.toString().plus(response.error?.message),
Toast.LENGTH_LONG
).show()
} else {
finish()
}
}
}
SplashProvider
@Composable
fun SplashProvider(
) {
val images = listOf(R.drawable.splash1, R.drawable.splash2, R.drawable.splash3)
val imageIndex = Random.nextInt(images.size)
Splash(images[imageIndex])
}
@Composable
fun Splash(img: Int) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Image(
contentDescription = "Recipe",
painter = painterResource(img),
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
Column(
modifier = Modifier
.wrapContentSize()
.clip(CircleShape)
.background(Color.Black.copy(alpha = 0.6f))
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
CircularProgressIndicator(
modifier = Modifier.size(50.dp),
strokeWidth = 4.dp,
color = Color.White
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Loading...",
color = Color.White,
fontSize = 16.sp
)
}
}
}