Reprint please indicate source: a maple leaf column

In the last article, we explained the basic usage and implementation principle of AsyncTast. We know that AsyncTask is internally realized through thread pool and Handler, and the operation of asynchronous task is realized through encapsulation of thread pool and Handler. More about AsyncTask related content, can refer to my Android source code (iii) – > asynchronous task AsyncTask

In this article we will explain the concepts associated with HandlerThread. What is a HandlerThread? The best way to understand a class is to look at its definition, so let’s take a look at how HandlerThread is defined. Class definition:

Handy class for starting a new thread that has a looper. 
The looper can then be used to create handler classes. 
Note that start() must still be called.Copy the code

The purpose of this class is to create a thread containing looper. So when do we need it? To accomplish multiple tasks at the same time, we create multiple threads in the application. In order to facilitate communication between multiple threads, we will use Handler to implement communication between threads. At this point our manual implementation of a simplified version of multithreading +Handler is what HandlerThrea is all about.

Let’s first look at the basic usage of HandlerThread:

MHandlerThread = new HandlerThread("myHandlerThreand"); mHandlerThread.start(); final Handler mHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { Log. I ("tag", "received message:" + msg.obj.tostring ()); }}; title = (TextView) findViewById(R.id.title); title.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Message msg = new Message(); msg.obj = "11111"; mHandler.sendMessage(msg); msg = new Message(); msg.obj = "2222"; mHandler.sendMessage(msg); }});Copy the code

We first define a HandlerThread object, which is generated directly from new. Look at its constructor:

public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }Copy the code

HandlerThread inherits from Thread, so HandlerThread is essentially a Thread that is constructed by doing some initialization.

Mhandlerthread.start (); mhandlerThread.start (); mhandlerThread.start ();

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }Copy the code

The Looper prepate() and loop.loop () methods are called internally. If you are familiar with Android asynchronous messaging, you should know that in Android a thread corresponds to a Looper object and a MessageQueue object. Handler object and N, for details please refer to: Android source code parsing (two) – > asynchronous message mechanism

So using the run method, we know that we created a Looper and MessageQueue for the HandlerThread we created;

Note that it calls an empty implementation method: onLooperPrepared() before calling looper.loop (). We can implement our own onLooperPrepared() method and do some Looper initialization.

There is a notifyAll() in the run method when mLooper is created, and a wait() in getLooper(). Since the mLooper is executed in a thread and our handler is initialized in the UI thread, that is, we must wait until the mLooper is created before returning getLooper() properly; Wait () and notify() are designed to solve the synchronization problem between these two threads

Then we call:

final Handler mHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { Log. I ("tag", "received message:" + msg.obj.tostring ()); }};Copy the code

The Handler constructor passes in the Looper object of the HandlerThread, so the Handler object contains a reference to the Looper object of the HandlerThread.

We then call handler’s sendMessage method to send the message, which will be received in handler’s handleMessge method.

The last thing to note is that we need to stop the looper thread manually when we don’t need it;

protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();
    }
Copy the code

HandlerThread is relatively simple. It is essentially a Thread Thread, but it contains Looper and MessageQueue. Finally, we summarize here.

Conclusion:

  • HandlerThread is essentially a Thread object, but inside it we create the Thread’s Looper and MessageQueue.

  • With HandlerThread we can communicate not only between UI threads and child threads but also between child threads;

  • Handlerthreads need to be recycled manually when they are not needed.

Android project construction process android source code parsing (ii) — > asynchronous message mechanism Android source code parsing (iii) — > asynchronous task AsyncTask

This article is synchronized to Github:Github.com/yipianfengy…Welcome star and Follow


Reprint please indicate source: a maple leaf column

In the last article, we explained the component process of Android project and the generation process of APK file. In fact, as long as you remember the construction diagram, you basically have a general understanding of the APK construction process. For more knowledge about the APK construction process, please refer to my: Android source code parsing (a) – > Android project construction process

Some time ago in Zhihu read a very good blog: there is no need to read The ANDROID source code pain set thought, in order to better in-depth ANDROID system, decided to learn the ANDROID framework layer source code, from the simplest ANDROID asynchronous messaging mechanism to start it. Hence this article: Asynchronous messaging in Android. This article mainly analyzes the Asynchronous message mechanism of Android from the source point of view.

(1) The general use of Handler

/** * Test Activity, Public class MainActivity extends AppCompatActivity {public static final String TAG = MainActivity.class.getSimpleName(); private TextView texttitle = null; /** * define Handler in main thread, MHandler = new Handler() {@override public void handleMessage(Message) MSG) {if (MSG. What == 101) {log. I (TAG, "received handler message... ); }}}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); texttitle = (TextView) findViewById(R.id.texttitle); texttitle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread() { @Override public void run() { mHandler.sendEmptyMessage(101); } }.start(); }}); }}Copy the code

You can see, the use of general handler method are defined in the main thread handler, then the child thread invokes the mHandler. SendEmptyMessage (); Method, but there is a question here, can we define Handler in child threads?

How to define Handler in child thread?

We define the Handler in the child thread and see the result:

/ * * * define texttitle click event handling * / texttitle setOnClickListener (new View. An OnClickListener () {@ Override public void onClick (View V) {/** * new Thread() {@override public void run() {handler mHandler = new handler () { @override public void handleMessage(Message MSG) {if (MSG. What == 101) {Log. ); }}}; } }.start(); }});Copy the code

Click the button and run this code:

Handler object can be defined or initialized only in the main thread. The looper.prepare () method needs to be called before the Handler object is initialized, so let’s add this code and execute it again:

/ * * * define texttitle click event handling * / texttitle setOnClickListener (new View. An OnClickListener () {@ Override public void onClick (View  v) { new Thread() { @Override public void run() { Looper.prepare(); Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 101) { Log.i(TAG, "Define Handler in child thread and receive message..." ); }}}; } }.start(); }});Copy the code

Looper.prepare() is used to initialize the Handler object. The Handler is initialized with looper.prepare ().

This is not the case. The main method of ActivityThread is executed during App initialization:

public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain") SamplingProfilerIntegration.start() // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false) Environment.initForCurrentUser() // Set the reporter for event logging in libcore  EventLogger.setReporter(new EventLoggingReporter()) AndroidKeyStoreProvider.install() // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()) TrustedCertificateStore.setDefaultUserDirectory(configDir) Process.setArgV0("") Looper.prepareMainLooper() ActivityThread thread = new ActivityThread() thread.attach(false) if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler() } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")) } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER) Looper.loop() throw new RuntimeException("Main thread loop unexpectedly exited") }Copy the code

You can see that the original looper.prepare () method is called here, so we can initialize the Handler directly elsewhere.

We can see that the looper.loop () method is also called, and we can see from reading other articles that the standard way to write a Handler is this:

Looper.prepare(); Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 101) { Log.i(TAG, "Define Handler in child thread and receive message..." ); }}}; Looper.loop();Copy the code

1) Check the looper.prepare () method

static final ThreadLocal sThreadLocal = new ThreadLocal(); /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() ! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }Copy the code

If you’re familiar with JDK, when maintaining variables with ThreadLocal, ThreadLocal provides a separate copy of the variable for each thread that uses it, so each thread can change its own copy independently. It does not affect the corresponding copy of other threads. Looper.prepare() can only be called once per thread, so let’s try calling it twice.

/** * looper.prepare () is called twice */ looper.prepare (); Looper.prepare(); Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 101) { Log.i(TAG, "Define Handler in child thread and receive message..." ); }}}; Looper.loop();Copy the code

Run the program again, click the button, and execute the code:



You can see that the program fails and the Excetion message in Prepare is displayed.

Moving on to the constructor of the Looper object, we can see that a MessageQueue object is initialized in its constructor:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }Copy the code

The looper.prepare () method starts with a Looper object associated with a MessageQueue object, and there is only one Looper object and only one MessageQueue object in a thread.

2) View the constructor of the Handler object

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }Copy the code

In the Handler constructor, we initialize variables and determine that the Handler object should not be initialized in an inner class, a static class, or an anonymous class, and save the Looper object from the current thread. The looper.prepare () method starts with a Looper object associated with a MessageQueue object, and there is only one Looper object and only one MessageQueue object in a thread. The Handler constructor maintains the current thread’s Looper object inside the Handler

SendMessage (MSG) normally, when sending an asynchronous message, we call it like this:

mHandler.sendMessage(new Message());Copy the code

By constantly following the source code, it will eventually call:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }Copy the code

MSG. Target is the Handler object itself; The Queue object is the MessageQueue object associated with the Looper object maintained internally by our Handler. Look at the enqueueMessage method of the Messagequeue object:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                
                
                
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; 
                prev.next = msg;
            }

            
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }Copy the code

MessageQueue does not use a list to store all messages, but uses message.next to store the next Message, thus ordering all messages by time.

4) Check the looper.loop () method

/** * 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; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); if (msg == null) { return; } Printer logging = me.mLogging; if (logging ! = null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging ! = null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } final long newIdent = Binder.clearCallingIdentity(); if (ident ! = newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); }}Copy the code

You can see that there is a lot of content in the method. The looper.loop () method starts an endless loop to determine whether the message in MessageQueue is empty or not. If so, return the message and execute queue.next() :

Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis();  Message prevMsg = null; Message msg = mMessages; if (msg ! = null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg =  msg; msg = msg.next; } while (msg ! = null && ! msg.isAsynchronous()); } if (msg ! = null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg ! = null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (! keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; }}Copy the code

As you can see, the implementation logic is basically Message off the stack, with some restrictions on threads, concurrency control, etc. After retrieving the Message object at the top of the stack:

msg.target.dispatchMessage(msg)Copy the code

So what is msg.target? We can trace it to the Handler object we defined. Then we can look at the dispatchMessage method of the Handler class:

/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback ! = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}Copy the code

As you can see, if we set callback (Runnable object), the handleCallback method will be called directly:

private static void handleCallback(Message message) {
        message.callback.run();
    }Copy the code

That is, if we set the callback (Runnable) object when we initialize the Handler, we call the run method directly. For example, we often write about the runOnUiThread method:

runOnUiThread(new Runnable() {
            @Override
            public void run() {

            }
        });Copy the code

Look at its internal implementation:

public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }Copy the code

If msg.callback is empty, we call our McAllback.handlemessage (MSG), the handler’s handlerMessage method. Since the Handler object is created in the main thread, execution of the Handler’s handlerMessage method is also done in the main thread.

Conclusion:

1) Define Handler in main thread, execute directly:

Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); }};Copy the code

If you want to define a Handler in a child thread, the standard way to write this is:

Looper.prepare(); Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); }}; Looper.loop();Copy the code

2) There is only one Looper object and only one MessageQueue object in a thread, and there can be N Handler objects. The Handler object is associated with a unique Looper object in the thread, and the Looper object is associated with a unique MessageQueue object.

3) MessageQueue Message queue does not store the list of messages through the list, but implements the function of the list by associating the next Message with the next attribute of the Message object, and all messages are sorted by time.

4) The interaction between two sub-threads in Android can also be realized through the asynchronous messaging mechanism of Handler. You can define the Handler object in thread A and get the reference of Handler in thread B and call the sendMessage method.

5) There is a handler member variable inside the activity by default, and some other asynchronous messaging mechanisms are implemented in Android:

mHandler.post(new Runnable() {
                    @Override
                    public void run() {

                    }
                });Copy the code

See its internal implementation:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }Copy the code

You can see that the internal calls are the sendMessage family of methods…

View post method:

public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo ! = null) { return attachInfo.mHandler.post(action); } ViewRootImpl.getRunQueue().post(action); return true; }Copy the code

You can see that it calls the POST method of the handler object saved by default in the activity.

The activity’s runOnUiThread method:

public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }Copy the code

Determine if the current thread is a UI thread, if not, call handler’s POST method, otherwise run method is executed directly.

Reference article: Android asynchronous message processing mechanism fully analyzed, with you from the source point of view of a thorough understanding of Android asynchronous message processing mechanism and source code analysis

In addition to android source code parsing method interested in can refer to my: Android source parsing (a) – > Android project construction process

This article is synchronized to Github:Github.com/yipianfengy…Welcome star and Follow