En la mayoría de los casos, no se necesita la serialización en Android

TL; DR: en la mayoría de las aplicaciones, tiene sentido tomar una decisión arquitectónica consciente y explícita de que, en caso de muerte del proceso, la aplicación simplemente se reinicie desde cero sin intentar restaurar el estado. Y en este caso Serializable, Parcelabley otros Bundleno son necesarios.


Si al menos una actividad de la aplicación está entre onStart()y onStop(), entonces se garantiza que la actividad y, por lo tanto, el proceso en el que vive la actividad, son seguros. En otros casos, el sistema operativo puede eliminar el proceso de solicitud en cualquier momento.


No tuve que implementar un procesamiento transparente (es decir, ser invisible para el usuario) de la muerte de un proceso en una aplicación real. Pero parece ser bastante factible, describió un ejemplo de trabajo: https://github.com/mychka/resurrection .


La idea es onSaveInstanceState()guardar el estado completo de la aplicación en cada actividad y onCreate(), si se eliminó el proceso, restaurar:


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

Para simular el cierre de un proceso, puede minimizar la aplicación y usar el comando


adb shell am kill org.resurrection

Si decidimos manejar la muerte del proceso, entonces se pueden observar los siguientes gastos generales.


  1. Complicación del código.


    • , , , . , , - . - : , , .
    • 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
Para decirlo con certeza, debe sumergirse en el código fuente de Android. Si hay una opción "No mantener actividades", entonces hay un código en el kernel de Android que puede destruir "suavemente" las actividades. Con una alta probabilidad, puede encontrar un escenario complicado cuando se llama a este código y, en consecuencia, androidx.lifecycle.ViewModelprovocará un bloqueo, a diferencia onSaveInstanceState(). Pero en la vida real, aparentemente, todavía androidx.lifecycle.ViewModelpuedes usarlo de manera segura.


All Articles