According to Droidcon NYC 2015 - How the Main Thread works
Michael Bailey | YouTube
Main Thread
1.Looper | Message Queue | Handler
A. android.os.Looper
package android.app;
/**
* This message the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*/
public final class ActivityThread {
<omitted>
public static void main(String[] args) {
<omitted>
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
<omitted>
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
Class used to run a message loop for a thread
- A thread has zero or one Loopers
- It has a looper, if you call Looper.prepare();
- It is running an message loop if you call Looper.loop();
Looper.loop()
public final class Looper {
/** Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop. */
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; looper.prepare wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (::) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
}
}
}
B.Message Queue
Internally(non-public API):
- A queue of Message to be processed at a given time
- next() returns the next message that should be processed. Will not return Messages that will be processed at future times.
- Timing based on SystemClock.uptimeMillis() (CPU time)
Message
- when - earliest time it can be processed
- Processing, either
- callback.run() (Runnable)
- OR target.handleMessage(message)
- obtain()/release() from an Object pool
How can Messages be added to the MessageQueue for processing?
C. Handler
- Add message to the MessageQueue
- post()
- postDelayed()/postAtTime()
- postAtFrontOfQueue() <- @piwai says do not use it
- Process messages with no Callback at the front of the MessageQueue
- handleMessage()
- Start new Handler
new Handler();
new Handler(Looper.getMainLooper());
new Handler(Looper.myLooper());
D.DrawHandler –> Choreographer
- Manages Frame Drawing on the Main Thread.
This is responsible for basically getting frames displayed and pushing the UI code putting the messages on there so the UI code gets pushed to the GPU and drawing on the screen
/* From Android API 22 sdk source*/
FrameHandler handler = new FrameHandler(Looper.myLooper());
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
handler.sendMessageAtTime(msg, nextFrameTime);
private final class FrameHandler extends Handler {
public FrameHandler(Looper lopper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
RenderThread
- New in API 21+
- Offloads pushing UI to the GPU to a non-main thread
- If something takes too long on the main thread, bad things can happen:
- Drop frame
- Input events don’t get processed
- ANR
Android thread rules:
From the Android docs:
“Thus, there are simply two rules to Android’s single thread model:”
- Do not block the UI thread
- Do not access the Android UI thread tookit from outside the UI thread.
Other thread
How do you run stuff NOT on the main thread
- java.util.concurrent
- AsyncTask
- RxJava
StrictMode
http://developer.android.com/reference/android/os/StrictMode.html
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.Vmpolicy.Builder())
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
Instrumentation Thread (Test thread)
- From Android API 22 sdk source
public class Instrumentation {
/**
* Create and start a new thread in which to tun instrumentation.
* This new thread will call to {@link #onStart} where
* you can implement the instrumentation.
*/
public void start() {
<omitted>
mRunner = new InstrumentationThread("Instr: " + getClass().getName());
mRunner.start();
}
}
Activity Lifecycle Events
- Activity Lifecycle Events (startActivity(), finish()) go out of your process through Binder IPC to the ActivityManager
- Then back onto your main queue in the form of lifecycle callbacks (onCreate(), onDestroy()..).
SyncBarriers
- No synchronous messages will be processed if a sync barrier is in place. It must be removed first.
- Asynchronous Messages will continue to be processed even when a