Skip to content

2.2 Useful Talks

EwasWorld edited this page Dec 29, 2020 · 1 revision

Table of Contents

Android App Anatomy - Eric Burke 2012

Link

Eric Burke. Works for Square

  • Last year (2011) talked about how to make your UI look beautiful

Main Takeaways

  • ActionBarSherlock for a consistent look across any android version
  • Simulate low memory conditions (where Android will kill your app that's running in the background) using DDMS tools and android dev tools' don't keep activities and no background processes options
  • Tape API lets the app automatically retry things that are expected to intermittently fail such as things requiring a network connection. Careful of battery life issues.
  • When a new thread is created, don't use Android classes on it like dialogs. Threads can outlive the Activity which can cause issues on commit.
  • One Activity per flow. Section the app into overall areas (e.g. account setup, settings, and general use). Each area should have its own Activity. Within that everything should be a Fragment.
  • Listeners for passing info between Fragments isn't great. Otto provides a publish/subscribe architecture for this which is lightweight and decoupled. It also has a Producer which is useful for supporting caching and further decoupling Activities and Fragments from threads.
  • Fail fast and hard, even in production. If you have something that shouldn't occur, e.g. a double unsubscribe, crash the app immediately. A warning sent to the log that is never checked won't help and in this case it can cause a memory leak which will be harder to track back to the double unsubscribe error.
  • Info on how Dagger dependency injection works and can be used.

Notes

Intro

  • Following android design with an action bar, flat design, and making use of android's built in back button
  • ActionBarSherlock allows your app to look the same on any version of android

Layout 2:48

  • Android apps look like they run forever but if you say switch out and take a phone call and come back, the process will have been paused and restarted. * Rotating will destroy the activity and recreate one. Activities can also be destroyed when the process is changed
  • This becomes a problem when there are static variables as these are lost when a process is killed.
  • For them, the app would crash when users attached a photo to the receipt because it would get low on memory, killing the process, then when you go back to the app, the process is restarted but the variables are gone.
  • How do you test for this? Simulate low memory conditions
    • Run the DDMS tool (comes with android dev tools?). You can open another app on the phone then kill the process for your app. Then hit back to go back to your app which will restart the process
    • There's also a box for 'Don't keep activities' in android dev tools
    • Dev tools: no background processes: as soon as you leave your app, android will kill the process

Tape API 7:35

  • "If you respect users, persist tasks to disk" -Jesse Wilson
  • You should make apps that expect network connection to be flakey
  • Tape framework: create a task like 'upload picture' and it will try to upload to upload. If the task fails, schedule an android alarm to say 5 mins from now try again
  • Careful with battery life by having exponential growth in delay time or check there's a network connection when the task wakes up

Fragment Lifecycle 11:48

  • Tapping some 'upload' button will create a loader or a thread. However, a thread isn't bound by the life of the activity though, it will last until it finishes it's task.
  • If that thread then plays with the FragmentManager by showing a progress dialog fragment, for example, if the activity finishes before the thread does, you're going to commit the fragment transaction after the activity has saved its instance state (which it does when it's being killed). This is going to make a big boo boo.
  • In general, Square doesn't use Loaders (for the most part)

Fragment Layout 15:06

  • One activity, many fragments for all of the onboarding (setting up a new user)
  • Why? Smoother animations between steps, ActionBar doesn't move, code organisation
  • Made an app where there was one activity and everything is in a fragment (to try to avoid lifecycle issues). That didn't work though so they went with onboarding, settings, and payment flow each having their own activity and everything within that is a fragment. Each of these activities has a slightly different look and feel
  • Google recommends each fragment has a public listener that is implemented by the ACTIVITY. In the fragment's onAttatch, the activity is then cast to the listener type. This creates 'the god object'. Also called the wagon wheel design pattern, the activity in the middle and all the fragments making a wheel around it. This is tightly coupled, adding new fragments means touching the god object every time.
  • They instead have a bus architecture with publishers and subscribers. Publishers and subscribers can be anything. The bus has the publish/subscribe mechanism but doesn't know the concrete types giving no compile time dependency

Otto (event bus) 23:25

  • (see slides for concrete examples of pub/sub)
  • Fail fast. They crash completely on fail, even in production. For example, they do this when unregister is called twice in a row (without a register in between), rather than just throw a warning in the log. This is because no one ever looks at the logs and android floods the log with junk. The double unregister will cause a memory leak which will eventually crash the program later on and will be more difficult to track back to the unregister
  • Subscribe doesn't scan super classes for subscribe annotations because android Reflections are much slower than desktop Java Reflections (historical) so they don't walk the hierarchy. This means that subscribe must be done on concrete classes. Before this was removed it would take a full second to do this pub/sub
  • You can even subscribe to Object
  • It's all synchronous and on the same thread
  • Example for why Producers are useful
    • You could download an user's profile image in a thread in the background and have this image published to the bus.
    • Then in the activity you have the subscribe method show the image in a view
    • If you're lucky, when the image is published, the activity will be displayed on screen and it will run the callback. But usually the downloader will run and publish but there's no screen currently up so nothing subscribes to receive the image.
    • They instead have an image cache that's a long lived object, possibly tied to the life of the application context. If the process gets killed and revived, it's up to you to recreate the cache. You'd probably store the image on the file system so you don't have to fetch it again.
    • This cache is annotated as a singleton and has a produce annotated method which returns the cached image. The activity then subscribes and the bus recognises that the subscribe and will immediately feed it an event from the producer as if the publisher had just triggered a publish.
  • This producer decouples threads from the Activity and Fragment lifecycle, preventing the thread commit problem mentioned earlier
  • Otto is forked from Guava's EventBus. They didn't want to include Guava because it's big and space is important on mobile. It's also faster using less reflection (10x speed improvement), more caching, and adds Producer
  • They next want to get to code generation using annotations?
  • When creating an API: when in doubt, leave it out. This allows the code generation approach

Dependency Injection 34:35

  • Guice on Android. It's really good but has startup issues. It added a full second to their app's startup time
  • RoboGuice is built on top of Guice and adds some useful Android conveniences for injecting Activities and fragments.
  • They created Dagger instead. This is compile time dependency injection using JSR-330 annotations.
  • The things you want to inject have to be package-private or public because Dagger uses code generation to put these things into the instance. If they allowed private, they would have to use reflections which are slow
  • If you have a non-managed object you can make the fields private-final and use inject on the constructor
  • (see example in slides)
  • Entry points (places where inject needs to be done) are defined inside the module. Providers methods in the module tell Dagger to make the things to inject into those places. This is kicked off in the application's on create.
  • There should be a base class that all your fragments extend from. This class' in create is where the gross retrieval of the application and the actual injection happens

Questions 42:07

  • Unit test only on Java objects, not Android objects
  • Robolectric
  • Robodium?