Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

droidcon News
Falsehoods Android Developers Believe about Lifecycles
Speaker: Andrew Bailey Source: droidcon Online 2020 Hand-On & In-Depth Series Webinar (Part 3 of 4)
Transcript
en-us
00:00
so we can't talk about android without
00:02
talking about life cycles this
00:04
feels like a pretty obligatory thing in
00:06
our world
00:07
but i want to frame a little bit of a
00:09
different conversation about
00:11
life cycles because as software
00:13
developers it's sort of our job to make
00:15
assumptions
00:16
about our world we make assumptions
00:18
about what our
00:20
customers need and what our product
00:22
managers really want us to do
00:24
and we also make assumptions about how
00:25
we can model things so that they're easy
00:27
to digest
00:28
if we don't make these sort of
00:29
assumptions it's really impossible to
00:30
get any meaningful code written
00:32
whatsoever
00:34
the problem comes in where we make
00:36
assumptions about our frameworks and
00:37
about our platforms that don't hold up
00:40
because when we do things like this at
00:41
least to really obscure crashes and
00:43
subtle bugs that you may not even know
00:45
about
00:46
and i'm going to talk specifically about
00:49
life cycle related false assumptions
00:51
and depending on what sort of experience
00:53
you might have
00:54
you may be hearing me talk about these
00:56
and think that these are really
00:57
basic assumptions that are incorrect to
00:59
make i promise that more people are
01:01
making these
01:02
than you would think i work at big nerd
01:05
ranch and
01:06
you may think of us as more of a
01:07
training company but on a day-to-day
01:09
basis we do consulting
01:11
and we see a lot of projects that come
01:13
in and a lot of projects with a
01:14
surprising number of users are making
01:16
these sort of mistakes
01:18
so we're going to dig right into it and
01:19
we need to start at the very basics when
01:22
you worked on your first
01:23
android application it might have looked
01:26
something
01:26
like this so here's a really basic
01:28
counter application that i made
01:30
uh you've got an increment and a
01:32
decrement button and you can just count
01:34
some number of things that are happening
01:36
and this is all great but what you
01:37
probably noticed when you were working
01:39
on your first app
01:39
was when you rotate the phone this state
01:42
disappears
01:43
and if you're not expecting this this is
01:45
a really confusing experience to get
01:48
so the next steps are you probably did
01:50
some or all of these
01:52
but really the the uh the positive here
01:56
the the best case scenario is that you
01:57
end up going down the path of
01:59
implementing on save instance state
02:01
you go through all the song and dance of
02:03
writing that value
02:04
that counter uh incremented value into a
02:07
bundle
02:08
restore it out do all that song and
02:10
dance and you realize that these are a
02:12
lot of steps to go through
02:13
and you realize that this is a lot of
02:16
things to keep track of if you're
02:17
working on
02:18
some really big application and then
02:20
eventually you do start working on some
02:22
really big application
02:24
and one thing that you're very likely to
02:26
run into especially if you have a
02:27
designer is
02:28
they might ask you we don't want to
02:30
support landscape that's not really
02:32
something that we can design for
02:33
so can you lock the app to portrait and
02:36
when you hear this
02:38
you might have this reaction and
02:41
you may not notice that you've made an
02:43
incorrect assumption here
02:45
the assumption that you've made is that
02:46
the only reason that you'll lose state
02:48
in the only reason that your activity
02:49
restarts
02:50
is because of changing from portrait to
02:52
landscape
02:53
this is incorrect but we'll talk about
02:55
that more in just a second we haven't
02:57
gotten there yet
02:58
right now you're going into your android
03:00
manifest you're setting the screen
03:02
orientation to portrait you're adding a
03:04
lint suppression
03:05
um and then there's no orientation
03:08
changes anymore
03:09
so you don't have to worry about your
03:10
state being lost on a configuration
03:12
change
03:14
then somebody else on your team is going
03:16
to mention well
03:17
what about multi window are we doing
03:19
anything to support that
03:21
and you're not really sure how this
03:22
works so you go into your android
03:24
emulator and you enter multi window
03:27
and your state goes away so you do some
03:29
more research
03:30
and the really easy option is to turn
03:32
off multi-window support
03:34
and you can do that and it'll get you by
03:37
really quickly but you can sort of tell
03:38
that it's
03:39
not really the best goal and you've made
03:42
another assumption here
03:43
which is that there's a really small
03:45
number of reasons that your activity
03:47
that can be recreated and multi-window
03:50
is just
03:50
another one of them but you still might
03:53
not know this yet so going back into
03:54
your android manifest you
03:56
set resizable activity to false and now
03:58
you don't have to worry about
03:59
multi-window
04:00
anymore then somebody like your product
04:03
manager comes along and they say
04:05
well what about that samsung foldable
04:06
phone the really fancy and expensive one
04:09
are we doing anything to support that
04:11
and you might not know either so you
04:13
look up and you find that you can spin
04:15
up a foldable phone emulator you
04:17
run your app on the foldable phone and
04:19
everything looks great so far
04:21
but then you unfold the phone and
04:23
something really unexpected happens
04:26
because your app still has its state but
04:29
it's got all these black borders around
04:30
it it's kept the same
04:32
size that it had before this is a
04:34
compatibility mode that the system is
04:36
imposing on your app because you've set
04:38
resizable activity defaults
04:40
and you can see that there's a banner
04:41
that says you can make this app full
04:43
screen but you'll
04:44
have to restart it and if you click that
04:46
button then your state goes away
04:48
which is the exact same problem that
04:50
we've been seeing over and over again
04:52
so you do some more research and
04:54
depending on where you end up
04:55
you might see a stack overflow thread
04:57
that's guiding you to use the android
04:59
config changes property in your android
05:01
manifest
05:02
if you don't know really what's
05:04
happening here then you're actually
05:06
making two assumptions here
05:08
the first assumption is that config
05:10
changes is a really cheap way of
05:11
properly handling configuration changes
05:13
we'll get back to this in a second
05:16
the second assumption that you're making
05:17
here is that there is a way that you can
05:19
reliably prevent your activity from
05:21
restarting that's what we've been seeing
05:23
this whole time
05:24
both of these are incorrect but if you
05:26
didn't want to make this change in your
05:28
android manifest you can get rid of a
05:30
lot of the code from before you can add
05:32
this config changes slide here
05:34
even though you're specifying a lot of
05:35
things here you no longer have any limit
05:37
suppressions which it's
05:38
nice but this is overlooking a lot of
05:40
details and to dive more into this we
05:42
need to see all of the reasons that your
05:44
activity can restart
05:46
so far we've been talking about this
05:48
first group which falls into the bucket
05:50
of configuration changes
05:51
and more specifically we've been talking
05:53
about these two orientation and window
05:55
size which are
05:56
sort of the same thing if you think
05:58
about it from a really high level
05:60
but there's a lot of reasons that your
06:01
window can resize it's things like
06:03
multi-window it's foldable phones if
06:05
you're running on a chrome os device
06:06
then
06:07
you can run in a window and the user can
06:09
literally resize that window
06:11
but there's a lot of other types of
06:12
configuration changes that your activity
06:14
can restart from
06:15
a few other ones are language so if the
06:18
user changes their language then your
06:20
app
06:20
will restart or you'll get a
06:22
configuration change and the activity
06:23
will be recreated
06:25
you can also change the device and font
06:26
scaling factor to make things on screen
06:28
bigger or smaller
06:30
and the user can also change their sim
06:31
cards and depending on which version of
06:33
android you're running on that might
06:34
also
06:35
can trigger a configuration change and
06:36
there's more than these
06:38
so you can add these to that android
06:41
manifest config changes property
06:43
the downside here is that really the
06:45
goal of the config
06:46
changes attribute in xml is you're
06:48
telling the framework that you're going
06:50
to handle these types of configuration
06:52
changes
06:53
so if you didn't have alternative layout
06:55
resources for portrait or landscape or
06:57
different window sizes
06:58
you might not have noticed that
06:59
something unusual was happening but if
07:01
you have translations for your
07:02
application
07:03
and you put language inside of config
07:05
changes you'll notice that when the user
07:07
changes their language
07:08
all of the text inside of your
07:10
application will still be in the old
07:11
language and locale
07:13
so it won't be until you fetch resources
07:15
again that you start seeing
07:17
things in the updated language and
07:19
really you're supposed to go through and
07:21
manually set those strings yourself
07:23
which is
07:24
very complicated and that's why this
07:26
isn't recommended
07:27
and you're supposed to do that for the
07:28
other types of configuration changes as
07:30
well
07:32
there's also night mode which is a
07:34
little bit of an outlier here
07:36
night mode is special because it's
07:37
implemented a little bit by the
07:39
framework and a little bit by the
07:40
support library
07:41
and with nightmare in particular there
07:43
isn't a way to add it to the config
07:45
changes property it's not a supported
07:47
value
07:47
so if you do want to support night mode
07:49
and you do want to use the constructs
07:50
that the platform has provided to you
07:52
you don't really have a choice you have
07:54
to support configuration changes
07:57
and the really recommended way that you
07:59
do that is to use view model and at this
08:01
point
08:01
you're probably talking your platform
08:03
lead into doing this
08:05
and if you're using view model you
08:06
handle all of these situations in one
08:09
fell swoop
08:10
this is really recommended from an
08:11
architectural perspective it also
08:13
handles the case where you can
08:14
programmatically trigger a restart of
08:16
your activity manually
08:17
all of those will work but there's
08:20
another situation
08:22
if you just handle these you get most of
08:24
the activity recreations for you
08:26
those get handled but if you listen to
08:29
reports from your customers and
08:30
possibly from the google play store you
08:33
might hear reports of this
08:35
so you might hear users saying i was
08:37
doing something really important in the
08:39
application and then i went to look
08:40
something up
08:41
and by the time i came back all this
08:43
state was lost and i've been working on
08:44
that for hours so what
08:46
happened and that's because there's
08:48
another reason that the activity can be
08:50
recreated and there's a whole other
08:52
world to what's happening here
08:53
because there are scenarios where your
08:55
view models singletons and static
08:56
variables can all get wiped out and
08:58
re-initialized even if the user doesn't
08:60
leave your application
09:01
and that takes us into another category
09:03
of activity restarts but this which is
09:05
process eviction
09:06
and these behave a little bit
09:07
differently so to dive into this there's
09:10
a few reasons that android
09:12
will recreate your process uh it does
09:15
these the most well known one is low
09:16
memory
09:17
uh but the goal here is that android
09:19
should be able to kick your process from
09:21
memory
09:22
and it should be able to restore it so
09:24
that if the user does want to come back
09:25
to it they can do that hopefully without
09:27
noticing that anything has happened
09:30
there might be some more delay when
09:31
you're back when your application is
09:34
reloading and bringing everything into
09:35
memory but you should be able to return
09:38
to that
09:38
old state and the other thing about this
09:40
is when android kills your process it's
09:42
doing so very forcibly it's not going to
09:44
call on destroy for you it's just going
09:46
to do it
09:48
the primary way that we know that
09:50
android will do this
09:51
is with something called the viking
09:52
killer by a few people at google and
09:54
also by me
09:55
because that's a fantastic name more
09:57
formally it's known as
09:58
lmkd which is the low memory killer
10:00
demon the behavior varies device to
10:02
device but
10:03
basically the way that it works is if
10:05
there's significant memory pressure so
10:07
you're running like google chrome in the
10:08
video game or something
10:10
then eventually the system is going to
10:11
need to reclaim memory to keep things
10:14
working and what it'll do is it'll go
10:16
through applications that are in the
10:17
background it'll find ones that are
10:19
least recently used and it will evict
10:21
them out of memory and
10:23
keep their states so what that means is
10:25
if you
10:26
wrote to your onsave instance 8 bundle
10:28
that bundle will get persisted and
10:31
android will use that to recreate your
10:32
process later
10:34
and hopefully users don't know that this
10:36
is happening at all
10:38
the less common reason that your process
10:41
can restart actually relates to
10:42
permissions
10:43
because we have callbacks in place to
10:46
tell when our application is granted
10:47
permissions we
10:48
need to do those to show that dialogue
10:50
and respond to those results
10:52
but we don't have a callback for when a
10:53
user revokes permissions
10:55
the way that android actually implements
10:57
that is to kill the process and then
10:58
recreate it
10:59
because when we create it we have to go
11:01
and check all of those permissions again
11:04
um and that's the only really guaranteed
11:06
way that the framework has to make sure
11:07
that we're not doing anything
11:09
badly in practice this does behave the
11:11
exact same way as the viking killer
11:14
but it does also mean that you might be
11:16
making an incorrect assumption
11:17
that just because you checked for
11:19
permissions before launching an activity
11:21
or fragment
11:22
means that when the fragment or activity
11:24
is started or created that it has those
11:26
permissions
11:27
that might not be accurate if you've
11:29
revoked a permission
11:30
while you're on that screen
11:33
another thing that i do want to call out
11:35
is that there is a four stop button
11:37
on the app info page for every
11:39
application
11:40
and this has nothing to do with anything
11:42
that i've just talked about it's
11:43
completely different
11:45
the way that this button works is if the
11:46
application is running it will evict the
11:48
process immediately
11:50
but it also throws out all the savings
11:51
and state bundle so
11:53
you can't use this as a way to test how
11:55
your process
11:56
deaths are working so this raises the
11:59
obvious question
11:60
of if that button doesn't work then how
12:02
do i test this
12:04
and there's two really good ways of
12:05
testing this
12:07
one is you can do this with android
12:09
studio so if you run your app in android
12:11
studio
12:12
you can leave the application hit the
12:14
stop button and then reopen it on the
12:16
device and that triggers the same
12:17
scenarios
12:18
there is also a library called venom
12:20
which i did not make it will do the
12:22
exact same thing and it will give you a
12:24
notification
12:24
to do this if you're using
12:28
viewmodel the way that you can handle
12:29
this is to implement the save state
12:31
library
12:32
so at this point if this is an issue to
12:34
you you will probably want to start
12:36
talking your team
12:37
to using this and at this point your
12:39
android manifest
12:40
only has one really attribute for your
12:43
activity tag which is really clean
12:45
and the next uh the rest of your changes
12:47
are going to take place in your view
12:48
model
12:49
so if you have this counter view model
12:51
from before then
12:52
you'll update it to look something like
12:54
this so the changes here are you'll take
12:56
a save state handle into the constructor
12:58
of the view model
12:59
and then whenever you modify the counter
13:01
value you will write that to the save
13:03
state handle as well as mutating
13:05
whatever live data you might have
13:06
associated with it
13:08
and then when you want to initialize
13:09
things you can either read the value as
13:10
it was last saved
13:12
or you can create a live data that has
13:14
that value by using the get live data
13:16
function on the save state handle
13:19
if you don't want to use viewmodel or
13:21
save state library i do want to call out
13:23
that you can also use onsave
13:25
instancedate and this will also work
13:27
perfectly fine so if you have stronger
13:29
opinions about
13:30
architecture and you don't want to use
13:31
those libraries you can do that too
13:33
it will work just fine and at this point
13:36
we are handling everything now and
13:39
depending on where you are
13:40
you might have a lot of things that you
13:42
need to update if you're concerned about
13:44
these things
13:45
and at this point you might be thinking
13:47
well okay at least you're not going to
13:48
tell me anything
13:49
drastic like this is all doable but it's
13:51
so loud
13:53
and i have some bad news because you
13:56
also might be making assumptions about
13:57
your initialization logic
13:59
executing as you expect and this doesn't
14:02
always work and there's two ways that
14:04
i'm going to talk about this
14:06
the first one is your splash screen you
14:09
might have an activity that looks
14:10
something like this so when you launch
14:12
the application
14:13
you can show some screen that has a logo
14:15
associated with your application
14:17
and you can do whatever pre-fetching
14:19
work that you might need to do before
14:20
you can actually launch your ui
14:22
you'll then launch the activity for
14:24
whatever your main activity is to show
14:26
all the content
14:27
and then you don't want users getting
14:28
back to that splash activity
14:30
so you'll call finish on it to get it
14:32
out of the back stack
14:33
and at this point the initialization has
14:35
worked perfectly fine all of our data is
14:37
where it needs to be
14:38
the problem comes when we're being
14:40
restored from process depth
14:42
because when android is restoring our
14:44
application from process depth it only
14:46
restores activities that are on the back
14:48
stack
14:48
and because the splash screen isn't
14:50
there it means that any initialization
14:51
logic that we did there
14:53
has not happened this time and if you're
14:55
doing things like setting up singletons
14:57
or even like setting up dagger for
14:59
example in your splash screen for some
15:00
reason
15:01
then this can lead to a lot of
15:02
exceptions because your application
15:04
isn't in an initialized state that you
15:06
expect it to be
15:08
there's also another edge case that is
15:10
very bad
15:11
let's talk about your application class
15:14
a lot of people have initialization
15:16
logic especially for dagger
15:17
living in your oncreate method of an
15:19
application class
15:20
and there is an edge case very rare but
15:24
it can happen
15:25
where this never gets called if you look
15:28
in your bug tracker
15:29
you might actually see an exception
15:31
where your application can't be cast to
15:33
whatever type you specify
15:35
and if you're running into debugger and
15:36
when this happened you would actually
15:37
see that your application class isn't uh
15:40
isn't an instance of whatever
15:41
application class you specified it as
15:44
and this is going to be a really insane
15:46
thing to see and it seems very
15:48
impossible so the first thing that
15:50
you'll do of course is you'll go into
15:51
your android manifest and you'll say
15:53
no i did specify that the application
15:56
should be this class
15:57
and we need to talk more about what's
15:59
happening here to really understand the
16:01
full details
16:02
because if you look at what the
16:03
application class is you'll see that
16:05
it's the base class
16:06
and this happens to my knowledge in
16:08
exactly one situation
16:10
and it has to do with backups because if
16:12
your application is being backed up or
16:14
restored
16:15
then it gets initialized in sort of a
16:17
sandbox mode
16:19
the goal here is because there's a lot
16:20
of initialization that involves disk
16:22
writing it's very easy to initialize the
16:24
entire universe
16:26
your application is initialized in a
16:28
very basic mode
16:29
where the application class isn't used
16:31
it uses the base
16:32
instance instead your content providers
16:35
are not initialized
16:36
but your users can still launch
16:38
activities so if this happens
16:40
and your users go into your application
16:42
then you can actually
16:43
skip all of your activity or all of your
16:46
applications on create logic
16:48
and sometimes this will happen after
16:49
users have first downloaded the
16:50
application
16:51
so it's possible that the first
16:53
experience that they see is your
16:54
application crashing
16:56
this is very upsetting this is very bad
16:59
news to hear
17:00
and you might want me to tell you right
17:02
now how to fix this and
17:04
it's a little bit awkward so my first
17:07
advice here is
17:08
see if your users are affected by this
17:11
if you don't allow backup then this
17:13
probably isn't an issue for you
17:14
also if this doesn't affect that many
17:16
users then you probably don't want to
17:18
bother
17:19
but if this does affect users then there
17:21
isn't really that good of an answer to
17:23
this unfortunately
17:24
um you the the most clever thing that
17:26
i've seen somebody do to this
17:28
is they've in their initial activity
17:31
they've checked whether or not their
17:33
application is theirs and if it isn't
17:35
they just
17:35
show a dialogue that says we can't start
17:37
the application right now and then they
17:39
just close the application
17:40
and eventually they'll be able to start
17:42
it this does work
17:43
it is awkward and ideally the framework
17:46
would handle it for you
17:47
but this is really as good as you can
17:49
get with a lot of the initialization
17:50
logic
17:53
at this point we've made a lot of really
17:54
good progress here we've handled a lot
17:56
of different recreation and death
17:58
scenarios and even some really obscure
17:60
crashes
18:00
but because we're spending a lot of time
18:02
fixing these our
18:04
product manager is coming to us and
18:05
they're very upset because they need
18:07
features that's where the product that's
18:09
where the company is making money from
18:10
so we're going to talk again to our
18:12
designer because our designer wants us
18:13
to add a details page to some component
18:16
in our activity
18:17
in this case the example that i'm going
18:19
to go over is a list of students
18:21
so you might have some student list and
18:23
you want to open a student detail
18:24
fragment to see
18:25
all sorts of information about them this
18:27
is really common
18:28
one classic way of implementing this
18:30
navigation is to use a new instance
18:32
method that looks something like this
18:34
you can take your student you can
18:36
implement parcelable on it there's a
18:38
partialized annotation to do all that
18:39
implementation
18:40
for you which is really great if you
18:42
don't want to do it manually and you're
18:43
using the navigation library then
18:45
it will look something like this which
18:47
is also very clean
18:49
um the problem with using an argument it
18:52
applies to both of these
18:54
is really what we're passing in here so
18:57
in this case
18:57
the student object itself because
18:59
universities have a lot of information
19:01
about their students
19:02
it might be a very very large object
19:05
and it might be really big this can
19:07
cause a few issues especially if you're
19:09
assuming
19:10
that the student doesn't change between
19:12
the time that you start the new ui and
19:14
by the time
19:15
that you commit the changes you you
19:18
might assume
19:19
that the user that the student won't
19:22
change there
19:22
and you might not observe it which is up
19:24
to you to decide whether or not that's
19:25
good or bad
19:26
but really there's a couple of things
19:29
going on here
19:30
and the first one here is if you're not
19:33
using either of those two
19:34
implementations if you're not using
19:36
bundles or the navigation library you
19:38
might be making a mistake
19:39
where if you're using a property or a
19:41
constructor then
19:43
you might not be actually persisting
19:44
those values correctly and if you get a
19:46
configuration
19:47
change you might lose that parameter
19:48
entirely so that's its own issue if
19:50
you're not handling that
19:52
but the real issue that i want to call
19:53
out here is that there's more
19:55
restrictions than just the type of data
19:57
that we can fit inside of a bundle
19:59
so we know that there's limitations on
20:03
using primitive types parcelable
20:04
serializable and all of those
20:06
but right now we've got a very big
20:09
parcelable and there's actually a size
20:10
limitation
20:11
so we need to be mindful of that the
20:13
code that we've got is
20:14
okay-ish probably we're not going to run
20:17
into that limit
20:18
but if we don't if we start doing
20:19
something like this we're using
20:21
serializable that takes up more space
20:23
because
20:24
it's a less efficient persistence
20:26
mechanism but the worst possible thing
20:28
that you can do as far as size is
20:30
concerned is to use json instead
20:32
this is going to be very large and i do
20:34
not recommend that you do this
20:35
under virtually any circumstance because
20:39
there's a very small limit for how much
20:42
information can be stored inside of a
20:44
bundle
20:44
at most and this is a very theoretical
20:47
limit it's one megabyte
20:48
this is the amount of memory that
20:50
android can transfer between
20:52
processes which needs to happen because
20:54
this information needs to go from your
20:56
application
20:57
into the activity manager or for your
20:58
activities to be started and for the
20:60
state to be persistent
21:02
you also only get one bundle per
21:03
activity in terms of save state
21:05
so this does apply to both when you are
21:07
doing fragment arguments intent extras
21:10
and saved instance state bundles
21:11
um so with save instance say bundles in
21:13
particular you only get
21:15
one of these for a specific activity
21:17
which means that you need to be careful
21:18
about all of your fragments contributing
21:20
against this limit
21:21
and the official recommendation is to
21:23
keep all of this state under 50
21:25
kilobytes which
21:26
is not that large so the goal here is to
21:29
avoid complicated objects and don't use
21:31
json because it's
21:32
very easy to get like a three megabyte
21:34
json file which will never fit into one
21:37
of these bundles
21:39
in terms of saved instance state in
21:41
particular it might look something like
21:42
this this is extremely exaggerated
21:44
because there's more state than just the
21:46
state that you are persisting
21:48
there's also the internal state like
21:50
your view state your fragment backstack
21:51
and any fragment arguments that they
21:53
have
21:53
and for a single activity those all get
21:55
combined into a single bundle
21:57
so if you're using something like a
21:59
parcelable that will contribute a lot if
22:01
it's a very complicated object
22:03
but if you're using a json string that
22:04
can easily exceed this limit and you
22:06
don't want to do that
22:08
so instead of passing all those values
22:10
you should be very careful and mindful
22:12
about what you're
22:13
passing and persisting uh realistically
22:16
the type of information that you want to
22:18
be
22:18
storing either for arguments or for
22:21
savings and state is
22:22
state so it's things like user input
22:25
it's
22:25
anything that you can't derive later on
22:28
if you have an object that you want to
22:30
display
22:30
consider passing the id instead of the
22:32
entire object itself
22:34
and if you do need to pass the entire
22:36
object then definitely don't use json
22:38
use parcelable if you can so that's all
22:41
great we've now got this navigation
22:43
working
22:44
we've implemented that feature
22:45
everything is good and then our designer
22:47
wants us to
22:48
implement some retry because we might
22:50
have some errors that are network
22:51
related inside of our application
22:53
and we don't want to handle those
22:54
gracefully the first thing that you
22:56
might consider doing in this situation
22:58
is to use an alert dialog
22:60
the downside to an alert dialog is that
23:02
as soon as you rotate the phone
23:03
or do any other configuration change or
23:06
any other activity recreation
23:08
that dialogue is going to go away so
23:10
this isn't really going to work out
23:13
another thing that you might want to do
23:14
is you might want to use a dialogue
23:16
fragment but you might want to use
23:18
a really generic interface and i see a
23:20
lot of people trying to use lambda
23:22
expressions with dialogue fragments
23:24
the issue here is that there's not a way
23:26
to persist lambda expressions
23:29
so here i'm using put parcelable in
23:31
order to store this lambda expression
23:33
but that's very questionable because
23:34
lambda expressions aren't parcelable
23:37
if you actually were to try and do this
23:39
then it would be a compiler error in
23:40
that situation
23:42
or if you're using the navigation
23:43
library then you get one of two errors
23:45
the first one if you use the lambda type
23:48
is that it's
23:49
just not a legal identifier but the
23:52
other error that you might get
23:53
is runtime because it doesn't implement
23:55
either parcelable or serializable if you
23:57
pull out an interface instead
23:59
and really there's not a way to use
24:01
lambda expressions with fragments
24:03
effectively whatsoever
24:04
because the lambda expression can
24:06
capture any part of an activity
24:08
you might be trying to persist that
24:10
entire activity which at best
24:11
leaks the activity at worst does a whole
24:14
lot of other really nefarious things
24:16
behind the scenes
24:18
but really you're going to lose this
24:19
information as soon as you get a
24:20
configuration
24:21
change and you want to avoid it so
24:24
instead of going those routes you should
24:26
use a dialog fragment instead and you
24:28
can use a view model in order to send
24:30
those results
24:31
so here we've got the dialogue fragment
24:34
from before
24:34
we're getting an activity view model
24:36
specifically because if you get one
24:38
that's scoped to the fragment then it'll
24:40
go away after the dialogue fragment
24:41
disappears which you probably don't want
24:44
so here we're setting the positive
24:46
button to just retry some photo upload
24:48
that may have failed it may have caused
24:49
this dialogue to reappear
24:51
an alternative option if you don't want
24:53
to use a if you don't want to use a view
24:55
model is to use a callbacks interface
24:57
so here i've added an interface that has
24:59
an on retry upload function
25:01
and you can just require that the
25:02
activity implements this callbacks
25:04
interface and delegate the behavior
25:06
somewhere else and this is all fine and
25:08
you roll this out and it's
25:10
working and then you also notice that
25:12
your crash reporter has
25:14
illegal state exceptions complaining
25:15
about when you're showing this dialogue
25:18
and there's another assumption that's
25:20
made here about
25:22
being able to show fragments and do
25:24
transactions arbitrarily
25:25
and this also is not true
25:29
the reason or one reason that this might
25:31
not be working is
25:32
because in this case you might be
25:34
uploading a photo
25:35
and then suppose you get a call from
25:37
your mother and you're a good son or
25:39
daughter and you know that it's in your
25:40
best interest to answer this phone call
25:42
so go
25:42
you go ahead you answer the call you
25:44
have a lovely conversation
25:46
but in the background your photo is
25:48
still being uploaded your activity has
25:50
been suspended and it's still
25:52
doing that upload and it's still
25:53
churning along
25:55
if you get a network error in this state
25:57
and you are using that to show a
25:58
dialogue
25:59
then that's where the problem is the
26:01
problem here is
26:03
because your activity is in the stop
26:04
state at this point
26:06
you can no longer do fragment
26:08
transactions because there's a
26:09
possibility
26:10
that this activity gets evicted from
26:12
memory and
26:13
needs to be restored after process depth
26:16
and there's no way to modify the bundle
26:18
of save state that you've already
26:19
persisted
26:20
so the fragment manager makes it illegal
26:22
to do fragment transactions while you
26:24
are in the background for this reason
26:26
because you might not come back to them
26:28
i do also want to point out that this
26:29
isn't exclusive to phone calls if your
26:32
activity is in the stop state at all
26:33
then it's illegal to do fragment
26:35
transactions
26:36
and if you're using the navigation
26:37
library then you might not even know
26:39
that this is an issue because
26:41
when i was testing it on i believe a
26:43
beta version of the library
26:45
it actually swallows the error and just
26:47
logs into logcat
26:48
that were we can't do this navigation
26:50
call so
26:51
you don't get an exception and you also
26:53
don't get the navigation
26:54
but you don't get a crash which is good
26:58
if you really do want to show this
26:59
dialogue because it's important though
27:01
then
27:01
there's a few options the one that i've
27:04
seen a lot of people use
27:06
is to use commit allowing state loss
27:08
instead
27:09
the way that this works is it does allow
27:11
you to do the fragment transaction
27:13
while your activity is in the stop state
27:15
but it does mean that if your process
27:17
gets evicted and the user comes back to
27:19
it
27:19
then you might not see that dialogue
27:22
which
27:23
might be okay because that's a really
27:24
rare situation and with this dialogue in
27:27
particular it's just related to an error
27:29
so the user will probably see that their
27:30
photo isn't being uploaded anymore and
27:32
they'll be able to
27:33
make sense of what happened so
27:37
this is one approach to handling it if
27:38
you have a more
27:40
advanced dialogue where you do need to
27:41
make sure that it always shows
27:43
this can range from really difficult to
27:45
impossible and
27:46
you need to be really careful about how
27:48
you're thinking about how you do that
27:51
and at this point i've given you a lot
27:52
of information and pointed out a lot of
27:54
things
27:55
and you may be wondering well okay
27:58
can i go ahead and fix those right now
28:00
please
28:01
um and you may also be wondering how do
28:04
i do that
28:05
and really the advice that i have here
28:07
is to do the right thing
28:09
for your application and i will leave
28:12
you with one
28:12
parting assumption which is that you
28:14
have to fix every single one of these
28:16
problems
28:17
which might not be the case for your
28:18
application there's a few exceptions to
28:20
this
28:22
for example if you're not publishing
28:23
your application or if you have an
28:25
application that you haven't uploaded
28:26
that you haven't updated in seven years
28:28
and it's designed for like uh
28:29
like android 2.3 it probably doesn't
28:32
matter if you've had these problems
28:34
anymore
28:34
at this point there's not really anybody
28:36
using your application and
28:38
it probably doesn't matter that much
28:39
anymore likewise if you're doing a demo
28:42
library or you have some other toy
28:43
application just to demonstrate some
28:45
functionality
28:46
and that functionality doesn't rely on
28:48
process depth or configuration changes
28:51
it might not matter if you're not
28:52
handling these
28:55
another really big reason that i see
28:56
people not handling these which actually
28:58
works out ironically well is if they're
29:00
running on controlled hardware
29:02
so what i mean by that is if you are
29:04
building something for a kiosk or like a
29:06
smart refrigerator or something and you
29:08
are running your software on that
29:10
then you probably know that you're not
29:12
going to have to worry about
29:13
rotating from portrait to landscape like
29:15
if your refrigerator is running in
29:17
landscape all of a sudden
29:18
you have a very big problem and it's not
29:20
that you lost activity state
29:22
another big reason that you might not
29:24
need to handle this is if you're doing
29:26
something with a game engine for example
29:27
so if you're working with like unity or
29:29
some other 3d engine
29:31
you don't want to really recreate your
29:33
entire activity because that can
29:34
introduce
29:35
latency if you are in some really
29:38
intensive situation and you do want to
29:40
handle orientation changes
29:41
so you might use config changes if you
29:43
are a game engine to prevent
29:45
a scenario where your game stutters for
29:47
a second and then comes back to life
29:49
there's a few other exceptions that you
29:51
can think of to justify these behaviors
29:52
but if these aren't you
29:54
then please proceed
29:57
first of all please embrace activity and
29:59
fragment transactions
30:00
they're a reality they're going to
30:02
happen at some point or another
30:04
especially if you have a lot of users
30:05
that have downloaded your app from the
30:06
play store
30:08
and you can effectively think that an
30:10
activity recreation can happen
30:11
virtually any time to fix these or
30:14
really to
30:15
implement these correctly use view model
30:17
and save state or
30:18
override on save instance date and use
30:21
that saved instance a bundle to
30:22
maintain this state you can avoid uh try
30:25
to avoid android configuration changes
30:27
but there are a couple of exceptions to
30:29
this rule as well
30:30
for example webview does not support
30:32
writing its state into a bundle it used
30:35
to but that was actually leaking disk
30:36
space
30:37
so in all modern versions of android
30:39
that's disabled
30:40
so if you're using webview and you want
30:42
to support orientation changes
30:44
you might add config changes for window
30:46
size in that example
30:47
and just let your activity restart and
30:49
lose state if you change languages or
30:50
something
30:52
likewise if you're doing something with
30:53
surface view so something like a game
30:55
engine or possibly using the
30:56
camera you might not want to recreate
30:59
your activity because that can introduce
31:00
latency and stuttering which you might
31:02
want to avoid
31:03
so there are a few exceptions to when
31:05
you can use config changes but please
31:07
don't throw it on every single activity
31:08
in your application because that's
31:10
not a good idea the other thing to be
31:13
mindful of
31:14
and to tolerate is process eviction so
31:16
make sure
31:17
that any initialization logic that you
31:19
have on your splash screen
31:21
isn't critical for your application so
31:23
don't do things that will lead to things
31:25
like no pointer exceptions or
31:27
illegal state exceptions do lazy
31:29
initialization if you can
31:31
also assume that if you are in the
31:33
middle of a complicated flow
31:35
um there might be a situation where the
31:38
user leaves your app and comes back to
31:39
it
31:40
and all of the state that existed in
31:42
singleton's has been lost
31:43
so you need to make sure that anything
31:45
from your complicated flow is being
31:47
saved somewhere like a safe safe handle
31:48
or
31:49
in a savings state bundle um and
31:51
likewise
31:52
uh prefer lazy initialization lazy
31:54
loading if it's possible to do
31:56
a lot of the time this isn't possible to
31:58
do with things like frameworks like bug
31:60
reporters and other third-party apis
32:02
that you might be integrating with
32:03
and for those it's safe enough to leave
32:05
those in your application class and
32:07
if you really need to tolerate the
32:08
backup situation to do that on a
32:10
case-by-case basis
32:13
the last words of wisdom that i have are
32:14
to make sure that you are testing these
32:16
scenarios to make sure that you are
32:18
doing what is right for your application
32:20
if you have your app locked to portrait
32:22
trigger uncommon activity recreation
32:24
scenarios
32:25
do things like change your display size
32:27
change your language change your locale
32:29
um if you like trigger multi window run
32:31
it on a foldable phone make sure
32:33
everything is working
32:35
um trigger process that's using android
32:37
studio or using the venom library
32:39
and don't rely on the don't keep
32:41
activities toggle on the developer
32:43
options to test this for you
32:45
because it's not a real world
32:46
representation of what actually happens
32:48
don't keep activities will destroy
32:50
activities as soon as the user leaves
32:52
them
32:52
which isn't true if your activity is on
32:54
the back stack it will be in the stop
32:56
state it won't be destroyed yet
32:58
don't keep activities also does nothing
33:00
to simulate process death so be very
33:02
mindful about that if you've been using
33:04
it to test
33:05
process or to test activity recreation
33:07
scenarios in your application
33:10
and again i cannot stress this enough
33:11
fix the problems that affect
33:13
your application and your users because
33:15
there is not a one-size-fits-all
33:17
solution for
33:18
these issues thank you so much for
33:21
listening i've been andrew bailey
33:22
you can find all this information i'll
33:24
have a link to this recording as well
33:26
at andrewbailey.dev falsits where i've
33:29
got right now some additional
33:30
information a link to that venom library
33:33
and also some more resources if you want
33:35
to
33:35
investigate these crashes for yourself
33:38
and now i believe we can turn it over to
33:40
q a
33:42
yes we can thank you so much for the
33:44
amazing talk and who i sure learned a
33:47
lot
33:48
we have a couple of questions and i'm
33:50
gonna hand it over
33:51
to jan so we can get those
33:55
right away okay just give me one second
33:58
here
34:06
so jan should be able to present his
34:08
screener
34:10
and there we go
34:18
all right uh let's see going top to
34:21
bottom uh
34:22
is it also possible to use adb to kill
34:24
and recreate app
34:25
processes you probably can
34:28
i'm not sure what that would look like
34:30
but i'm guessing what it would be
34:32
is you would run the application leave
34:35
it by hitting the home button to make
34:36
sure that you're saving the instance
34:38
state
34:38
and then use adb to kill the process you
34:41
might also be able to use some task
34:42
killers in order to accomplish that
34:46
uh let's see uh is don't keep activities
34:49
in the developer options a good tool to
34:51
test
34:51
these state changes uh i think i just
34:54
mentioned that but it will not do
34:56
process it it won't trigger that
34:58
whatsoever it's not
34:58
attempting to do that uh will you share
35:02
your slides um i'm not planning to
35:05
upload the slides but you can re-watch
35:07
this presentation again i do also have a
35:11
cheat sheet that does a really good job
35:12
of summarizing all this information
35:14
on that andrew bailey.debt falsehood
35:16
site so
35:17
check that out i think that'll be really
35:19
helpful
35:21
what is the best way to test these
35:23
various configuration changes preferably
35:25
using
35:25
espresso or ui automator um that's a
35:28
really great question
35:31
myself oh i think myself um
35:35
so one thing that you can do is
35:38
you can use there's a testing library
35:40
called activity scenario
35:41
you can actually use that to trigger
35:43
entire recreations of
35:45
your application or of your activity
35:46
rather so if you
35:48
wanted to do that you can do that and
35:50
that would simulate configuration
35:52
changes
35:53
another option is you can literally
35:55
rotate the phone you can change the
35:57
requested orientation of the activity
35:58
which will also trigger configuration
36:00
change
36:03
when the process is destroyed and
36:04
recreated does the android platform
36:06
maintain the process activity as
36:08
flashback stack
36:10
yeah the backstack records themselves
36:13
are all maintained
36:14
activity manager saves those out to disk
36:16
um so the back stack itself is persisted
36:19
that's where those save instance say
36:20
bundles live
36:22
but nothing from your application
36:23
process at that point could be alive
36:25
anymore which implementation do you
36:29
recommend to prevent these issues if you
36:31
are using
36:32
mvp um oh that's a great question i
36:35
don't normally i don't personally use
36:37
mvp i use mvvm and i use viewmodel
36:40
personally
36:41
my recommendation would be to if you're
36:44
not using the model library
36:46
would be to write some sort of
36:48
abstraction or some sort of method that
36:50
will expose the saved instance state
36:52
bundle and then
36:53
override on save instant state delegate
36:56
that over to
36:57
your i guess presenter probably
37:00
and use that to write into that bundle
37:04
and then save
37:05
the state what happens if the bundle
37:08
limit size is exceeded i'm so glad you
37:10
asked i forgot to mention this
37:11
during the talk um it depends on os
37:14
version
37:15
if you're running on android 7 or higher
37:17
then you will get
37:18
a transaction to large exception and
37:21
that will get thrown at the time
37:23
of when you start the activity or save
37:26
your instance date it doesn't happen
37:28
when you exceed the size of the bundle
37:30
itself
37:30
the bundle doesn't actually have a size
37:32
limitation if you're running on an older
37:34
version of android pre 7
37:36
then you'll only get a log that goes out
37:38
to the console and i believe that the
37:40
activity just doesn't start which is
37:41
very
37:42
confusing and unexpected clap thank you
37:45
so much
37:48
uh let's see what happens if the bundle
37:51
limit size we talked about that
37:53
uh why can you use lambdas in material
37:55
alert dialogue big
37:57
builder does it suffer from this issue
37:59
too i haven't used material alert dialog
38:01
builder itself but i'm guessing that
38:03
this is
38:04
more attuned to alert dialogue i'm
38:07
guessing
38:07
that if you show this and then rotate
38:09
the phone that dialog will disappear
38:11
you can use lambda expressions with
38:13
alert dialogue and with
38:15
a material alert or some other dialogue
38:17
itself but as soon as you pass those
38:19
into a fragment
38:20
that's where you will get issues
38:24
what is the best way to mimic a
38:26
foreground service being killed by the
38:28
operating system
38:29
and how do you make sure to restart it
38:31
every time
38:32
uh let's see foreground services i'm not
38:34
too familiar on
38:36
how you could trigger death from them
38:39
especially because
38:40
from my experience the operating system
38:42
can seemingly decide to kill those
38:44
at random especially on poorly behaving
38:46
devices like samsung with a lot of
38:47
background restrictions
38:49
um so i don't really have too much
38:51
advice for
38:52
that i'm guessing you could probably
38:54
just
38:55
terminate your process if you look at
38:57
this process id
38:58
and you can send that basically
38:60
implementing a task killer if you wanted
39:01
to do that from
39:02
a testing scenario
39:06
can you describe why we should not use
39:09
live data inside repository in mvvm or
39:13
mvi architecture um
39:18
let's see live data itself in a
39:19
repository i don't disagree with
39:21
um the the main thing to be concerned
39:23
about is if you are
39:25
using live data inside of a repository
39:28
you
39:29
might be you might have state there that
39:31
doesn't
39:32
get persistent um so if you're relying
39:35
on
39:35
a repository to be in a particular state
39:39
then that might not be true because when
39:40
you're coming back from process death
39:42
those repositories will get
39:43
re-initialized and won't contain their
39:45
values
39:46
anymore um so that's the only reason why
39:49
you wouldn't want to do that otherwise
39:52
i'm not really trying to get an opinion
39:53
on like live data versus rx java code
39:55
routines inside of your repository
39:56
do whatever is right for you
40:00
let's see you spoke about testing
40:02
activity recreation
40:04
is there any reasonable way to test
40:07
process death to my knowledge
40:10
i'm not aware of a way of like unit
40:13
testing process death
40:14
it gets very complicated because there's
40:16
a
40:17
sort of intricate relationship between
40:19
the
40:20
test runner itself and your application
40:23
process um
40:24
i i would love to like to look into this
40:27
more because i'm
40:28
thinking that because of a side project
40:30
that i'm working on this will be
40:31
relevant
40:32
um but i don't know of a good way of
40:34
testing this
40:35
apart from doing that manually if you
40:37
are doing that manually then run through
40:39
the steps in either android studio or
40:41
use venom or another library that can
40:43
trigger process it for you
40:47
the practical bundle size limit is 500
40:49
kilobytes
40:50
i'm pretty sure it's 50 kilobytes um i
40:53
could be wrong on that but
40:54
the the link that i have the andrew
40:56
bailey.dev slash falsehoods i do have a
40:58
link to where google is
40:60
suggesting that you keep your state
41:01
under 50 kilobytes so that is an
41:03
official recommendation from google
41:07
uh let's see transaction two large
41:08
exception we talked about that
41:10
uh before making the fragment
41:12
transaction how can i check if the state
41:14
is safe oh that's another great question
41:15
because i've seen people do this as well
41:18
um i don't remember if there's an api
41:20
that exists
41:21
in the activity library or in the
41:24
activity framework that will do this for
41:26
you
41:26
um what i've seen people do in the past
41:28
is to keep it variable
41:30
and to basically say uh like
41:33
is instant date saved set it to
41:36
uh let's see set it to like true
41:39
initially
41:40
set it to false when you are on creating
41:43
your activity because
41:44
that savings and state is being restored
41:46
out and then
41:47
it again when you are writing that
41:50
instance state out and basically keeping
41:51
track of it manually
41:52
i wish there was a better way of doing
41:54
that but i'm not sure
41:56
if there is off the top of my head
41:60
um some other scenarios where unsaved
42:02
instance crash
42:03
can occur um i'm not sure what this
42:06
question
42:07
is getting at um i definitely covered
42:10
the main
42:10
situations for crashes um
42:14
i think there's a lot of other reasons
42:15
like this barely scratches the surface
42:17
and there's a lot of other ways to do
42:19
things incorrectly
42:20
um so if you're not doing best practices
42:24
then you could be running into your own
42:25
crashes that i haven't
42:26
seen if you're doing some more obscure
42:28
implementations
42:30
but sure there's all sorts of other
42:31
reasons why crashes could occur that are
42:33
related to saving instant states
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One