Summary of a.

In Android, the main interprocess communication with Binder, Socket, and communication between threads is Android message mechanism, before analyzing the source code, first review how to use.

Two. Traditional use

[-> SecondActivity.java]

package com.dopezhi.handlerdemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class SecondActivity extends AppCompatActivity {

    private final String TAG = "SecondActivity";

    public final int MSG_GET = 1;
    public final int MSG_RESULT = 2;

    public Handler mUiHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            Log.i(TAG, "mUiHandler handleMessage thread : " + Thread.currentThread());
            switch (msg.what) {
                case MSG_RESULT:
                    Toast.makeText(getApplicationContext(), (String) msg.obj, Toast.LENGTH_LONG).show();
                    break;
                default:
                    break; }}};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        LooperThread looperThread = new LooperThread("looper_thread");
        looperThread.start();

        findViewById(R.id.get_second).setOnClickListener(v -> {
            looperThread.mSubHandler.sendEmptyMessage(MSG_GET);
        });

    }

    class LooperThread extends Thread {
        public Handler mSubHandler;

        public LooperThread(String name) {
            super(name);
        }

        @Override
        public void run(a) {
            Looper.prepare();
            mSubHandler = new Handler(Looper.myLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    Log.i(TAG, "mSubHandler handler handleMessage thread : " + Thread.currentThread());
                    switch (msg.what) {
                        case MSG_GET:
                            double number = Math.random();
                            Message message = new Message();
                            message.what = MSG_RESULT;
                            message.obj = "dopezhi : " + number;
                            mUiHandler.sendMessage(message);
                            break;
                        default:
                            break; }}}; Looper.loop(); }}}Copy the code

Create another thread (looper_thread) in the main thread, initialize Looper in the looper_thread, create mSubHandler for the child thread, and hook to the child thread’s Looper.

Initialize mUIhandler in the main thread, hooked to the main thread Looper.

Click the button, and the main thread sends the message MSG_GET to the child thread. After receiving the message, the child thread sends the message MSG_RESULT to the main thread. Finally, the main thread displays the contents of the message sent by the child thread through Toast.

3. Enclosed wheel -HandlerThread

Why do handlerThreads exist? Looper.prepare, looper. Loop, looper. Loop, looper. prepare, looper. Loop If the main thread wants to get a Looper from the child thread, the child thread may execute looper. prepare to create a Looper, but the child thread may not immediately schedule the Looper and create it.

[-> MainActivity.java]

package com.dopezhi.handlerdemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private final static String TAG = "MainActivity";

    protected final int MSG_GET = 1;
    protected final int MSG_RESULT = 2;

    private HandlerThread mHandlerThread;

    // Handler instance in child thread
    private Handler mSubTreadHandler;

    private Handler mUiHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            Log.i(TAG, "mUiHandler handleMessage thread : " + Thread.currentThread());
            switch (msg.what) {
                case MSG_RESULT:
                    Toast.makeText(getApplicationContext(), (String) msg.obj, Toast.LENGTH_LONG).show();
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: "+ msg.what); }}};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "onCreate thread : " + Thread.currentThread());
        // Click the button to send a message to the child thread handler
        findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSubTreadHandler.sendEmptyMessage(MSG_GET);
                Log.i(TAG,"I frist"); }}); findViewById(R.id.jumoToSecond).setOnClickListener(v -> { Intent intent =new Intent();
            intent.setClass(this, SecondActivity.class);
            startActivity(intent);
        });
        initHandlerThread();

    }

    private void initHandlerThread(a) {
        // Create the HandlerThread instance
        mHandlerThread = new HandlerThread("handler_thread");
        // Start the thread
        mHandlerThread.start();
        // Get the Looper instance in the HandlerThread thread
        Looper loop = mHandlerThread.getLooper();

        //NEW, the idelHandler execution method is called back to when there is no message in the message queue
        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle(a) {
                Log.i(TAG,"The queue is empty");
                return false; }});// Create a Handler to bind to the thread
        mSubTreadHandler = new Handler(loop) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i(TAG, "mSubThreadHandler handleMessage thread : " + Thread.currentThread());
                switch (msg.what) {
                    case MSG_GET:
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Log.i(TAG,"sorry , I first");
                        double number = Math.random();
                        String result = "dopezhi: " + number;
                        // Send a message to the UI thread to update the UI
                        Message message = new Message();
                        message.what = MSG_RESULT;
                        message.obj = result;
                        // When the child thread receives the message, it sends it to the main thread Handler
                        mUiHandler.sendMessage(message);
                        break;
                    default:
                        break; }}}; }}Copy the code

As you can see, starting a child thread and executing the message loop does not require the following steps, just new HandlerThread.start.

[-> HandlerThread.java]

public class HandlerThread extends Thread

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

If you look at the source code for HandlerThread, which is essentially Thread, you override the run method to initialize Looper and start the Looper loop. If mhanderThread.getLooper () is called before then, wait() until Looper creates the call notifyAll.