Eden Bugdary Machluf
Advanced sessions
Threads
31/07/18
First,
> 3500 members
Largest Android Active
Community
Android Academy Staff
Yonatan Levin
Google Developer
Expert &
CTO @ KolGene
Yossi Segev
Mobile engineer
Colu
Refael Ozeri
Founder & CEO
Hero Apps
Eden Bugdary
Mobile developer @
AppNext
What Do We Do?
● Android Fundamentals
● Android UI / UX
● Community Hackathon
● Android Advanced
● Mentors Program
● Active community
Moscow
Mentors program
NOW!
https://bit.ly/2Nn9dAf
Logistics
facebook.com/groups/android.academy.ils/
https://bit.ly/2MWtigC
Android Advanced Course
22.8 - UI, GPU, Animations
16.9 - Clean Architecture
3.7 - CPU & Memory
31.7 - Threads & Network
Timor Surkis
Android Dev @ Gini Apps
Superhuman tech guy
Artyom Okun
Mobile Developer @ Colu
Awesome crunchy guy
Eden Bugdary Machluf
Advanced sessions
Threads
31/07/18
Eden Bugdary Machluf
Android Academy
Mobile developer @Appnext
What is a Thread?
CPU & processes
CPU Process
1 ...
Thread
1 ...
Thread
A thread of execution is the smallest sequence of
programmed instructions that can be managed
independently.
When launching an app...
Application
Process
Main
Thread
The main thread
UI rendering
Application
components
User interaction
Lifecycle callbacks
Main thread
Queue
The main thread is very busy
25
– When blocked - system shows ANR dialog
android.os.NetworkOnMainThreadException
– Exception on network operations
– Exclusive interaction with the UI
Leaving the Main thread
java.lang.Thread
public class MyThread extends Thread {
@Override
public void run() {
doStuff();
}
}
MyThread myThread = new MyThread();
myThread.start();
Getting 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");
}
});
Back to the Main Thread
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText("Some data update");
}
});
Back to the Main Thread
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText("Some data update");
}
});
From a View
mTextView.post(new Runnable() {
@Override
public void run() {
mTextView.setText("Some data update");
}
});
From a View
mTextView.post(new Runnable() {
@Override
public void run() {
mTextView.setText("Some data update");
}
});
From a View
mTextView.post(new Runnable() {
@Override
public void run() {
mTextView.setText("Some data update");
}
});
How does it work??
Threads in JAVA
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.
We have a problem
Create new thread consumes
system resources and memory.
How much memory?
Let’s make
threads
reusable
How it works?
We will need 3 players:
– Looper
– Message
– Handler
“
android.os.Looper
– Keeping its thread alive.
– Only one Looper per thread.
– Thread don’t get a Looper by default.*
– MessageQueue created when we create Looper.
android.os.Message
Two types of Messages:
– Data Message
– Task Message
android.os.Message
Message holding data.
Field Name Description
what int - Unique identifier
when long - timestamp
arg1, arg2 int - “Low cost” parameters
obj Object
data Bundle
android.os.Message
Message holding a task (Runnable).
Field Name Description
callback Runnable task
when long - timestamp
So it’s simple...
Task
Thread
Looper
MessageQueue
android.os.Handler
– Creating messages.
– Inserting messages to the queue.
– Removing messages in the queue.
– Consuming messages.
Thread
Looper
MessageQueue
Thread
Looper
MessageQueue
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Thread
Looper
MessageQueue
Handler
Handler
Questions?
60
Enough theory
Let’s see some code
View.java
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
View.java
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
View.java
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
View.java
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
View.java
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}
Road Map
Overview
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
ꢈ A Looper is created and attached to a thread.
ꢈ Handler is created.
ꢈ The Handler create a Message.
ꢈ The Handler insert the Message into the Looper queue.
ꢈ The Handler is consuming/executing the message.
Fancy thread
public class FancyThread extends Thread {
@Override
public void run() {
Looper.prepare();
// Code is missing for simplicity
Looper.loop();
}
}
Overview
✓ A Looper is created and attached to a thread.
ꢈ Handler is created.
ꢈ The Handler create a Message.
ꢈ The Handler insert the Message into the Looper queue.
ꢈ The Handler is consuming/executing the message.
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
}
});
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
}
});
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...
Fancy thread
public class FancyThread extends Thread {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper(), callback);
Looper.loop();
}
}
Fancy thread
public class FancyThread extends Thread {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper(), callback);
Looper.loop();
}
}
Fancy thread
public class FancyThread extends Thread {
//...
public Handler getHandler() {
return mHandler;
}
}
Overview
✓ A Looper is created and attached to a thread.
✓ Handler is created.
ꢈ The Handler create a Message.
ꢈ The Handler insert the Message into the Looper queue.
ꢈ The Handler is consuming/executing the message.
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);
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);
Creating a Message with Handler
Message msg = handler.obtainMessage(/*...*/);
this.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
Message message = new Message(); !! Avoid !!
Overview
✓ A Looper is created and attached to a thread.
✓ Handler is created.
✓ The Handler create a Message.
ꢈ The Handler insert the Message into the Looper queue.
ꢈ The Handler is consuming/executing the message.
“
Insert a Message
to the queue
“
Inserting message to the queue
// Data messages
boolean handler.sendMessage(msg);
boolean handler.sendMessageAtFrontOfQueue(msg);
boolean handler.sendMessageDelayed(msg, 2000);
// and more...
“
Inserting message to the queue
// Task messages
boolean handler.post(Runnable r)
boolean handler.postAtFrontOfQueue(Runnable r)
boolean handler.postDelayed(Runnable r, long delayMillis)
// and more...
“
Creating and Inserting message 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);
“
Creating and Inserting message 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);
“
Creating and Inserting message 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);
“
Creating and Inserting message to the queue
// Reference to our Handler
Handler fancyHandler = mFancyThread.getHandler();
// Create data message with what = 1
Message msg = fancyHandler.obtainMessage(1);
// Adding task message to the queue
fancyHandler.post(new MyRunnable());
Removing a Message
from 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...
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...
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...
Overview
✓ A Looper is created and attached to a thread.
✓ Handler is created.
✓ The Handler create a Message.
✓ The Handler insert the Message into the Looper queue.
ꢈ The Handler is consuming/executing the message.
Consuming Data Message
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 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();
}
});
}
FancyActivity.java
@Override
protected void onStop() {
super.onStop();
mFancyThread.getHandler().sendEmptyMessage(-1);
}
FancyActivity.java
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case -1:
Looper.myLooper().quit();
return true;
}
}
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.
Overview
✓ A Looper is created and attached to a thread.
✓ Handler is created.
✓ The Handler create a Message.
✓ The Handler insert the Message into the Looper queue.
✓ The Handler is consuming/executing the message.
113
Phew… Questions?
114
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.");
}
});
117
Messages passing is a powerful
async mechanism
but it can be hard to implement
from scratch.
Luckily, You don’t have
too...
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
HandlerThread - Stopping
handlerThread.quit();
// or
handlerThread.quitSafely(); // API >= 18
HandlerThread- don’t forget
- Remember to quit().
- Call start() before calling getLooper().
HandlerThread
- Flexible, reusable tasks chaining.
- Guarantees thread safety.
- Implementation can take a while.
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();
// 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();
// 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();
How it works?
Task
ThreadPoolExecutor
Threads Pool
Queue
How it works?
Task
ThreadPoolExecutor
Threads Pool
Queue
How it works?
ThreadPoolExecutor
Threads Pool
Queue
How it works?
Task
ThreadPoolExecutor
Threads Pool
Queue
How it works?
Task
ThreadPoolExecutor
Threads Pool
Queue
How it works?
ThreadPoolExecutor
Threads Pool
Queue
Stopping ThreadPoolExecutor
mExecutor.shutdown();
mExecutor.shutdownNow();
ThreadPoolExecutor
– Easy to deal with threads work distribution.
– Useful out-of-the-box implementations available.
– Implementing efficiently requires time.
Summary
– All the threads are made from 3 main components:
Looper, Handler and Messages.
– We can create our own thread just like android system
does.
– Android can make our lives easier if we choose to use
the (kind of) out of box ready components.
Eden Bugdary Machluf
Thank you!

Advanced #2 threading