android app anatomy
play

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


  1. Android App Anatomy Eric Burke Square @burke_eric

  2. Topics • Android lifecycle • Fragments • Open source • Tape • Otto • Dagger

  3. ActionBarSherlock Android Design on every device.

  4. Lifecycle

  5. Install Apps Run Forever Uninstall

  6. Process 1 Process 2 Apps Run Forever

  7. Activity Activity Activity Process 1 Process 2 Apps Run Forever

  8. R.I.P . Static Variables Activity Activity Activity Process 1 Process 2 Apps Run Forever

  9. Kill your process

  10. Tape

  11. “If you respect users, persist tasks to disk.” - Jesse Wilson

  12. Don’t Do This

  13. Server Loader or Client UI Thread Image File

  14. Do This Pending

  15. Tape API • QueueFile • O(1) FIFO queue of byte[] • ObjectQueue • A queue of <T> • TaskQueue • Injects and starts tasks

  16. Tape Server Client UI TaskQueue peek() remove() add() Service UploadTask UploadTask

  17. 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));

  18. 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.

  19. 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

  20. Fragment Lifecycle

  21. onCreate() onSaveInstanceState() Activity Time ➞

  22. Tap the Upload Button Activity Time ➞

  23. Loader or Thread Activity Time ➞

  24. DialogFragment beginTransaction() Loader or Thread Activity Time ➞

  25. DialogFragment commit() Loader or Thread Activity onSaveInstanceState() Time ➞

  26. Square does not* use Loaders.

  27. Fragment Layout

  28. Advantages • Smooth animations between steps • ActionBar does not move • Code organization

  29. Onboarding Fragment Fragment Fragment Payment Flow Settings Fragment Fragment Fragment Fragment Fragment Fragment

  30. Fragment → Activity Communication implements Containing Activity defines Listener Fragment Interface http://developer.android.com/training/basics/fragments/communicating.html

  31. Listener Interface 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; } }

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

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

  34. Fragment Fragment Fragment Activity Fragment Fragment Fragment How can Fragments communicate without tight Activity coupling?

  35. Publish Subscribe Fragment Fragment Bus Fragment Fragment Service Activity

  36. LocalBroadcastManager • Included in Android Support library • Publishes Intents within your process

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

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

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

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

  41. Subscribing 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); }

  42. Boilerplate. No type safety. Hard to test.

  43. Otto

  44. Registration 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); } }

  45. Registration 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); Fails fast at } } runtime.

  46. Subscribing @Subscribe public void onLocationUpdated(Location l) { showLocation(l); } Receives any type extending Location.

  47. Publishing bus.post (getCurrentLocation()); Synchronous delivery.

  48. @Produce (getting data the first time)

  49. How to get this image?

  50. post(UserImage) Downloader Bus UserImageCache

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

  52. get() done() onResume() onPause() Downloader Activity UserImageCache

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

  54. Activity @Subscribe Bus @Produce UserImageCache

  55. @Produce decouples threads from the Activity and Fragment lifecycle.

  56. Otto API Summary • register(), unregister(), post() • @Subscribe, @Produce • Thread confinement • Easy to test!

  57. Origins of Otto • Forked from Guava’s EventBus • Optimized for Android - 16k! • Less reflection; more caching

  58. Dependency Injection

  59. Guice on Android? • We’ve used it for 2+ years • Startup performance is a challenge • Runtime error checking • See also: RoboGuice

  60. Can we do better?

  61. Dagger †

  62. † What is Dagger? Compile-time dependency injection.

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

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

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

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

  67. † 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

  68. † Bootstrapping public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.get( new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

  69. † Bootstrapping public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.get( new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

  70. † Bootstrapping public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.get( new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

  71. † Bootstrapping public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.get( new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } }

  72. † Fragment ExampleApp (ObjectGraph) BaseFragment NameFragment AddressFragment

Recommend


More recommend