Upload
vitali-pekelis
View
39
Download
0
Embed Size (px)
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.