Android MainActivity's onCreate() Called Twice on Initial Launch After Installing
Asked Answered
A

5

7

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
            )
        }

    }
}
Antiproton answered 5/9, 2023 at 7:40 Comment(1)
I think better way is drop a little small code into new project until see the bugYolanda
H
2

In my case, I confirmed that this problem only occurs in Android 13 version and that there is a problem with the appcompat version.

androidx.appcompat:appcompat:1.6.1 
-> androidx.appcompat:appcompat:1.5.1

Changing the version solved the problem.

Helsa answered 10/11, 2023 at 6:32 Comment(1)
I've tried many solutions, but only this worked for me. Many thanksMacassar
K
1

Just sharing my two cents - I only see this happening on the first time the debug version is launched. Does not happen on release version.

Tested on:

  • Android 14
  • implementation 'androidx.appcompat:appcompat:1.7.0'

Edit:

I stand corrected - installing release version from internal testing track (the uploaded file was .aab), I do see this issue.

Kesselring answered 9/7 at 6:29 Comment(0)
K
0

I had the same problem, and it seems to be a bug in Android. While this solution did help to some extent, it led to unexpected behavior where sometimes, the code inside onCreate() wasn't executed at all. So, I came up with an alternative workaround:

class SingleOnCreateExecution(context: Context, savedInstanceState: Bundle?, executeCode: () -> Unit) {

init {
    val preferences = context.getSharedPreferences("executeOnce", Context.MODE_PRIVATE)
    val launchCount = preferences.getInt("launchCount", 0)

    when (launchCount) {
        1 -> if (savedInstanceState == null) executeCode() // Second time run only if no saved state
        else -> executeCode() // Runs when launchCount is 0 or after the second time, regardless of saved state
    }

    preferences.edit().putInt("launchCount", launchCount + 1).apply()
}
}

and run it like this:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    SingleExecution(this, savedInstanceState) {
        Log.i("myTag", "onCreate")
        // ... your onCreate() code here
    }
}

This ensures that the check "savedInstanceState == null" is only made on the second launch. As a result, onCreate() runs normally most of the time, minimizing disruptions.

If you're already tracking the app's launchCount in your application, you can use your existing SharedPreferences.

Keikokeil answered 8/9, 2023 at 0:44 Comment(0)
S
0

Used this code - I hope your side working

val intent = Intent(this, EmpDashboardActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP.or(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY.or(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        startActivity(intent) finish()

used in manifest file in splash activity

<activity
        android:name=".housescanify.survey.empSurveyUi.surveyWelcome.WelcomeSurveyActivity"
        android:configChanges="orientation|screenSize"
        android:exported="false"
        android:launchMode="standard"
        android:screenOrientation="portrait"
        tools:ignore="LockedOrientationActivity">
        <meta-data
            android:name="android.app.lib_name"
            android:value="" />
    </activity>
Shakira answered 9/9, 2023 at 9:18 Comment(0)
O
0

Try this: Migrate your splash screen implementation to Android 12 and later

Then try upgrading to Android 14.

Ophthalmia answered 9/6 at 12:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.