55
Android Pro Recipes Gabriel Dogaru [email protected] Iasi-JUG, December 11, 2013

Android Pro Recipes

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: Android Pro Recipes

Android Pro Recipes

Gabriel [email protected]

Iasi-JUG, December 11, 2013

Page 2: Android Pro Recipes

Topics

● App Lifecycle● AsyncTask problems● Some frameworks

○ Otto○ Tape○ Dagger

Page 3: Android Pro Recipes

Credits

● Android App Anatomy - Eric Burke● Concurrency in Android - G. Blake Meike ● infoq.com

Page 4: Android Pro Recipes

Where it all starts - lifecyle

App runs forever

Install Uninstall

Page 5: Android Pro Recipes

App runs forever

Process 1 P n…….

Page 6: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity Activity Activity

Page 7: Android Pro Recipes

Activity

Page 8: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity ActivityFragment Fragment

Page 9: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity ActivityFragment Fragment

RIP

Page 10: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity ActivityFragment Fragment

RIP● Terminates application process● Managed by oom_adj● RIP Static Variables

Page 11: Android Pro Recipes
Page 12: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity ActivityFragment Fragment

RIP● Garbage collection

Page 13: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity ActivityFragment Fragment

RIP

Page 14: Android Pro Recipes
Page 15: Android Pro Recipes

Why Care?

Page 16: Android Pro Recipes

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

Page 17: Android Pro Recipes

AsyncTaskprivate 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 }}

Page 18: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity Activity

AsyncTask

Page 19: Android Pro Recipes

App runs forever

Process 1 P n…….

Activity Activity Activity

AsyncTask

Page 20: Android Pro Recipes

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) }}

Page 21: Android Pro Recipes

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(); } });}

Page 22: Android Pro Recipes

What to do?

● Weak Reference● Persistent● Cancellable● Independent

Page 23: Android Pro Recipes

Weak Reference

● Just make all references to the Activity weak

Page 24: Android Pro Recipes

Weak Reference

● Just make all references to the Activity weak

DON’T

Page 25: Android Pro Recipes

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 semanticsrequires some fancy state management

Page 26: Android Pro Recipes

Persistent AsyncTaskpublic 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); }} }

Page 27: Android Pro Recipes

Persistent AsyncTaskpublic 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(); }// . . .

Page 28: Android Pro Recipes

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"); }}}

Page 29: Android Pro Recipes

Cancellable

● Addresses tasks that can be abandoned● Implement onCancel● Call it from onPause

Page 30: Android Pro Recipes

Cancellable AsyncTaskpublic 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; } }// . . .

Page 31: Android Pro Recipes

Independent

● Use AsyncTask to submit tasks to a data layer

“If you respect users, persist tasksto disk.”- Jesse Wilson

Page 32: Android Pro Recipes

Tape is a collection of queue-related classes for Android and Java by Square, Inc.

Page 33: Android Pro Recipes

Client UI

Task Task Service

Server

Task Queueadd() peek()remove()

Page 34: Android Pro Recipes

How to update the ui?

App runs forever

Process 1 P n…….

Activity Activity Activity

background task

Page 35: Android Pro Recipes

How to update the ui?

App runs forever

Process 1 P n…….

Activity Activity Activity

background task

Page 36: Android Pro Recipes

Events !!!!!

LocalBroadcastManager● standard Android way● we all read about it

Page 37: Android Pro Recipes

Publish

Intent intent = new Intent(BroadcastIds.LOCATION_UPDATED_ACTION);

intent.putExtra(BroadcastIds.CURRENT_LOCATION_KEY,getCurrentLocation());

LocalBroadcastManager.getInstance( getActivity()).sendBroadcast(intent);

Page 38: Android Pro Recipes

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); }};

//….

Page 39: Android Pro Recipes

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);}

Page 40: Android Pro Recipes

Really?

Page 41: Android Pro Recipes

Otto

*from Square

Page 42: Android Pro Recipes

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); }}

Page 43: Android Pro Recipes

Subscribing

@Subscribepublic void onLocationUpdated(Location l) { showLocation(l);}

Page 44: Android Pro Recipes

Publishing

bus.post(getCurrentLocation());

//the first time

@Producepublic UserImage produceUserImage() { return cachedUserImage;}

Page 45: Android Pro Recipes

Data Layer (Cache)

Bus

Activity

bus.post(...)@Produce

@Subscribe

Page 46: Android Pro Recipes

Otto API Summary

● register(), unregister(), post()● @Subscribe, @Produce● Thread confinement● Compile time errors● Easy to test!

Page 47: Android Pro Recipes

Dependency Injection to the Rescue

Dagger ● compile time injection● based on Google Guice

Page 48: Android Pro Recipes

@Injectimport javax.inject.Inject; // JSR-330public class PublishFragment extends BaseFragment {

@Inject Bus bus; @Inject LocationManager locationManager; …

}

Page 49: Android Pro Recipes

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; }}

Page 50: Android Pro Recipes

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(); } }}

Page 51: Android Pro Recipes

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); }

Page 52: Android Pro Recipes

Integration 3public 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); }

}

Page 53: Android Pro Recipes

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

Page 54: Android Pro Recipes

Further studdy

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

Page 55: Android Pro Recipes