Android App Anatomy Eric Burke Square @burke_eric Topics Android - - PowerPoint PPT Presentation

android app anatomy
SMART_READER_LITE
LIVE PREVIEW

Android App Anatomy Eric Burke Square @burke_eric Topics Android - - PowerPoint PPT Presentation

Android App Anatomy Eric Burke Square @burke_eric Topics Android lifecycle Fragments Open source Tape Otto Dagger ActionBarSherlock Android Design on every device. Lifecycle Install Apps Run Forever Uninstall


slide-1
SLIDE 1

Android App Anatomy

Eric Burke Square @burke_eric

slide-2
SLIDE 2

Topics

  • Android lifecycle
  • Fragments
  • Open source
  • Tape
  • Otto
  • Dagger
slide-3
SLIDE 3
slide-4
SLIDE 4

ActionBarSherlock Android Design on every device.

slide-5
SLIDE 5

Lifecycle

slide-6
SLIDE 6

Apps Run Forever Install Uninstall

slide-7
SLIDE 7

Apps Run Forever

Process 1 Process 2

slide-8
SLIDE 8

Apps Run Forever

Process 1 Process 2 Activity Activity Activity

slide-9
SLIDE 9

Apps Run Forever

Process 1 Process 2 Activity Activity Activity Static Variables

R.I.P .

slide-10
SLIDE 10

Kill your process

slide-11
SLIDE 11
slide-12
SLIDE 12
slide-13
SLIDE 13

Tape

slide-14
SLIDE 14

“If you respect users, persist tasks to disk.”

  • Jesse Wilson
slide-15
SLIDE 15

Don’t Do This

slide-16
SLIDE 16

Client UI Image File Server Loader or Thread

slide-17
SLIDE 17

Do This

Pending

slide-18
SLIDE 18

Tape API

  • QueueFile
  • O(1) FIFO queue of byte[]
  • ObjectQueue
  • A queue of <T>
  • TaskQueue
  • Injects and starts tasks
slide-19
SLIDE 19

TaskQueue

Tape

add() peek() remove()

Client UI Server Service

UploadTask UploadTask

slide-20
SLIDE 20

Usage Pattern

  • 1. UI adds to a TaskQueue:

queue.add(new ImageUploadTask(image));

  • 2. Start the Android Service:

context.startService(new Intent(context, ImageUploadTaskService.class));

slide-21
SLIDE 21
  • 3. Service executes the next task:

Task t = queue.peek(); t.execute(this); // Listener

  • 4. If successful:

queue.remove(); executeNext();

  • 5. If failure, schedule a retry.
slide-22
SLIDE 22

Retrying Failed Tasks

  • Always process tasks in order: FIFO
  • Distinguish between client bugs vs.

network or server exceptions

  • Client errors - do not retry
  • Server & network errors - retry
slide-23
SLIDE 23

Fragment Lifecycle

slide-24
SLIDE 24

Time ➞

Activity

  • nCreate()
  • nSaveInstanceState()
slide-25
SLIDE 25

Time ➞

Activity

Tap the Upload Button

slide-26
SLIDE 26

Loader or Thread

Time ➞

Activity

slide-27
SLIDE 27

Loader or Thread

Time ➞

Activity DialogFragment

beginTransaction()

slide-28
SLIDE 28

Loader or Thread

Time ➞

Activity DialogFragment

commit()

  • nSaveInstanceState()
slide-29
SLIDE 29

Square does not* use Loaders.

slide-30
SLIDE 30

Fragment Layout

slide-31
SLIDE 31
slide-32
SLIDE 32

Advantages

  • Smooth animations between steps
  • ActionBar does not move
  • Code organization
slide-33
SLIDE 33

Onboarding

Fragment Fragment Fragment

Settings

Fragment Fragment Fragment

Payment Flow

Fragment Fragment Fragment

slide-34
SLIDE 34

Fragment Listener Interface Containing Activity

defines implements

Fragment → Activity Communication

http://developer.android.com/training/basics/fragments/communicating.html

slide-35
SLIDE 35

public class NewsFragment extends Fragment { private Listener listener; public interface Listener { void onItemSelected(int position); } @Override public void onAttach(Activity a) { super.onAttach(a); listener = (Listener) a; } }

Listener Interface

slide-36
SLIDE 36

public class HomeActivity extends Activity implements NewsFragment.Listener { @Override public void onItemSelected(int position) { … } }

Activity

slide-37
SLIDE 37

public class Onboarding extends Activity implements AccountFragment.Listener, ActivateFragment.Listener, ShippingFragment.Listener, BankFragment.Listener, etc... { // The god object... }

slide-38
SLIDE 38

How can Fragments communicate without tight Activity coupling?

Activity

Fragment Fragment Fragment Fragment Fragment Fragment

slide-39
SLIDE 39

Bus

Fragment Fragment Fragment Fragment Service Activity

Publish Subscribe

slide-40
SLIDE 40

LocalBroadcastManager

  • Included in Android Support library
  • Publishes Intents within your

process

slide-41
SLIDE 41

Intent intent = new Intent( BroadcastIds.LOCATION_UPDATED_ACTION); intent.putExtra( BroadcastIds.CURRENT_LOCATION_KEY, getCurrentLocation()); LocalBroadcastManager.getInstance( getActivity()).sendBroadcast(intent);

Publishing

slide-42
SLIDE 42

Intent intent = new Intent( BroadcastIds.LOCATION_UPDATED_ACTION); intent.putExtra( BroadcastIds.CURRENT_LOCATION_KEY, getCurrentLocation()); LocalBroadcastManager.getInstance( getActivity()).sendBroadcast(intent);

Publishing

slide-43
SLIDE 43

Intent intent = new Intent( BroadcastIds.LOCATION_UPDATED_ACTION); intent.putExtra( BroadcastIds.CURRENT_LOCATION_KEY, getCurrentLocation()); LocalBroadcastManager.getInstance( getActivity()).sendBroadcast(intent);

Publishing

slide-44
SLIDE 44

BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive( Context context, Intent intent) { Location location = (Location) intent.getParcelableExtra( BroadcastIds.CURRENT_LOCATION_KEY); showLocation(location); } };

Subscribing

slide-45
SLIDE 45

IntentFilter locationFilter = new IntentFilter( BroadcastIds.LOCATION_UPDATED_ACTION); @Override public void onResume() { super.onResume(); LocalBroadcastManager.getInstance(getActivity()) .registerReceiver(receiver, locationFilter); } @Override public void onPause() { super.onPause(); LocalBroadcastManager.getInstance(getActivity()) .unregisterReceiver(receiver); }

Subscribing

slide-46
SLIDE 46

Boilerplate. No type safety. Hard to test.

slide-47
SLIDE 47

Otto

slide-48
SLIDE 48

public class BaseFragment extends Fragment { @Inject Bus bus; @Override public void onResume() { super.onResume(); bus.register(this); } @Override public void onPause() { super.onPause(); bus.unregister(this); } }

Registration

slide-49
SLIDE 49

public class BaseFragment extends Fragment { @Inject Bus bus; @Override public void onResume() { super.onResume(); bus.register(this); } @Override public void onPause() { super.onPause(); bus.unregister(this); } }

Registration

Fails fast at runtime.

slide-50
SLIDE 50

@Subscribe public void onLocationUpdated(Location l) { showLocation(l); }

Subscribing

Receives any type extending Location.

slide-51
SLIDE 51

bus.post(getCurrentLocation());

Publishing

Synchronous delivery.

slide-52
SLIDE 52

@Produce

(getting data the first time)

slide-53
SLIDE 53

How to get this image?

slide-54
SLIDE 54

post(UserImage)

Bus UserImageCache Downloader

slide-55
SLIDE 55

public class AccountActivity extends BaseActivity { @Subscribe public void onUserImageUpdated( UserImage image) { ((ImageView) findViewById(R.id.image)) .setImageBitmap(image.getBitmap()); }

Subscribing

slide-56
SLIDE 56

Activity

  • nResume()
  • nPause()

Downloader UserImageCache

get() done()

slide-57
SLIDE 57

@Singleton public class UserImageCache { @Produce public UserImage produceUserImage() { return cachedUserImage; } … }

Producers

slide-58
SLIDE 58

Activity UserImageCache

@Produce

Bus

@Subscribe

slide-59
SLIDE 59

@Produce decouples threads from the Activity and Fragment lifecycle.

slide-60
SLIDE 60

Otto API Summary

  • register(), unregister(), post()
  • @Subscribe, @Produce
  • Thread confinement
  • Easy to test!
slide-61
SLIDE 61

Origins of Otto

  • Forked from Guava’s EventBus
  • Optimized for Android - 16k!
  • Less reflection; more caching
slide-62
SLIDE 62

Dependency Injection

slide-63
SLIDE 63

Guice on Android?

  • We’ve used it for 2+ years
  • Startup performance is a challenge
  • Runtime error checking
  • See also: RoboGuice
slide-64
SLIDE 64

Can we do better?

slide-65
SLIDE 65

Dagger †

slide-66
SLIDE 66

What is Dagger?

Compile-time dependency injection.

slide-67
SLIDE 67

import javax.inject.Inject; // JSR-330 public class PublishFragment extends BaseFragment { @Inject Bus bus; @Inject LocationManager locationManager; … }

@Inject

slide-68
SLIDE 68

public class AccountUpdater { private final Bus bus; private final AccountService accounts; @Inject public AccountUpdater(Bus bus, AccountService accounts) { this.bus = bus; this.accounts = accounts; } }

Constructor Injection

slide-69
SLIDE 69

@Module( entryPoints = { HomeActivity.class, PublishFragment.class, SubscribeFragment.class } ) public class ExampleModule { @Provides @Singleton Bus provideBus() { return new Bus(); } }

Module

slide-70
SLIDE 70

@Module( entryPoints = { HomeActivity.class, PublishFragment.class, SubscribeFragment.class } ) public class ExampleModule { @Provides @Singleton Bus provideBus() { return new Bus(); } }

Module

slide-71
SLIDE 71

Missing Provider Method?

No injectable members on com.squareup.otto.Bus. Do you want to add an injectable constructor? required by com.squareup.anatomy.PublishFragment for com.squareup.anatomy.ExampleModule

slide-72
SLIDE 72

public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate();

  • bjectGraph = ObjectGraph.get(

new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

Bootstrapping

slide-73
SLIDE 73

public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate();

  • bjectGraph = ObjectGraph.get(

new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

Bootstrapping

slide-74
SLIDE 74

public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate();

  • bjectGraph = ObjectGraph.get(

new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

Bootstrapping

slide-75
SLIDE 75

public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate();

  • bjectGraph = ObjectGraph.get(

new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

Bootstrapping

slide-76
SLIDE 76

BaseFragment Fragment AddressFragment NameFragment ExampleApp (ObjectGraph)

slide-77
SLIDE 77

public class BaseFragment extends Fragment { @Override public void onCreate(Bundle state) { super.onCreate(state); ((ExampleApp) getActivity() .getApplication()) .objectGraph().inject(this); } }

Fragment Injection

slide-78
SLIDE 78

public class BaseFragment extends Fragment { @Override public void onCreate(Bundle state) { super.onCreate(state); ((ExampleApp) getActivity() .getApplication()) .objectGraph().inject(this); } }

Fragment Injection

slide-79
SLIDE 79

public class BaseFragment extends Fragment { @Override public void onCreate(Bundle state) { super.onCreate(state); ((ExampleApp) getActivity() .getApplication()) .objectGraph().inject(this); } }

Fragment Injection

slide-80
SLIDE 80

Dagger Limitations

  • No final and private field injection
  • No method injection
  • No scopes
  • @Assisted

slide-81
SLIDE 81

Dagger Features

  • Compile time injection
  • Very little magic
  • über fast

slide-82
SLIDE 82

Tape → square.github.com/tape/ Otto → square.github.com/otto/ ActionBarSherlock → actionbarsherlock.com Dagger → square.github.com/dagger

https://github.com/eburke/presentations