Advanced #3 threading

Preview:

Citation preview

Advanced sessions

Threading

Yossi Segev12/7/2017

3

Hi !

The Largest Android Community

Android Academy - TLV

TLV - Android Academy

Join Us:FundamentalsAdvanced

With designersHackathons

Mentors

Yossi Segev

Android @ Colu

Android Academy

Advanced sessions

Threading

Yossi Segev12/7/2017

3

Application Process

Main Thread

When launching an app...

The Main Thread

UI rendering

Application components

User interaction

Lifecycle callbacks

Main threadQueue

The Main thread is very busy

● When blocked - system shows ANR dialog

● Exception on network operations

● Exclusive interaction with the UI

android.os.NetworkOnMainThreadException

Leaving the Main thread

java.lang.Thread

java.lang.Thread

public class MyThread extends Thread {

@Override

public void run() {

doStuff();

}

}

MyThread myThread = new MyThread();

myThread.start();

java.lang.Thread

public class MyRunnable implements Runnable {

@Override

public void run() {

doStuff();

}

}

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

myThread.start();

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.

How to stop a thread?

Stopping a thread

● Return from the run() method.

Stopping a thread

● Return from the run() method.

● Use cancellation flag.

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

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

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

Stopping a thread

● Return from the run() method.

● Use cancellation flag.

● Call the Thread.interrupt() method.

Thread.interrupt()

Not a magic method!

The implementation is up to us!

Handling interrupt()

public class MyThread extends Thread {

@Overridepublic void run() {

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

}}

}

Handling interrupt()

@Override

public void run() {

while (!isInterrupted()) {

// Do some work...

try {

sleep(1000);

} catch (InterruptedException e) {

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

break;

}

}

}

Stopping a thread

● Return from the run() method.

● Use cancellation flag.

● Call the Thread.interrupt() method.

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

Back to the Main thread

Back to the Main thread

We can:

● Activity.runOnUiThread(runnable)

● View.post(runnable)

Back to the Main thread

runOnUiThread(new Runnable() {

@Override

public void run() {

mTextView.setText("Some data update");

}

});

Fixing the crash - CounterActivity

mTextView.post(new Runnable() {

@Override

public void run() {

mTextView.setText("Some data update");

}

});

How does it work??

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

}

}

View.java

public boolean post(Runnable action) {

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

return attachInfo.mHandler.post(action);

}

getRunQueue().post(action);

return true;

}

AndroidThreads

Communication mechanism

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.

Introducing...

The Handler

The Looperand the

MessageQue

ue

android.os.Message

Two types of Messages:

Data Message

Task Message

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

Message holding a task (Runnable).

android.os.Message

Field Name Description

callback Runnable task

android.os.Handler

● Creating messages.

● Inserting messages to the queue.

● Removing messages in the queue.

● Consuming messages.

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

Overview

Thread

Looper

MessageQueue

Overview

Thread

Looper

MessageQueue

Handler

Overview

Thread

Looper

MessageQueue

HandlerHandler

Overview

Thread

Looper

HandlerHandler

MessageQueue

Overview

Thread

Looper

HandlerHandler

MessageQueue

Overview

Thread

Looper

HandlerHandler

MessageQueue

Overview

Thread

Looper

HandlerHandler

MessageQueue

Overview

Thread

Looper

HandlerHandler

MessageQueue

Overview

Thread

Looper

HandlerHandler

MessageQueue

Overview

Thread

Looper

HandlerHandler

MessageQueue

Enough theoryLet’s see some code

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

FancyThread.java

public class FancyThread extends Thread {

@Override

public void run() {

Looper.prepare();

// Code is missing for simplicity

Looper.loop();

}

}

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

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

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

FancyThread.java

@Override

public void run() {

Looper.prepare();

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

Looper.loop();

}

FancyThread.java

public class FancyThread extends Thread {

// …

public Handler getHandler() {

return mHandler;

}

}

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

Creating a Message

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

Creating a Message with Handler

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

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

Creating a Message

!! Avoid !!

Message message = new Message();

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

Insert a Messageto the queue

Inserting data message to the queue

boolean handler.sendMessage(msg);

boolean handler.sendMessageAtFrontOfQueue(msg);

boolean handler.sendMessageDelayed(msg, 2000);

// And more...

Inserting task message

boolean handler.post(Runnable r)

boolean handler.postAtFrontOfQueue(Runnable r)

boolean handler.postDelayed(Runnable r, long delayMillis)

// And more...

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

Removing a Messagefrom the queue

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

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

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;

}

}

};

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?

Consuming task message

// No extra work is needed.

fancyHandler.post(new Runnable() {

@Override

public void run() {

// Will run on the Handler thread.

}

});

Keep it clean!

Don’t forget to call quit()

on your Looper when it is no

longer needed!

FancyActivity.java

@Override

protected void onStop() {

super.onStop();

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

@Override

public void run() {

Looper.myLooper().quit();

}

});

}

Option #1

FancyActivity.java

@Override

protected void onStop() {

super.onStop();

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

}

Option #2

FancyThread.java

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

case -1:

Looper.myLooper().quit();

return true;

}

}

Option #2

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.

Phew… Questions?

P.S:

The Main thread is

just a “regular”

thread with a Looper.

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

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

}

});

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

Luckily, You don’t have too...

Android Threading primitives

(quick) overview

HandlerThread

Flexible tasks chaining in a queue.

HandlerThread - Example use case

“Our app onboarding flow has 6

different network calls.”

HandlerThread

A Thread with a “free” Looper implementation.

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

Creating a custom HandlerThread

HandlerThread

Override

onLooperPrepared()

for additional setup .

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

HandlerThread - Stopping

handlerThread.quit();

// or

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

HandlerThread

Remember to quit().

Call start() before calling getLooper()

HandlerThread

Flexible, reusable tasks chaining.

Guarantees thread safety.

Implementation can take a while.

IntentService

“Fire and forget” long-running operations.

IntentService - Example use case

“I want to preload data from my

server to my local DB even

when my app is in the

foreground.”

IntentService

● Subclass of Service.

● Executes operation on a background thread.

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

● Automatically stops when out of work.

IntentService work queue

startService(Intent i1);

startService(Intent i2);

startService(Intent i3);

onCreate()

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

onDestory()

Creating IntentService

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

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.

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

}

}

IntentService as a work queue

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

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

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

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.

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.

AsyncTask

Quick “in-and-out” operations.

AsyncTask - Example use case

“I need to fetch a bitmap from a

URL and populate an

ImageView. ”

AsyncTask

Easily get off the Main thread to perform a task.

Return results back to the Main thread to update UI.

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

}

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

}

}

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

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?

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 .

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.

Loaders

Handle configurations changes gracefully.

Loaders - Example use case

“I’m querying data from a

Content provider.”

Loaders

● Can Load data asynchronously.

● Coordinating with Activity lifecycle.

● Surviving configuration changes.

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.

Loader

Loader

Doing background work ● Loading data asynchronously.

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

}

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

}

LoaderCallbacks

LoaderCallBacks

Notify on Loader events

Events:

● New Loader is needed.

● Data is ready.

● Loader is about to reset.

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

}

LoaderManager

● Monitor Activity lifecycle.

● Manage Loaders:

➢ Start / Stop / Reset / Retrain

(Activity lifecycle / direct request)

● Invoke LoaderCallBacks.

LoaderManager

Monitor Activity

Manage Loaders

Notify LoaderCallBacks

Init Loader

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

Init Loader

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

Getting a reference to the Activity LoaderManager

Init Loader

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

Instruct LoaderManager to initialize the Loader

Init Loader

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

Unique Loader ID to init

Init Loader

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

Optional BundleCan be used for Loader creation

Init Loader

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

Interface to report on Loader state changes

initLoader()

LoaderManager

initLoader(LOADER_ID, args, LoaderCallbacks);

No.

onCreateLoader(int id, Bundle args)

Use the existing Loader

Yes.

ThreadPoolExecutor

Call initLoader()only when ready to receive data.

Get fresh data using restartLoader().

Loaders

Coordination with lifecycles.

Built-in results caching mechanism.

Query content providers with ease.

Implementation can be time consuming.

ThreadPoolExecutor

Easy work distribution between multiple threads.

ThreadPoolExecutor - Example use case

“I need to allow my users to

upload multiple pictures from

their gallery simultaneously.”

ThreadPoolExecutor

Managing a pool of thread.

Creating/terminating threads as necessary.

Utilize the Executor interface.

Executor.java

public interface Executor {

void execute(Runnable command);

}

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

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

ThreadFactory.java

public interface ThreadFactory {

Thread newThread(Runnable r);

}

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;

}

}

Creating custom Executor

Executor executor = new ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler

);

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

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

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

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

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

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

ThreadPoolExecutor

How to find the right numbers for your needs?

Implement → Measure → Repeat

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;

Executing a task

mExecutor.execute(new MyRunnableTask());

// When task rejection is possible

try {

mExecutor.execute(new MyRunnableTask());

} catch (RejectedExecutionException e) {

// Handle rejection gracefully

}

Stopping ThreadPoolExecutor

mExecutor.shutdown();

mExecutor.shutdownNow();

ThreadPoolExecutor

Implement → Measure → Repeat.

Don’t use the default thread priority.

Remember to handle tasks rejections.

ThreadPoolExecutor

Easy to deal with threads work distribution.

Useful out-of-the-box implementations available.

Implementing efficiently requires time.

One last thing...

Remember - Don’t leak!

Remember - Don’t leak!

Avoid non static inner classes when possible.

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Always implement cancellation policy.

Remember - Don’t leak!

Avoid non static inner classes when possible.

Avoid anonymous class declaration when possible.

Always implement cancellation policy.

Always release references.

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.

Thank you.

Recommended