Deep dive into Android async operations

  • Published on
    15-Jul-2015

  • View
    699

  • Download
    2

Embed Size (px)

Transcript

  • Deep diveinto Android async operations

    Mateusz Grzechociski@mgrzechocinski

    Demo app source codehttps://github.com/mgrzechocinski/DroidconKrakow2014

  • Whats it for?

    Confitura 2010

    Review & share own experience

    ...and would appreciate yours

    From developer to (intermediate) developers

  • Meet Adam

  • ...and his story

  • Adam, a developer who knows

    whats Looper.loop()

    what ANR is

    Long operation? Spawn a thread!

  • Requirements

    Bundesliga matches results

    Refresh on click

  • Simple enough?

    REST client 1 activity 1 button, refresh = call URLs in spawned threads

  • StrictModepublic class DemoApp extends Application {

    @Override

    public void onCreate() {

    super.onCreate();

    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()

    .detectDiskReads()

    .detectDiskWrites()

    .detectNetwork()

    .penaltyLog()

    .build());

    }

    }

  • "AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

    Android SDK docs

  • Performing network operations

  • DEMO 1AsyncTask - multiple calls

  • < DONUT DONUT >=HONEYCOMB

    SERIAL PARALLEL SERIAL

    AsyncTask.executeOnExecutor(Executor)

  • DEMO 1aAsyncTask - memory leak

  • Solutions

    Track activity/fragment lifecycle manually cancel it?

    Use fragment with setRetainInstance

    (true)

  • AsyncTask

    Pros Simple Great for few seconds

    operations Cancellation support

    Cons Memory leaks, Possible crashes Behaves different Lack of exceptions handling

    (or RoboAsyncTask)

    http://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-or-am-i-just-missing-something

  • Adam wonders: Really a good idea?

    run task change config (e.g. by rotating the screen)

    cancel previous task run new task

    update UI

  • Loaders

    HONEYCOMB+ support-v4

  • Loaders, in particular CursorLoader, are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment's onStop() and onStart() methods, so that when users return to an application, they don't have to wait for the data to reload.

    Android SDK docs

  • AsyncTaskLoaderThis class performs the same function as the AsyncTask, but a bit better. It can handle Activity configuration changes more easily (add MG: by Loader Manager), and it behaves within the life cycles of Fragments and Activities.

    http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html

  • How it works...

    LoaderManager initLoader LoaderCallback CursorLoader AsyncTaskLoader

  • DEMO 2Loader - first shoot and

    nothing

    WHY?

  • @Override

    protected void onStartLoading() {

    super.onStartLoading();

    Log.d(LOG_TAG, "onStartLoading");

    + forceLoad();

    }

  • DEMO 3Loader - forceLoad()

    config change?

  • @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_matches);

    if (savedInstanceState != null) {

    + getSupportLoaderManager().initLoader(LOADER_ID, args, this);

    //restore state...

    }

    }

  • DEMO 4Loader - config change

    leaving activity?

  • @Override

    protected void onStartLoading() {

    super.onStartLoading();

    Log.d(LOG_TAG, "onStartLoading");

    + if(result == null){

    forceLoad();

    + }else{

    + deliverResult(result);

    + }

    }

  • DEMO 5Loader - leaving activity (home etc)

    force refresh?

  • @Override

    public void onLoadFinished(Loader loader, Match match) {

    Log.d(LOG_TAG, "onLoadFinished: " + match);

    currentMatches.add(match);

    adapter.setCurrentMatches(currentMatches);

    adapter.notifyDataSetChanged();

    + getSupportLoaderManager().destroyLoader(LOADER_ID);

    }

  • DEMO 6Loader - force refresh

  • Loader

    Pros Better than AsyncTask, much Gives partial caching

    Need in-loader result caching when leaving activity

    No control No memory leaks

    Cons Designed to support DB data

    (cursor) Hacking, hacking, hacking to

    use in networking Poorly documented Opposite to AsyncTask

    too highly coupled Poor exception management

  • https://github.com/stephanenicolas/robospice/wiki/A-User's-Perspective-on-RoboSpice

  • The idea[...]

    Basically, what happens with RS is that when a request is being processed, its listeners will be invoked as long as the associated activity is alive.

    [...]

    The main purpose of RS is to make sure that there is no memory leak : your activity, if it has to die, will die and be garbage collected, RS doesn't hold any hard reference to it that would prevent garbage collection. That's really the core idea behind RoboSpice. [...]

    Stephen Nicolashttp://stackoverflow.com/questions/19011200/how-does-robospice-manage-activity-lifecycle

  • https://www.youtube.com/watch?v=xHXn3Kg2IQE

  • Show me do code!

  • DEMO 7RoboSpice - basic

    config change refresh() no caching

  • @Override

    protected void onStart() {

    super.onStart();

    spiceManager.start(this);

    + spiceManager.addListenerIfPending(Match.class, CACHE_KEY, this);

    }

    Config change

  • Force refresh

    private void loadMatchesOnClick() {

    int matchID = 1;

    MatchResultRequest request = new MatchResultRequest(Match.class, matchID);

    spiceManager.execute(request, CACHE_KEY, DurationInMillis.ALWAYS_EXPIRED, this);

    }

    or

    spiceManager.removeDataFromCache(Match.class, CACHE_KEY);

  • DEMO 8RoboSpice - extended

  • By default Handling config changes

    Full control caching

    Multithreading

    No internet connection NoNetworkException.class

    Modules to SpringAndroid, Retrofit

    and much more...

  • RoboSpice

    Pros Robust, full featured Last commit < 3 months ago

    Cons POJO per Request No a simple library

    SpiceManager with 1300 LOC

  • Go Reactive

    ReactiveExtentions by MS

    Ported by Netflix

    rxAndroid = rxJava + few Android classes

    rxJava hit 1.0

    rxAndroid still 0.x

  • https://github.com/ReactiveX/RxJava/wiki

  • Motivations"[...] If a calling Activity/Fragment makes multiple requests, it needs to handle responses in a single function by switching on the URL. This makes for code that isnt very readable, especially when you have to jump from where the request is executed to where the response is handled"

    http://markhudnall.com/2013/10/15/rxjava-and-android/

  • MotivationsIts still a little verbose, but its more similar to writing synchronous code. I use the response in code that directly follows the request. I also get error handling on the UI thread for free.

    http://markhudnall.com/2013/10/15/rxjava-and-android/

  • Iterable Observable

    getDataFromLocalMemory()

    .skip(10)

    .take(5)

    .map({ s -> return s + " transformed" })

    .forEach({ println "next => " + it })

    getDataFromNetwork()

    .skip(10)

    .take(5)

    .map({ s -> return s + " transformed" })

    .subscribe({ println "onNext => " + it })

  • getDataFromNetwork()

    .skip(10)

    .take(5)

    .map({ s -> return s + " transformed" })

    .subscribe({ println "onNext => " + it })

  • DEMO 9RxAndroid in UI Layer

  • Worth trying?

    Streams of events e.g. Weather station, tweets analyzer

    In common app?

    When using Retrofit@GET("/user/{id}/photo")

    Observable getUserPhoto(@Path("id") int id);

  • rxAndroid

    Pros Reduced callback hell with

    FPP Robust set of

    functions/transformations

    Cons Need to change the way of

    thinking Hard, really On Android - even harder Possible memory leaks? Debugging?

  • Out of scope or time

    IntentService (!= Service)

    Event buses (Otto, GreenRobots EventBus) especially with Loaders

    Volley

    Ion

  • Remember

    Dont use AsyncTask

    Consider using Loaders

    Give a try RoboSpice

    Learn FRP (rxJava/rxAndroid)

  • Thanks

    Mateusz Grzechociski

    @mgrzechocinskihttp://grzechocinski.net