Slide 1

Slide 1 text

Be a Good Citizen Care about Android lifecycle and window size

Slide 2

Slide 2 text

BE A GOOD CITIZEN 2 1. About me 2. About Mirego 3. What are we talking about? 4. Android (activity) lifecycle 5. Properly supporting multiple window size 6. Why should we care about all that? Agenda .

Slide 3

Slide 3 text

BE A GOOD CITIZEN 3 About me . Guillaume Bourassa, Android Developer since 2012. Cogeco Radio Bell Fibe TV Varage Sale V Mobile Metric Pagan Portal Sky Motion Groupe Capitale Médias Air Transat La Presse+ La Capitale IGA SAQ

Slide 4

Slide 4 text

BE A GOOD CITIZEN 4 About Mirego .

Slide 5

Slide 5 text

BE A GOOD CITIZEN 5 About Mirego .

Slide 6

Slide 6 text

BE A GOOD CITIZEN 6 Disclaimer ● Not a formal study ● Extracted from personal observations

Slide 7

Slide 7 text

BE A GOOD CITIZEN 7 Pre-requisites ● Understanding of Activities ● Understanding of resource qualifiers

Slide 8

Slide 8 text

BE A GOOD CITIZEN 8 So, what are we talking about? ● Activity/fragment lifecycle ● Window size (responsiveness)

Slide 9

Slide 9 text

BE A GOOD CITIZEN 9 There is nothing new here! ● You’re right! ● These concerns exist since the stone age (~2009)

Slide 10

Slide 10 text

BE A GOOD CITIZEN 1 0 Ok, but why? ● Because (some) people don’t care (enough) ● Apps seem to work without these considerations ● Lifecycle and responsiveness are the cornerstone of a rock steady, future-proof app ● More on the philosophical why at the end

Slide 11

Slide 11 text

Android Activity Lifecycle

Slide 12

Slide 12 text

BE A GOOD CITIZEN 1 2 Activity Lifecycle The graph

Slide 13

Slide 13 text

BE A GOOD CITIZEN 1 3 Activity Recreation

Slide 14

Slide 14 text

BE A GOOD CITIZEN 1 4 Activity Lifecycle Types of recreation ● Activity recreation ○ Most frequent case ○ Can cause UX frustrations ○ Most important to handle ● Process recreation ○ Less frequent ○ Causes more crashes ○ Almost never happens during development

Slide 15

Slide 15 text

BE A GOOD CITIZEN 1 5 Activity Lifecycle Activity recreation When? ● On configuration change ○ Device orientation change ○ Multi-window activation / update ○ Window resize on ChromeOS

Slide 16

Slide 16 text

BE A GOOD CITIZEN 1 6 Activity Lifecycle Activity recreation onCreate Instance A onStart onResume onStop onSaveInstance State onPause onDestroy Instance B onCreate onStart onResume Bundle

Slide 17

Slide 17 text

BE A GOOD CITIZEN 1 7 Activity Lifecycle Activity recreation What happens to other Activities ● The whole stack gets re-created ● Only the top first, then the others upon unstacking What happens to everything else ● Application doesn’t change ● Any other object in memory remain ● Java process is alive and in good health

Slide 18

Slide 18 text

BE A GOOD CITIZEN 1 8 Activity Lifecycle Activity recreation Can configuration changes be handled manually? ● Yes, but don’t do it ● Activity is not recreated when window size change, onConfigurationChanged() is called ● Views are re-layouted in new Window ● Window-size specific resources won’t be used with the new updated window dimensions (more on that later)

Slide 19

Slide 19 text

BE A GOOD CITIZEN 1 9 Activity Lifecycle Activity recreation Enter transitions ● Activity are recreated without running their enter transition ● If you rely on transition end to do something, it won’t happen ● if (savedInstanceState == null) is the only way to check in what case you are

Slide 20

Slide 20 text

BE A GOOD CITIZEN 2 0 Process Recreation

Slide 21

Slide 21 text

BE A GOOD CITIZEN 2 1 Activity Lifecycle Process recreation When? ● When Android decides to kill your app while in background (to free memory or because its angry at your app) ● When the user tries to return to the app, Android restores the Activity stack.

Slide 22

Slide 22 text

BE A GOOD CITIZEN 2 2 Activity Lifecycle Process recreation onCreate Activity A onStart onResume onStop onPause Activity B onCreate onStart onResume Activity B Application Activity A onCreate

Slide 23

Slide 23 text

BE A GOOD CITIZEN 2 3 Problematic

Slide 24

Slide 24 text

BE A GOOD CITIZEN 2 4 Activity Lifecycle Problematic How to keep data between recreation so that its transparent to the user?

Slide 25

Slide 25 text

BE A GOOD CITIZEN 2 5 Activity Lifecycle Save the data What is saved/restored ● All standard Views inner state ● All Fragments present in the FragmentManager (more on that later) Intent extras ● Intent (and its “extras” Bundle) is still around What is not saved ● Custom views state ● Anything kept as a class member ● For process recreation: literally everything else

Slide 26

Slide 26 text

26 BE A GOOD CITIZEN Good Citizen Rule # 1 Never assume that something that you previously kept in memory is still available.

Slide 27

Slide 27 text

BE A GOOD CITIZEN 2 7 Where do I keep my data?

Slide 28

Slide 28 text

BE A GOOD CITIZEN 2 8 Activity Lifecycle Save the data Where to save data? ● saveInstanceState bundle (for small things) ● On disk

Slide 29

Slide 29 text

BE A GOOD CITIZEN 2 9 Activity Lifecycle Save the data onSaveInstanceState ● Data must be Serializable / Parcelable ● Use with cautious ● Don’t use it to save data, otherwise... java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 76000872 bytes

Slide 30

Slide 30 text

BE A GOOD CITIZEN 3 0 Activity Lifecycle Save the data The disk ● Keep every backend response in a persisted cache (don’t rely on HTTP cache) ● Gives a better user experience ● Can be hard/costly to implement

Slide 31

Slide 31 text

BE A GOOD CITIZEN 3 1 Activity Lifecycle Save the data ViewModels to the rescue ● MVVM is a great pattern ● Works well with data binding ● ViewModels survive Activity recreation (not process recreation)

Slide 32

Slide 32 text

BE A GOOD CITIZEN 3 2 Activity Lifecycle Save the data override fun onCreate() { super.onCreate() val viewModel = ViewModelProviders.of(this).get(AwesomeViewModel::class.java) } How do ViewModel survive?

Slide 33

Slide 33 text

BE A GOOD CITIZEN 3 3 Activity Lifecycle Save the data public ViewModelStore getViewModelStore() { if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; } How do ViewModel survive?

Slide 34

Slide 34 text

BE A GOOD CITIZEN 3 4 So… View Models and disk?

Slide 35

Slide 35 text

35 BE A GOOD CITIZEN Good Citizen Rule # 2 Opinion warning It’s okay if activity and process recreation are not handled with the same level of transparency for the user

Slide 36

Slide 36 text

BE A GOOD CITIZEN 3 6 Activity Lifecycle Level of recreation Activity recreation ● Happens when app is active (user is actively using it) ● Orientation change often happens without intention ● User expects things to remain the same ○ Loading screen is annoying ○ Losing state is frustrating ● Must be transparent

Slide 37

Slide 37 text

BE A GOOD CITIZEN Process recreation ● Happens when app is in background ● Been there for a while ● User comes back much later ● Probable doesn’t even expect to get back to where he left ○ Loading screen is acceptable ○ Losing UI state is acceptable ○ Crashing is not an option 3 7 Activity Lifecycle Level of recreation

Slide 38

Slide 38 text

BE A GOOD CITIZEN 3 8 What about fragments?

Slide 39

Slide 39 text

BE A GOOD CITIZEN Fragment recreation ● Fragments get automatically recreated upon Activity recreation ● Apply the same rules ● Fragments decide if their instance is kept upon recreation (retainInstance = true) ● 3 fragment rules to keep in mind 3 9 Activity Lifecycle Fragments

Slide 40

Slide 40 text

BE A GOOD CITIZEN Fragments gets recreated, don’t add them blindly during onCreate() 4 0 Activity Lifecycle Fragment rule #1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() setContentView(R.layout.activity_other) supportFragmentManager.beginTransaction() .add(FragmentOne(), "one") .add(FragmentTwo(), "two") .commit() } Two new fragments will be added to the fragment manager each time Activity is recreated.

Slide 41

Slide 41 text

BE A GOOD CITIZEN ● The Fragment instance you have might not be the right one when you need it ● Always get Fragment instances from the SupportFragmentManager (or child) when you need it 4 1 Activity Lifecycle Fragment rule #2 Never keep Fragments as Activity class member

Slide 42

Slide 42 text

BE A GOOD CITIZEN ● Callback can end up in the wrong Activity ● Callback can be null ● Rule of thumb: Fragments talk to their Activity, not the other way around ○ getActivity() ○ Shared ViewModel (Google suggestion) 4 2 Activity Lifecycle Fragment rule #3 Never set a listener on a Fragment

Slide 43

Slide 43 text

BE A GOOD CITIZEN 4 3 What about the Activity inputs / outputs?

Slide 44

Slide 44 text

44 BE A GOOD CITIZEN Good Citizen Rule # 3 Activity input (parameters) must be lightweight and minimal

Slide 45

Slide 45 text

BE A GOOD CITIZEN ● An Activity must have clear inputs, when needed ● Inputs must be passed to an Activity exclusively through its Intent extra Bundle ● If possible use a library that help enforce them ● Inputs must be as lightweight as possible 4 5 Activity Lifecycle Inputs

Slide 46

Slide 46 text

BE A GOOD CITIZEN 4 6 Activity input example

Slide 47

Slide 47 text

BE A GOOD CITIZEN 4 7 Activity Lifecycle Input example Product List Activity Product Details Activity Product Model How to give a complex model object to another Activity?

Slide 48

Slide 48 text

BE A GOOD CITIZEN 4 8 Activity Lifecycle Input example Product List Activity Product Details Activity Product Model The bad way Application / some singleton / static field ID

Slide 49

Slide 49 text

BE A GOOD CITIZEN 4 9 Activity Lifecycle Input example Product List Activity Product Details Activity Serializable / Parcelable Product Model The wrong way

Slide 50

Slide 50 text

BE A GOOD CITIZEN Why using Parcelable complex objects as Activity inputs is a bad idea 5 0 Activity Lifecycle Inputs as Parcelable Product List Activity Product Details Activity Serializable / Parcelable Product Model ● Size limitations ○ Size allowed gets smaller over ○ Your object will get larger over time ● Activity gets more coupled with the context from where its started ○ Caller must have the same complex object ○ Reduce re-use

Slide 51

Slide 51 text

BE A GOOD CITIZEN 5 1 Activity Lifecycle Inputs The “good” way Product List Activity Product Details Activity Component that will get the data from where it exists ID Database File-based cache Rest API

Slide 52

Slide 52 text

BE A GOOD CITIZEN ● Activity input must be the minimal amount of information needed to perform its task ● Information is then used to get the actual data 5 2 Activity Lifecycle Minimal inputs Product List Activity Product Details Activity ID

Slide 53

Slide 53 text

53 BE A GOOD CITIZEN Good Citizen Rule # 4 Respect the Activity “pattern”

Slide 54

Slide 54 text

BE A GOOD CITIZEN What is an Activity? ● A standalone part of an app that must be able to live on its own ● Must not rely on anything else in memory to do its job (if it needs something, it must get it from a reliable source) ● Must be re-usable in different contexts ● Its inputs must be clear, minimal, and lightweight 5 4 Activity Lifecycle Pattern

Slide 55

Slide 55 text

Window size / responsiveness

Slide 56

Slide 56 text

BE A GOOD CITIZEN How to layout stuff? ● Apps draw stuff in a window with fixed dimensions (width / height) ● Fixed, but variable ○ From device to device ○ Over time on the same device (config change) 5 6 Responsiveness Philosophy

Slide 57

Slide 57 text

BE A GOOD CITIZEN ● Is the Activity running in landscape or portrait ○ Portrait: layout-sw600dp/main.xml ○ Landscape: layout-sw600dp-land/main.xml 5 7 Responsiveness Philosophy Bad way of deciding how to layout ● Is the Activity running on a tablet or a phone? ○ Phone: layout/main.xml ○ Tablet: layout-sw600dp/main.xml

Slide 58

Slide 58 text

58 BE A GOOD CITIZEN Good Citizen Rule # 5 Always base your layout decisions on width and height

Slide 59

Slide 59 text

BE A GOOD CITIZEN What width and height? ● Folders with -wXXXdp and -hXXXdp qualifiers layout-w600dp values-h400dp 5 9 Responsiveness Width and height

Slide 60

Slide 60 text

BE A GOOD CITIZEN Why width and height? ● It’s the only way to make sure your layout will work in every contexts (orientation, multi-window, floating window) ● Nothing else matters ● Almost every space constraint is either vertical or horizontal 6 0 Responsiveness Width and height

Slide 61

Slide 61 text

BE A GOOD CITIZEN List VS list/details 6 1 Responsiveness Width and height layout/list.xml layout-w800dp/list.xml

Slide 62

Slide 62 text

BE A GOOD CITIZEN 6 2 Responsiveness Width and height List VS grid ● Same activity layout for all 4 cases ● Header part has different layout at w800dp ● List item has a different layout at w600dp ● value for column count

Slide 63

Slide 63 text

BE A GOOD CITIZEN Vertical constraint example 6 3 Responsiveness Width and height Margin between texts has a different value at h600dp

Slide 64

Slide 64 text

BE A GOOD CITIZEN ● layout-sw600dp/main.xml ● Used to be the way to know if we were on a tablet ● Represent the smallest between the width of the window in portrait and the one in landscape ● Gives you a general idea of the size of the device, but not that much 6 4 Responsiveness Smallest Width

Slide 65

Slide 65 text

BE A GOOD CITIZEN Don’t use Smallest Width qualifier (-swXXXdp) for layouting decisions 6 5 Responsiveness Width and height ● Both these windows have a smallestWidth of 432dp ● Their dimensions are totally opposite ● There is nothing you can decide based on smallestWidth

Slide 66

Slide 66 text

BE A GOOD CITIZEN Multi-window related notes ● Never use different Activity classes for different window size (spoiler: it will crash) ● Lifecycle is funky ○ Vary with Android versions ○ When activating, current Activity is re-created but not resumed ○ Important to properly restore state to avoid unloaded screen or infinite loading 6 6 Responsiveness Multi-window

Slide 67

Slide 67 text

Being a good citizen sounds great… but why?

Slide 68

Slide 68 text

BE A GOOD CITIZEN Easy to ignore all of this My app is phone-only, locked in portrait, and I deactivated multi-window. My activities are never recreated. 6 8 Why? No easy answer

Slide 69

Slide 69 text

BE A GOOD CITIZEN ● Who are we to decide how users will use their app? ● Locking in portrait is a user decision, not a developer decision ● Most of Google apps do not lock orientation 6 9 Why? Supporting multiple orientations Using an app in portrait is a personal decision

Slide 70

Slide 70 text

BE A GOOD CITIZEN ● Even if your app is “phone only”, users can and will run them on their tablets. ● Without minimal care, your layouts will look terrible ● Portrait-locked apps on tablets is frustrating 7 0 Why? Supporting “tablets” Your app will run on tablets

Slide 71

Slide 71 text

BE A GOOD CITIZEN Tablet-optimized version of an app ● Support from day 1 is almost free ● Migrating a “phone-only, locked in portrait” app to a tablet-friendly is expensive 7 1 Why? No easy answer

Slide 72

Slide 72 text

BE A GOOD CITIZEN Multi-window does matter ● Seems useless at first (especially on phone) ● Google works hard to promote ChromeOS ○ Running Android apps on desktop is an awesome use case ○ Running fullscreen Android apps on desktop is a terrible experience ○ Pushes multi-window to the next level 7 2 Why? No easy answer

Slide 73

Slide 73 text

BE A GOOD CITIZEN 7 3 Why? Resizing in ChromeOS

Slide 74

Slide 74 text

BE A GOOD CITIZEN Another reason why Android apps on ChromeOS will get more important... 7 4 Why? ChromeOS

Slide 75

Slide 75 text

75 BE A GOOD CITIZEN Good Citizen Rule # 6 Always support multi-window and orientation change. This will result in a better user experience, and will help you detect broken rules.

Slide 76

Slide 76 text

BE A GOOD CITIZEN Because we are professionals developers ● Do the right thing from day one ● Don’t take shortcut ● Don’t give the burden of fixing your current bad decisions to future you ● Don’t give the burden of fixing your current bad decisions to future others 7 6 Why? The final reason

Slide 77

Slide 77 text

BE A GOOD CITIZEN In software development, drifting from the right way of doing things, using shortcuts and “we’ll fix that later”, will leave you after some time with a messy, un-maintainable and horrific codebase. - Me 7 7 Why? Citation

Slide 78

Slide 78 text

Merci . MIREGO ACADEMY