Android Pro Recipes

1,363 views

Published on

Presentation held at Iasi-JUG in December 2013
It focuses on corner cases between app life-cycle and AsyncTask and how to solve them.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,363
On SlideShare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
28
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Android Pro Recipes

  1. 1. Android Pro Recipes Gabriel Dogaru gdogaru@gmail.com Iasi-JUG, December 11, 2013
  2. 2. Topics ● App Lifecycle ● AsyncTask problems ● Some frameworks ○ Otto ○ Tape ○ Dagger
  3. 3. Credits ● Android App Anatomy - Eric Burke ● Concurrency in Android - G. Blake Meike ● infoq.com
  4. 4. Where it all starts - lifecyle Install Uninstall App runs forever
  5. 5. Process 1 ……. App runs forever Pn
  6. 6. Activity Activity Process 1 Activity Activity ……. App runs forever Pn
  7. 7. Activity
  8. 8. Fragment Fragment Activity Activity Process 1 Activity ……. App runs forever Pn
  9. 9. RIP Fragment Fragment Activity Activity Process 1 Activity ……. App runs forever Pn
  10. 10. RIP ● ● ● Fragment Fragment Activity Terminates application process Managed by oom_adj RIP Static Variables Activity Process 1 Activity ……. App runs forever Pn
  11. 11. RIP ● Garbage collection Fragment Fragment Activity Activity Process 1 Activity ……. App runs forever Pn
  12. 12. RIP Fragment Fragment Activity Activity Process 1 Activity ……. App runs forever Pn
  13. 13. Why Care?
  14. 14. What about that background task? AsyncTask ● enables proper and easy use of the UI thread. ● should ideally be used for short operations (a few seconds at the most.)
  15. 15. AsyncTask private class SomeTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { //do some stuff return result; } protected void onProgressUpdate(Integer... progress) { //update progress if needed } protected void onPreExecute(Long result) { //before } protected void onPostExecute(Long result) { //after } }
  16. 16. AsyncTask Activity Activity Activity Process 1 ……. App runs forever Pn
  17. 17. AsyncTask Activity Activity Activity Process 1 ……. App runs forever Pn
  18. 18. Some AsyncTask public static class BgTask extends AsyncTask<String, Void, String> { private final Activity act; public BgTask(Activity act) { this.act = act; } @Override protected String doInBackground(String... args) { // … doesn't matter what goes here... } @Override protected void onPostExecute(String args) { act.onTaskComplete(args) } }
  19. 19. Some AsyncTask 2 public void initButton(Button button) { button.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... args) { // … doesn't matter what goes here... } @Override protected void onPostExecute(String args) { onTaskComplete(args) } } .execute(); } }); }
  20. 20. What to do? ● Weak Reference ● Persistent ● Cancellable ● Independent
  21. 21. Weak Reference ● Just make all references to the Activity weak
  22. 22. Weak Reference ● Just make all references to the Activity weak DON’T
  23. 23. Persistent AsyncTask ● Best effort completion ● Hold a reference to the task in static or Application ● Manage references to Activities (onPause, onResume) ● Preserving “single delivery” AsyncTask semantics requires some fancy state management
  24. 24. Persistent AsyncTask public class SafeHeavyweightAsyncTaskActivity extends Activity implements HeavywightSafeTask.CompletionHandler{ static HeavywightSafeTask task; @Override public void onTaskComplete(String result) { task = null; ((TextView) findViewById(R.id.display)).setText(result); } @Override protected void onResume() { super.onResume(); if (null != task) { task.registerCompletionHdlr(this); }} @Override protected void onPause() { super.onStop(); if (null != task) { task.registerCompletionHdlr(null); }} }
  25. 25. Persistent AsyncTask public final class HeavywightSafeTask extends AsyncTask<String, Void, String> { public static interface CompletionHandler { void onTaskComplete(String result); } private enum State { EXECUTING, FINISHED, NOTIFIED; } private State state = State.EXECUTING; private CompletionHandler handler; private String result; public void registerCompletionHdlr(HeavywightSafeTask.CompletionHandler hdlr) { handler = hdlr; notifyHdlr(); } @Override protected String doInBackground(String... params) { // . . . } @Override protected void onPostExecute(String res) { result = res; state = State.FINISHED; notifyHdlr(); } // . . .
  26. 26. Persistent AsyncTask // . . . private void notifyHdlr() { if (null == handler) { return; } switch (state) { case EXECUTING: break; case FINISHED: state = State.NOTIFIED; handler.onTaskComplete(result); break; case NOTIFIED: throw new IllegalStateException( "Attempt to register after notification"); } } }
  27. 27. Cancellable ● Addresses tasks that can be abandoned ● Implement onCancel ● Call it from onPause
  28. 28. Cancellable AsyncTask public class SafeLightweightAsyncTaskActivity extends Activity implements LightweightSafeTask.CompletionHandler { LightweightSafeTask task; // . . . /** * @see android.app.Activity#onPause() */ @Override protected void onPause() { super.onPause(); if (null != task) { task.cancel(true); task = null; } } // . . .
  29. 29. Independent ● Use AsyncTask to submit tasks to a data layer “If you respect users, persist tasksto disk.” - Jesse Wilson
  30. 30. Tape is a collection of queue-related classes for Android and Java by Square, Inc.
  31. 31. Client UI add() Server Task Queue Task Task peek() remove() Service
  32. 32. How to update the ui? background task Activity Activity Activity Process 1 ……. App runs forever Pn
  33. 33. How to update the ui? background task Activity Activity Activity Process 1 ……. App runs forever Pn
  34. 34. Events !!!!! LocalBroadcastManager ● standard Android way ● we all read about it
  35. 35. Publish Intent intent = new Intent(BroadcastIds.LOCATION_UPDATED_ACTION); intent.putExtra(BroadcastIds.CURRENT_LOCATION_KEY ,getCurrentLocation()); LocalBroadcastManager.getInstance( getActivity()).sendBroadcast(intent);
  36. 36. Subscribing // MyActivity BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive( Context context, Intent intent) { Location location = (Location) intent.getParcelableExtra( BroadcastIds.CURRENT_LOCATION_KEY); showLocation(location); } }; //….
  37. 37. Subscribe …. 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); }
  38. 38. Really?
  39. 39. Otto *from Square
  40. 40. 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); } }
  41. 41. Subscribing @Subscribe public void onLocationUpdated(Location l) { showLocation(l); }
  42. 42. Publishing bus.post(getCurrentLocation()); //the first time @Produce public UserImage produceUserImage() { return cachedUserImage; }
  43. 43. Activity @Subscribe Bus bus.post(...) @Produce Data Layer (Cache)
  44. 44. Otto API Summary ● ● ● ● ● register(), unregister(), post() @Subscribe, @Produce Thread confinement Compile time errors Easy to test!
  45. 45. Dependency Injection to the Rescue Dagger ● compile time injection ● based on Google Guice
  46. 46. @Inject import javax.inject.Inject; // JSR-330 public class PublishFragment extends BaseFragment { @Inject Bus bus; @Inject LocationManager locationManager; … }
  47. 47. 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; } }
  48. 48. Module @Module(entryPoints = { MainActivity.class }) public static class ExampleModule { Context appContext; public ExampleModule(Context appContext) { this.appContext = appContext; } @Provides @Singleton Bus provideBus() { return new Bus(); } }}
  49. 49. Integration 2 public class ExampleApp extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.create(new ExampleModule(this)); } public ObjectGraph objectGraph() { return objectGraph; } public <T> T get(Class<T> clazz) { return objectGraph.get(clazz); }
  50. 50. Integration 3 public class MainActivity extends Activity { @Inject Bus bus; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((ExampleApp) getApplication()).objectGraph().inject(this); } }
  51. 51. Conclusions ● Managing the lifecycle can be tricky ● Always think of the corner cases ● Find the right tool to do the job and make your life easier ● Event architecture can help
  52. 52. Further studdy Tape → square.github.com/tape/ Otto → square.github.com/otto/ Dagger → square.github.com/dagger

×