This is the 18th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
preface
We’ve all used Handler, we’re familiar with how to use Handler, and we’re sure we know what to watch out for when using Handler, which is memory leaks, and we know that most memory leaks are caused by static variable references. Handler is an inner class. Either a non-static inner class or an anonymous inner class will hold references to an external class. If the Activty exits and the Handler holds its reference, the Activity is not destroyed but is still in Memory, resulting in a Memory Leak. How to solve this problem, there are very mature online articles and technical implementation, here is no longer cumbersome, this period mainly discusses another way to use HandlerThread.
First, the nature of HandlerThread
A HandlerThread is essentially a normal Thread.
Looper.prepare() is called to start a message loop for each thread. By default, new Android threads do not start a message loop. (Except for the main thread, for which the system automatically creates a Looper object to start the message loop.) The Looper object stores messages and events via MessageQueue. A thread can only have one Looper, corresponding to one MessageQueue. Looper is then set to work via looper.loop (), fetching messages from the message queue and processing them.
To use Handler to complete communication between threads, looper.prepare () is called to start a message loop for the thread, Handle is created, and looper.loop () is called to start work. It’s all very routine.
And HandlerThread does that for us, and it builds Looper internally.
HandlerThread
public class OtherActivity extends AppCompatActivity { private static final String TAG = "OtherActivity"; private Handler handler1; private Handler handler2; private HandlerThread handlerThread1; private HandlerThread handlerThread2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_other); HandlerThread1 = new HandlerThread("handle-thread-1"); handlerThread2 = new HandlerThread("handle-thread-2"); // Enable HandleThread handlerThread1.start(); handlerThread2.start(); handler1 = new Handler(handlerThread1.getLooper()) { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); Log.i(TAG, "handleMessage: ThreadName = " + Thread.currentThread().getName() + " msg.what = " + msg.what); }}; handler2 = new Handler(handlerThread2.getLooper()) { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); Log.i(TAG, "handleMessage: ThreadName = " + Thread.currentThread().getName() + " msg.what = " + msg.what); }}; handler2.sendEmptyMessage(2); handler1.sendEmptyMessage(5); } // Override protected void onDestroy() {super.ondestroy (); handlerThread1.quit(); handlerThread2.quitSafely(); }}Copy the code
The HandlerThread that creates the Looper and executes loop() needs to manually call quit at the end of the task. Otherwise, the thread will remain runnable due to loop() polling and CPU resources will not be released. It is more likely to cause a memory leak because Thread is holding out-of-life instances as GC Root.
The official use of the quitSafely() to terminate Looper is that it will only discard messages if the execution time is later than the current call time. This ensures that the messages that meet the execution time condition remain in the queue when the quitSafely is called and exit the polling only after they are all executed.
Does the main thread need quit? No, App is recycled directly by AMS when memory is low. Because the main thread is so important and manages the life cycle of components such as ContentProviders, activities, and Services, even if one component dies, it still needs to exist to schedule other components! In other words, the ActivityThread is scoped beyond these components and should not be handled by them. For example, if an Activity destroys, the ActivityThread still handles transactions for other activities or services and other components, and cannot terminate.
HandlerThread’s usefulness in Android
Android itself is a huge message processor. The ActivityThread class is the initial class for the Android APP process, and its main function is the entry point for the APP process. The code snippets that execute UI events in the APP process are provided by ActivityThread. That is, the main thread instance exists, but the code that created it is not visible. This is where the main function of ActivityThread is executed.
public final class ActivityThread { //... private static ActivityThread sCurrentActivityThread; public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } private void attach(boolean system) { sCurrentActivityThread = this; / /... } public static void main(String[] args) { //.... PrepareMainLooper (); // Create Looper and MessageQueue objects to handle the main thread's message looper.prepareMainLooper (); ActivityThread thread = new ActivityThread(); Attach (false); // Create a new thread. Looper.loop(); // Message loop run throw new RuntimeException("Main thread loop unexpectedly exited"); }}Copy the code
The Activity’s life cycle depends on the main thread looper. loop, which takes action when different messages are received.
conclusion
Anyway, use it, encapsulated good things why not, can reduce the bug in our development process, prompt development efficiency, how good a thing ah, use it, ha ha