In most cases, serialization in Android is not needed

TL; DR: In most applications, it makes sense to make an explicit conscious architectural decision that, in the event of a process death, the application simply restarts from scratch without trying to restore state. And in this case Serializable, Parcelableand others are Bundlenot needed.


If at least one activity of the application is between onStart()and onStop(), then it is guaranteed that the activity, and therefore the process in which the activity lives, are safe. In other cases, the operating system can kill the application process at any time.


I did not have to implement a transparent (that is, to be invisible to the user) processing of the death of a process in a real application. But it seems to be quite feasible, outlined a working example: https://github.com/mychka/resurrection .


The idea is to onSaveInstanceState()save the entire state of the application in each activity , and onCreate(), if the process was killed, restore:


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

In order to simulate the killing of a process, you can minimize the application and use the command


adb shell am kill org.resurrection

If we decide to handle the death of the process, then the following overhead can be noted.


  1. Code complication.


    • , , , . , , - . - : , , .
    • 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
To say for sure, you need to dive into the source code of Android. If there is an option “Don't keep activities”, then there is a code in the Android kernel that can “gently” destroy activities. With a high probability, you can come up with a tricky scenario when this code is called, and, accordingly, androidx.lifecycle.ViewModelwill lead to a crash, unlike onSaveInstanceState(). But in real life, apparently, androidx.lifecycle.ViewModelyou can still safely use it.


All Articles