167
Advanced sessions Threading Yossi Segev 12/7/2017 3

Advanced #3 threading

Embed Size (px)

Citation preview

Page 1: Advanced #3  threading

Advanced sessions

Threading

Yossi Segev12/7/2017

3

Page 2: Advanced #3  threading

Hi !

Page 3: Advanced #3  threading

The Largest Android Community

Android Academy - TLV

TLV - Android Academy

Join Us:FundamentalsAdvanced

With designersHackathons

Mentors

Page 4: Advanced #3  threading

Yossi Segev

Android @ Colu

Android Academy

Page 5: Advanced #3  threading
Page 6: Advanced #3  threading

Advanced sessions

Threading

Yossi Segev12/7/2017

3

Page 7: Advanced #3  threading

Application Process

Main Thread

When launching an app...

Page 8: Advanced #3  threading

The Main Thread

UI rendering

Application components

User interaction

Lifecycle callbacks

Main threadQueue

Page 9: Advanced #3  threading

The Main thread is very busy

Page 10: Advanced #3  threading

● When blocked - system shows ANR dialog

● Exception on network operations

● Exclusive interaction with the UI

android.os.NetworkOnMainThreadException

Page 11: Advanced #3  threading

Leaving the Main thread

Page 12: Advanced #3  threading

java.lang.Thread

Page 13: Advanced #3  threading

java.lang.Thread

public class MyThread extends Thread {

@Override

public void run() {

doStuff();

}

}

MyThread myThread = new MyThread();

myThread.start();

Page 14: Advanced #3  threading

java.lang.Thread

public class MyRunnable implements Runnable {

@Override

public void run() {

doStuff();

}

}

Thread myThread = new Thread(new MyRunnable());

myThread.start();

Page 15: Advanced #3  threading

java.lang.Thread

new

MyThread()

start() run()

● One-time use only.

● Dies after executing it’s run() method.

● It’s not legal to start a thread more than once.

Page 16: Advanced #3  threading

How to stop a thread?

Page 17: Advanced #3  threading

Stopping a thread

● Return from the run() method.

Page 18: Advanced #3  threading

Stopping a thread

● Return from the run() method.

● Use cancellation flag.

Page 19: Advanced #3  threading

Cancellation flag

public class MyThread extends Thread {

private boolean mRunning;

@Overridepublic void run() {

mRunning = true;

while (mRunning) {// Do some work...

}}

public void cancel() { mRunning = false; }}

Page 20: Advanced #3  threading

Cancellation flag

public class MyThread extends Thread {

private boolean mRunning;

@Overridepublic void run() {

mRunning = true;

while (mRunning) {// Do some work...

}}

public void cancel() { mRunning = false; }}

Page 21: Advanced #3  threading

Cancellation flag

public class MyThread extends Thread {

private boolean mRunning;

@Overridepublic void run() {

mRunning = true;

while (mRunning) {// Do some work...

}}

public void cancel() { mRunning = false; }}

Page 22: Advanced #3  threading

Stopping a thread

● Return from the run() method.

● Use cancellation flag.

● Call the Thread.interrupt() method.

Page 23: Advanced #3  threading

Thread.interrupt()

Not a magic method!

The implementation is up to us!

Page 24: Advanced #3  threading

Handling interrupt()

public class MyThread extends Thread {

@Overridepublic void run() {

while (!isInterrupted()) {// Do some work...

}}

}

Page 25: Advanced #3  threading

Handling interrupt()

@Override

public void run() {

while (!isInterrupted()) {

// Do some work...

try {

sleep(1000);

} catch (InterruptedException e) {

Log.d("MyThread", "Interrupted!");

break;

}

}

}

Page 26: Advanced #3  threading

Stopping a thread

● Return from the run() method.

● Use cancellation flag.

● Call the Thread.interrupt() method.

!!! DO NOT USE Thread.stop() !!!

Page 27: Advanced #3  threading

Back to the Main thread

Page 28: Advanced #3  threading

Back to the Main thread

We can:

● Activity.runOnUiThread(runnable)

● View.post(runnable)

Page 29: Advanced #3  threading

Back to the Main thread

runOnUiThread(new Runnable() {

@Override

public void run() {

mTextView.setText("Some data update");

}

});

Page 30: Advanced #3  threading

Fixing the crash - CounterActivity

mTextView.post(new Runnable() {

@Override

public void run() {

mTextView.setText("Some data update");

}

});

Page 31: Advanced #3  threading

How does it work??

Page 32: Advanced #3  threading

Activity.java

final Handler mHandler = new Handler();

mUiThread = Thread.currentThread();

public final void runOnUiThread(Runnable action) {

if (Thread.currentThread() != mUiThread) {

mHandler.post(action);

} else {

action.run();

}

}

Page 33: Advanced #3  threading

View.java

public boolean post(Runnable action) {

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

return attachInfo.mHandler.post(action);

}

getRunQueue().post(action);

return true;

}

Page 34: Advanced #3  threading

AndroidThreads

Communication mechanism

Page 35: Advanced #3  threading

Android Threads communication - Why?

Java threads communication mechanisms holds a blocking

risk. (ex: Pipes)

Interaction between threads in Android usually involves the

main thread.

Android defines it’s own non-blocking thread

communication mechanism.

Page 36: Advanced #3  threading

Introducing...

Page 37: Advanced #3  threading

The Handler

The Looperand the

MessageQue

ue

Page 38: Advanced #3  threading

android.os.Message

Two types of Messages:

Data Message

Task Message

Page 39: Advanced #3  threading

Message holding data.

android.os.Message

Field Name Description

what int - Unique identifier

when long - timestamp

arg1, arg2 int - “Low cost” parameters

obj Object

data Bundle

Page 40: Advanced #3  threading

Message holding a task (Runnable).

android.os.Message

Field Name Description

callback Runnable task

Page 41: Advanced #3  threading

android.os.Handler

● Creating messages.

● Inserting messages to the queue.

● Removing messages in the queue.

● Consuming messages.

Page 42: Advanced #3  threading

android.os.Looper

● Dispatching messages from its queue to Handlers.

● Only one Looper per thread.

● Keeping its thread alive.

● Thread don’t get a Looper by default.*

Page 43: Advanced #3  threading

Overview

Thread

Looper

MessageQueue

Page 44: Advanced #3  threading

Overview

Thread

Looper

MessageQueue

Handler

Page 45: Advanced #3  threading

Overview

Thread

Looper

MessageQueue

HandlerHandler

Page 46: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 47: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 48: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 49: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 50: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 51: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 52: Advanced #3  threading

Overview

Thread

Looper

HandlerHandler

MessageQueue

Page 53: Advanced #3  threading

Enough theoryLet’s see some code

Page 54: Advanced #3  threading

1. A Looper is created and attached to a thread.

2. Handler is created.

3. The Handler create a Message.

4. The Handler insert the Message into the Looper queue.

5. The Handler is consuming/executing the message.

Overview

Page 55: Advanced #3  threading

FancyThread.java

public class FancyThread extends Thread {

@Override

public void run() {

Looper.prepare();

// Code is missing for simplicity

Looper.loop();

}

}

Page 56: Advanced #3  threading

1. A Looper is created and attached to a thread.

2. Handler is created.

3. The Handler create a Message.

4. The Handler insert the Message into the Looper queue.

5. The Handler is consuming/executing the message.

Overview

Page 57: Advanced #3  threading

Handler creation

mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

// do stuff

}

};

mHandler = new Handler(new Handler.Callback() {

@Override

public boolean handleMessage(Message msg) {

return false; // return true if msg consumed

}

});

//Or...

Page 58: Advanced #3  threading

Handler creation

mHandler = new Handler(Looper looper) {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

// do stuff

}

};

mHandler = new Handler(Looper looper, new Handler.Callback() {

@Override

public boolean handleMessage(Message msg) {

return false; // return true if msg consumed

}

});

//Or...

Page 59: Advanced #3  threading

FancyThread.java

@Override

public void run() {

Looper.prepare();

mHandler = new Handler(Looper.myLooper(), callback);

Looper.loop();

}

Page 60: Advanced #3  threading

FancyThread.java

public class FancyThread extends Thread {

// …

public Handler getHandler() {

return mHandler;

}

}

Page 61: Advanced #3  threading

1. A Looper is created and attached to a thread.

2. Handler is created.

3. The Handler create a Message.

4. The Handler insert the Message into the Looper queue.

5. The Handler is consuming the message.

Overview

Page 62: Advanced #3  threading

Creating a Message

Page 63: Advanced #3  threading

Creating a Message// Data messages

Message.obtain(Handler h);

Message.obtain(Handler h, int what);

Message.obtain(Handler h, int what, Object o);

Message.obtain(Handler h, int what, int arg1, int arg2);

Message.obtain(Handler h, int what, int arg1, int arg2, Object o);

// Task message

Message.obtain(Handler h, Runnable task);

// Copy constructor

Message.obtain(Message originalMsg);

// Add data Bundle to existing message

msg.setData(Bundle bundle);

Page 64: Advanced #3  threading

Creating a Message with Handler

Message msg = handler.obtainMessage(/*...*/);

Page 65: Advanced #3  threading

Creating a Message

Message message = Message.obtain(); // Return empty message from pool

message.what = 4;

message.arg1 = 100;

message.arg2 = 200;

message.obj = new Object();

message.setData(bundle);

Page 66: Advanced #3  threading

Creating a Message

!! Avoid !!

Message message = new Message();

Page 67: Advanced #3  threading

1. A Looper is created and attached to a thread.

2. Handler is created.

3. The Handler create a Message.

4. The Handler insert the Message into the Looper queue.

5. The Handler is consuming the message.

Overview

Page 68: Advanced #3  threading

Insert a Messageto the queue

Page 69: Advanced #3  threading

Inserting data message to the queue

boolean handler.sendMessage(msg);

boolean handler.sendMessageAtFrontOfQueue(msg);

boolean handler.sendMessageDelayed(msg, 2000);

// And more...

Page 70: Advanced #3  threading

Inserting task message

boolean handler.post(Runnable r)

boolean handler.postAtFrontOfQueue(Runnable r)

boolean handler.postDelayed(Runnable r, long delayMillis)

// And more...

Page 71: Advanced #3  threading

Inserting messages to the queue

// Reference to our Handler

Handler fancyHandler = mFancyThread.getHandler();

// Create data message with what = 1

Message msg = fancyHandler.obtainMessage(1);

// Adding the message to the queue

fancyHandler.sendMessage(msg);

// Adding task message to the queue

fancyHandler.post(new MyRunnable());

Page 72: Advanced #3  threading

Removing a Messagefrom the queue

Page 73: Advanced #3  threading

Removing a Message from the queue

// Remove task message

Handler.removeCallbacks(Runnable r);

// Remove data messages

Handler.removeMessages(int what);

// Remove everything

Handler.removeCallbacksAndMessages(null);

// AND more...

Page 74: Advanced #3  threading

1. A Looper is created and attached to a thread.

2. Handler is created.

3. The Handler create a Message.

4. The Handler insert the Message into the Looper queue.

5. The Handler is consuming the message.

Overview

Page 75: Advanced #3  threading

Consuming - Overriding handleMessage()

mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what) {

case 1:

Logger.d("Received message type - 1");

break;

case 2:

Logger.d("Received message type - 2");

break;

}

}

};

Page 76: Advanced #3  threading

Consuming - Handler.Callback

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

case 1:

Logger.d("Received message type - 1");

return true;

case 2:

Logger.d("Received message type - 2");

return true;

default:

return false;

}

}

The Message is handled?

Page 77: Advanced #3  threading

Consuming task message

// No extra work is needed.

fancyHandler.post(new Runnable() {

@Override

public void run() {

// Will run on the Handler thread.

}

});

Page 78: Advanced #3  threading

Keep it clean!

Don’t forget to call quit()

on your Looper when it is no

longer needed!

Page 79: Advanced #3  threading

FancyActivity.java

@Override

protected void onStop() {

super.onStop();

mFancyThread.getHandler().post(new Runnable() {

@Override

public void run() {

Looper.myLooper().quit();

}

});

}

Option #1

Page 80: Advanced #3  threading

FancyActivity.java

@Override

protected void onStop() {

super.onStop();

mFancyThread.getHandler().sendEmptyMessage(-1);

}

Option #2

Page 81: Advanced #3  threading

FancyThread.java

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

case -1:

Looper.myLooper().quit();

return true;

}

}

Option #2

Page 82: Advanced #3  threading

Looper.quit() VS .quitSafely()

.quit() .quitSafely()

● All pending in the queue, Including

messages ready to be dispatched - are

discarded.

● Looper won’t process new messages.

● Discards only messages that are not

ready to dispatch. Ready messages will

be dispatched.

● Looper won’t process new messages.

● Only from API 18.

Page 83: Advanced #3  threading

Phew… Questions?

Page 84: Advanced #3  threading

P.S:

The Main thread is

just a “regular”

thread with a Looper.

Page 85: Advanced #3  threading

The Main thread Looper

The Main thread is the only

thread with a Looper by default.

The Looper cannot be stopped.

You can access the Main thread Looper from anywhere

with: Looper.getMainLooper()

Page 86: Advanced #3  threading

Main thread Handler creation

Handler mainHandler = new Handler(Looper.getMainLooper());

mainHandler.post(new Runnable() {

@Override

public void run() {

Log.d(TAG, "I'm running on the Main thread.");

}

});

Page 87: Advanced #3  threading

Messages passing is a powerful async mechanism but it can be hard to implement from scratch.

Luckily, You don’t have too...

Page 88: Advanced #3  threading

Android Threading primitives

(quick) overview

Page 89: Advanced #3  threading

HandlerThread

Flexible tasks chaining in a queue.

Page 90: Advanced #3  threading

HandlerThread - Example use case

“Our app onboarding flow has 6

different network calls.”

Page 91: Advanced #3  threading

HandlerThread

A Thread with a “free” Looper implementation.

Page 92: Advanced #3  threading

HandlerThread

HandlerThread handlerThread = new HandlerThread("MyHandlerThread");

//Make sure to call start() before creating the Handler!

handlerThread.start();

Handler handler = new Handler(mMyHandlerThread.getLooper());

Accessing the thread Looper

Page 93: Advanced #3  threading

Creating a custom HandlerThread

Page 94: Advanced #3  threading

HandlerThread

Override

onLooperPrepared()

for additional setup .

Page 95: Advanced #3  threading

Custom HandlerThreadpublic class MyHandlerThread extends HandlerThread {

private Handler mHandler;

public MyHandlerThread() { super("MyHandlerThread"); }

@Override

protected void onLooperPrepared() {

super.onLooperPrepared();

mHandler = new Handler(Looper.myLooper());

}

public void sayHi() {

mHandler.post(new Runnable() {

@Override

public void run() { Log.d("MyHandlerThread", "Hi!"); }

});

}

}

Called after Looper.prepare()

Page 96: Advanced #3  threading

HandlerThread - Stopping

handlerThread.quit();

// or

handlerThread.quitSafely(); // API >= 18

Page 97: Advanced #3  threading

HandlerThread

Remember to quit().

Call start() before calling getLooper()

Page 98: Advanced #3  threading

HandlerThread

Flexible, reusable tasks chaining.

Guarantees thread safety.

Implementation can take a while.

Page 99: Advanced #3  threading

IntentService

“Fire and forget” long-running operations.

Page 100: Advanced #3  threading

IntentService - Example use case

“I want to preload data from my

server to my local DB even

when my app is in the

foreground.”

Page 101: Advanced #3  threading

IntentService

● Subclass of Service.

● Executes operation on a background thread.

● Multiple startService() calls function as a work queue.

● Automatically stops when out of work.

Page 102: Advanced #3  threading

IntentService work queue

startService(Intent i1);

startService(Intent i2);

startService(Intent i3);

onCreate()

onHandleIntent(i1) onHandleIntent(i2) onHandleIntent(i3)

onDestory()

Page 103: Advanced #3  threading

Creating IntentService

Page 104: Advanced #3  threading

IntentService

public class MyIntentService extends IntentService {

public MyIntentService() {

super("MyIntentService"); // This will be the worker thread name

}

@Override

protected void onHandleIntent(Intent intent) {

// Do some work in the background...

}

} Reference to the calling Intent

Page 105: Advanced #3  threading

IntentService as a work queuepublic class MyIntentService extends IntentService {

public MyIntentService() { super("MyIntentService"); }

public static Intent start(Context context, String name) {

Intent i = new Intent(context, MyIntentService.class);

i.putExtra("extra_name", name);

return i;

}

@Override

protected void onHandleIntent(@Nullable Intent intent) {

String name = intent.getStringExtra("extra_name");

for (int i = 1 ; i <= 3 ; i ++) {

Log.d("MyIntentService", name + " counts " + i);

}

}

}

Extracting data from Intent

Convenient methodto create Intent with

data.

Page 106: Advanced #3  threading

IntentService as a work queuepublic class MyIntentService extends IntentService {

// …

@Override

public void onCreate() {

super.onCreate();

Logger.d("onCreate() called.”);

}

@Override

public void onDestroy() {

super.onDestroy();

Logger.d("onDestory() called.");

}

}

Page 107: Advanced #3  threading

IntentService as a work queue

startService(MyIntentService.start(this, "John"));

startService(MyIntentService.start(this, "Elvis"));

startService(MyIntentService.start(this, "Sara"));

Page 108: Advanced #3  threading

Log outputD/Academy: onCreate() called.

D/MyIntentService: John counts 1

D/MyIntentService: John counts 2

D/MyIntentService: John counts 3

D/MyIntentService: Elvis counts 1

D/MyIntentService: Elvis counts 2

D/MyIntentService: Elvis counts 3

D/MyIntentService: Sara counts 1

D/MyIntentService: Sara counts 2

D/MyIntentService: Sara counts 3

D/Academy: onDestory() called.

Page 109: Advanced #3  threading

IntentService

Perfect for “fire and forget” long-running operations.

Built-in queue mechanism. Auto stop when work is done.

Is a Service - Less “killable”.

Easy to implement.

Callbacks to UI are complicated. (ResultReceiver)

Cannot cancel.

Page 110: Advanced #3  threading

AsyncTask

Quick “in-and-out” operations.

Page 111: Advanced #3  threading

AsyncTask - Example use case

“I need to fetch a bitmap from a

URL and populate an

ImageView. ”

Page 112: Advanced #3  threading

AsyncTask

Easily get off the Main thread to perform a task.

Return results back to the Main thread to update UI.

Page 113: Advanced #3  threading

AsyncTask

public class MyAsyncTask extends AsyncTask<Params, Progress, Result> {

@Override

protected void onPreExecute() { ...}

@Override

protected Result doInBackground(Params... params) { ...} // Runs in a background thread

@Override

protected void onProgressUpdate(Progress... progress) { ...}

@Override

protected void onPostExecute(Result result) { ...}

@Override

protected void onCancelled(Result result) { ...}

}

Page 114: Advanced #3  threading

DownloadImageTask.javapublic class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

private ImageView mImageView;

public DownloadImageTask(ImageView imageView) {

mImageView = imageView;

}

@Override

protected Bitmap doInBackground(String... params) {

String url = params[0];

Bitmap bitmap = downloadImageFromUrl(url);

return bitmap;

}

@Override

protected void onPostExecute(Bitmap bitmap) {

super.onPostExecute(bitmap);

mImageView.setImageBitmap(bitmap);

}

}

Page 115: Advanced #3  threading

Executing AsyncTask

DownloadImageTask asyncTask = new DownloadImageTask(imageView);

asyncTask.execute(imageUrl);

// If API >= 11:

// or

asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageUrl);

// or

asyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, imageUrl);

// or

asyncTask.executeOnExecutor(myCustomExecutor, imageUrl);

Page 116: Advanced #3  threading

Cancelling running AsyncTask

asyncTask.cancel(true);

@Override

protected Integer doInBackground(Void... params) {

int currentCount = 0;

while (currentCount < 10 && !isCancelled()) { // Checking if canceled periodically

currentCount ++;

}

return currentCount;

}

Also interrupt the thread?

Page 117: Advanced #3  threading

AsyncTask

Don’t let the abstraction fool you!

If you can - specify executor.

Always use cancel()!

Be vigilant. Avoid memory leaks!

Use only as static inner class or as a stand-alone class.

Remember to release any UI reference .

Page 118: Advanced #3  threading

AsyncTask

Useful for quick “in-and-out” operations.

Easy progress and results update on the UI.

Easy to implement.

Easily misused. Handle with caution!

Default implementation changing based on API level.

Page 119: Advanced #3  threading

Loaders

Handle configurations changes gracefully.

Page 120: Advanced #3  threading

Loaders - Example use case

“I’m querying data from a

Content provider.”

Page 121: Advanced #3  threading

Loaders

● Can Load data asynchronously.

● Coordinating with Activity lifecycle.

● Surviving configuration changes.

Page 122: Advanced #3  threading

Loaders

Loader<D>The base class of all Loaders.

AsyncTaskLoader<D>Subclass of Loader<D>, uses AsyncTask to do its

work in the background.

CursorLoader<D>Subclass of AsyncTaskLoader<Cursor>, built to

query ContentProviders and monitor their data.

Page 123: Advanced #3  threading

Loader

Loader

Doing background work ● Loading data asynchronously.

Page 124: Advanced #3  threading

Custom Loader - SimpleLoader.javapublic class SimpleLoader extends AsyncTaskLoader<String> {

public SimpleLoader(Context context) { super(context); }

@Override

protected void onStartLoading() {

super.onStartLoading();

forceLoad(); // Calls loadInBackground()}

@Override

public String loadInBackground() {

// Code runs on a background thread.return data;

}

@Override

public void deliverResult(String data) {

super.deliverResult(data); // "Pushing results out"}

}

Page 125: Advanced #3  threading

CursorLoader example

String searchQuery = "Yossi";

String selection = ContactsContract.Contacts.DISPLAY_NAME + " = ?";

String[] selectionArgs = { searchQuery };

return new CursorLoader(

this, // Context

ContactsContract.Contacts.CONTENT_URI, // Table

projections, // Columns to return

selection, // Selection

selectionArgs, // Selection args

null); // Sort order

}

Page 126: Advanced #3  threading

LoaderCallbacks

LoaderCallBacks

Notify on Loader events

Events:

● New Loader is needed.

● Data is ready.

● Loader is about to reset.

Page 127: Advanced #3  threading

LoaderCallbacks<String>

@Override

public Loader<String> onCreateLoader(int id, Bundle args) {

return new SimpleLoader(this);

}

@Override

public void onLoadFinished(Loader<String> loader, String data) {

mTextView.setText(data);

}

@Override

public void onLoaderReset(Loader<String> loader) {

//clear data, reset UI etc...

}

Page 128: Advanced #3  threading

LoaderManager

● Monitor Activity lifecycle.

● Manage Loaders:

➢ Start / Stop / Reset / Retrain

(Activity lifecycle / direct request)

● Invoke LoaderCallBacks.

LoaderManager

Monitor Activity

Manage Loaders

Notify LoaderCallBacks

Page 129: Advanced #3  threading

Init Loader

getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);

Page 130: Advanced #3  threading

Init Loader

getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);

Getting a reference to the Activity LoaderManager

Page 131: Advanced #3  threading

Init Loader

getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);

Instruct LoaderManager to initialize the Loader

Page 132: Advanced #3  threading

Init Loader

getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);

Unique Loader ID to init

Page 133: Advanced #3  threading

Init Loader

getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);

Optional BundleCan be used for Loader creation

Page 134: Advanced #3  threading

Init Loader

getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);

Interface to report on Loader state changes

Page 135: Advanced #3  threading

initLoader()

LoaderManager

initLoader(LOADER_ID, args, LoaderCallbacks);

No.

onCreateLoader(int id, Bundle args)

Use the existing Loader

Yes.

Page 136: Advanced #3  threading

ThreadPoolExecutor

Call initLoader()only when ready to receive data.

Get fresh data using restartLoader().

Page 137: Advanced #3  threading

Loaders

Coordination with lifecycles.

Built-in results caching mechanism.

Query content providers with ease.

Implementation can be time consuming.

Page 138: Advanced #3  threading

ThreadPoolExecutor

Easy work distribution between multiple threads.

Page 139: Advanced #3  threading

ThreadPoolExecutor - Example use case

“I need to allow my users to

upload multiple pictures from

their gallery simultaneously.”

Page 140: Advanced #3  threading

ThreadPoolExecutor

Managing a pool of thread.

Creating/terminating threads as necessary.

Utilize the Executor interface.

Page 141: Advanced #3  threading

Executor.java

public interface Executor {

void execute(Runnable command);

}

Page 142: Advanced #3  threading

Pre-defined Executors

// Single thread is executing the tasks in order.

Executors.newSingleThreadExecutor();

// Keeping the number of threads constant.

Executors.newFixedThreadPool(n);

// Dynamic size pool - grows/shrinks as needed.

// with 60 sec idle time per thread.

Executors.newCachedThreadPool();

Page 143: Advanced #3  threading

Pre-defined Executors

// Single thread is executing the tasks in order.

Executors.newSingleThreadExecutor(threadFactory);

// Keeping the number of threads constant.

Executors.newFixedThreadPool(n, threadFactory);

// Dynamic size pool - grows/shrinks as needed.

// with 60 sec idle time per thread.

Executors.newCachedThreadPool(threadFactory);

Page 144: Advanced #3  threading

ThreadFactory.java

public interface ThreadFactory {

Thread newThread(Runnable r);

}

Page 145: Advanced #3  threading

MyThreadFactory

public class MyThreadFactory implements ThreadFactory {

@Override

public Thread newThread(@NonNull Runnable r) {

Thread thread = new Thread(r);

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

return thread;

}

}

Page 146: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler

);

Page 147: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler);

Minimum number of threads to keep in the pool

Page 148: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler);

Maximum number of threads to keep in the pool

Page 149: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler);

Time before idle thread is terminated

Page 150: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,

RejectedExecutionHandler handler);

The queue type

Page 151: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,RejectedExecutionHandler handler

);

ThreadFactory for threads creation

Page 152: Advanced #3  threading

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler);

How to handle rejected task

Page 153: Advanced #3  threading

ThreadPoolExecutor

How to find the right numbers for your needs?

Implement → Measure → Repeat

Page 154: Advanced #3  threading

AsyncTask default parallel executor values since Kitkat (API 19)

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

// We want at least 2 threads and at most 4 threads in the core pool,

// preferring to have 1 less than the CPU count to avoid saturating

// the CPU with background work

private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

private static final int KEEP_ALIVE_SECONDS = 30;

Page 155: Advanced #3  threading

Executing a task

mExecutor.execute(new MyRunnableTask());

// When task rejection is possible

try {

mExecutor.execute(new MyRunnableTask());

} catch (RejectedExecutionException e) {

// Handle rejection gracefully

}

Page 156: Advanced #3  threading

Stopping ThreadPoolExecutor

mExecutor.shutdown();

mExecutor.shutdownNow();

Page 157: Advanced #3  threading

ThreadPoolExecutor

Implement → Measure → Repeat.

Don’t use the default thread priority.

Remember to handle tasks rejections.

Page 158: Advanced #3  threading

ThreadPoolExecutor

Easy to deal with threads work distribution.

Useful out-of-the-box implementations available.

Implementing efficiently requires time.

Page 159: Advanced #3  threading
Page 160: Advanced #3  threading

One last thing...

Page 161: Advanced #3  threading

Remember - Don’t leak!

Page 162: Advanced #3  threading

Remember - Don’t leak!

Avoid non static inner classes when possible.

Page 163: Advanced #3  threading

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Page 164: Advanced #3  threading

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Always implement cancellation policy.

Page 165: Advanced #3  threading

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Always implement cancellation policy.

Always release references.

Page 166: Advanced #3  threading

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Always implement cancellation policy.

Always release references.

Be mindful to components lifecycles.

Page 167: Advanced #3  threading

Thank you.