Dans la plupart des cas, la sérialisation dans Android n'est pas nécessaire

TL; DR: Dans la plupart des applications, il est logique de prendre une décision architecturale consciente explicite que, en cas de mort d'un processus, l'application redémarre simplement à partir de zéro sans essayer de restaurer l'état. Et dans ce cas Serializable, Parcelableet d'autres Bundlene sont pas nécessaires.


Si au moins une activité de l'application se situe entre onStart()et onStop(), alors il est garanti que l'activité, et donc le processus dans lequel l'activité vit, sont sûrs. Dans d'autres cas, le systÚme d'exploitation peut tuer le processus de demande à tout moment.


Je n'ai pas eu à implémenter un traitement transparent (c'est-à-dire invisible pour l'utilisateur) de la mort d'un processus dans une application réelle. Mais cela semble tout à fait réalisable, a décrit un exemple de travail: https://github.com/mychka/resurrection .


L'idée est d' onSaveInstanceState()enregistrer l'intégralité de l'état de l'application dans chaque activité et onCreate(), si le processus a été interrompu, de restaurer:


abstract class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        ResurrectionApp.ensureState(savedInstanceState)
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)

        ResurrectionApp.STATE.save(outState)
    }
}

Afin de simuler l'arrĂȘt d'un processus, vous pouvez minimiser l'application et utiliser la commande


adb shell am kill org.resurrection

Si nous dĂ©cidons de gĂ©rer la mort du processus, les frais gĂ©nĂ©raux suivants peuvent ĂȘtre notĂ©s.


  1. Complication du code.


    • , , , . , , - . - : , , .
    • Serializable/Parcelable.
    • — , . , loading == true . , , loading false. TCP-, .
    • , Activity#onCreate() — , — .

    , , , . , , , - .


  2. Activity#onSaveInstanceState() . (, Activity#isChangingConfigurations == true, , .) , , - .


  3. . 1, . / .


  4. . , , , , , - .
    , , : , . , , , .



, . , , foreground . , .


( ) , , .
. (02.03.2020) : Facebook, Twitter, WhatsApp, Chrome, Gmail, Yandex.Maps. : Yandex.Navigator, YouTube, -, Skype.


, , . , , . : , .


https://github.com/mychka/life-from-scratch
BaseActivity . LoginActivity. "NEXT". DashboardActivity. .


adb shell am kill org.lifefromscratch

. , DashboardActivity LoginActivity#loginShownAt, .


. , . , :


abstract class BaseActivity : AppCompatActivity() {

    companion object {
        init {
            if (!appStartedNormally) {
                APP.startActivity(
                    APP.getPackageManager().getLaunchIntentForPackage(
                        APP.getPackageName()
                    )
                );
                System.exit(0)
            }
        }
    }
}

. , -.


. , .


class BinderReference<T>(val value: T?) : Binder()

Parcel . ,


class MyNonSerializableData(val os: OutputStream)

val parcel: Parcel = Parcel.obtain()
val obj = MyNonSerializableData(ByteArrayOutputStream())
parcel.writeStrongBinder(BinderReference(obj))
parcel.setDataPosition(0)
val obj2 = (parcel.readStrongBinder() as BinderReference<*>).value
assert(obj === obj2)

android.os.Binder https://habr.com/ru/post/274635/


. . , . :


const val DEFAULT_BUNDLE_KEY = "com.example.DEFAULT_BUNDLE_KEY.cr5?Yq+&Jr@rnH5j"

val Any?.bundle: Bundle?
    get() = if (this == null) null else Bundle().also { it.putBinder(DEFAULT_BUNDLE_KEY, BinderReference(this)) }

inline fun <reified T> Bundle?.value(): T =
    this?.getBinder(DEFAULT_BUNDLE_KEY)?.let {
        if (it is BinderReference<*>) it.value as T else null
    } as T

inline fun <reified Arg> Fragment.defaultArg() = lazy<Arg>(LazyThreadSafetyMode.NONE) {
    arguments.value()
}

. :


findNavController(R.id.nav_host_fragment).navigate(
    R.id.bbbFragment,
    MyNonSerializableData(ByteArrayOutputStream()).bundle
)


val data: MyNonSerializableData by defaultArg()

— androidx.lifecycle.ViewModel. , , destroy , configuration change, https://developer.android.com/reference/androidx/activity/ComponentActivity.html#onRetainCustomNonConfigurationInstance()


, , , . ViewModel .


BinderReference, androidx.lifecycle.ViewModel, onSaveInstanceState(outState)/onCreate(savedInstanceState). view model .


UPD: kamer Dimezis , , 2.3 (API 9). . (, finish()), configuration change. :
https://stackoverflow.com/questions/7536988/android-app-out-of-memory-issues-tried-everything-and-still-at-a-loss/7576275#7576275
https://stackoverflow.com/questions/11616575/android-not-killing-activities-from-stack-when-memory-is-low/11616829#11616829
:
https://issuetracker.google.com/issues/36934944
Pour ĂȘtre sĂ»r, vous devez plonger dans le code source d'Android. S'il y a une option «Ne pas garder les activitĂ©s», alors il y a un code dans le noyau Android qui peut dĂ©truire «doucement» les activitĂ©s. Avec une forte probabilitĂ©, vous pouvez trouver un scĂ©nario dĂ©licat lorsque ce code est appelĂ© et, par consĂ©quent, androidx.lifecycle.ViewModelentraĂźnera un crash, contrairement onSaveInstanceState(). Mais dans la vraie vie, apparemment, androidx.lifecycle.ViewModelvous pouvez toujours l' utiliser en toute sĂ©curitĂ©.


All Articles