Android MainActivity 的 onCreate() 在安装后首次启动时被调用两次

And*_*per 5 android android-manifest android-activity google-play internal-app-sharing

问题:

我遇到一个不寻常的问题,即我的应用程序启动器活动 (MainActivity) 的 onCreate() 方法被调用两次,但仅当我安装该应用程序后首次启动该应用程序时(从 Google Play 内部应用程序共享链接或在设备上安装签名的 apk 文件)。此行为会导致同时创建 MainActivity 的多个实例。

描述:

我已经彻底检查了我的代码,并且可以确认没有意外调用 startActivity 或任何会导致 onCreate() 被多次调用的显式代码。我检查了这个,但这些错误似乎已经过时了

观察结果:

  • 仅当应用程序安装后首次启动时才会出现此问题。
  • 应用程序的后续启动不会表现出此行为;onCreate() 按预期仅调用一次。
  • 该问题似乎与安装后应用程序的首次启动有关。
  • 我正在 Android 13 版本设备中进行测试

代码:

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>
Run Code Online (Sandbox Code Playgroud)

基础应用程序

@FlowPreview
@ExperimentalCoroutinesApi
@HiltAndroidApp
open class BaseApplication : Application()
Run Code Online (Sandbox Code Playgroud)

主要活动

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

}
Run Code Online (Sandbox Code Playgroud)

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

    }
}
Run Code Online (Sandbox Code Playgroud)

Von*_*onC 0

您的启动模式MainActivity未在您的AndroidManifest.xml. 默认启动模式为“ standard”,允许创建任意数量的实例。您可能想尝试将其设置为“ singleTop”,以确保如果新实例已位于堆栈顶部,则不会创建新实例:

供测试用:

<activity
    android:name="xyz.framework.presentation.MainActivity"
    android:launchMode="singleTop"
    android:windowSoftInputMode="adjustResize"
    android:exported="true">
Run Code Online (Sandbox Code Playgroud)

另外,在您的 中MainActivity,您启动RecipeActivity,然后在 2000 毫秒的延迟后,您finish()调用MainActivityMainActivity如果系统或用户在该时间范围内触发重新创建(例如旋转或向后导航),则可能允许重新创建。

finish()尝试在启动后立即调用RecipeActivity,或者更好的是,使用该FLAG_ACTIVITY_NO_HISTORY标志来确保MainActivity不保留在活动堆栈中:

Intent intent = new Intent(this, RecipeActivity.class.java);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
finish();
Run Code Online (Sandbox Code Playgroud)

为了帮助您调试此问题,请使用onCreate()时间戳和其他相关数据记录每次调用,并尝试追溯它被调用两次的原因:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d("MainActivity", "onCreate called at " + System.currentTimeMillis());
    // Rest of the code
}
Run Code Online (Sandbox Code Playgroud)