Android Thread Communication

Posted by shamsuljewel on Wed, 15 May 2019 19:20:19 +0200

Communication between Android main thread and sub-thread, communication between sub-thread and sub-thread

1. The main thread sends messages and the sub-threads execute operations.

In fact, in general, after the main thread has executed all the necessary logic, it will directly new Thread().start(); (Note, memory leak is not considered), because we generally do not need to let the sub-threads stick at a certain point until the message is executed, if the sub-threads are not stuck, the sub-threads will automatically end after execution. Of course, it's difficult for us to determine when the new start's Thread will execute. But you really need the main thread to send a message, and the sub-thread to perform the operation after receiving the message. You can use the following method.

//First create a sub-thread
HandlerThread handlerThread = new HandlerThread("name");//I haven't seen the source code, I don't know the specific function of the parameters, I only know the name, the type is String.
handlerThread.start();//Remember to start threads
/**
 * Note that this is the point (ignore my non-standard comments)
 * handler The handleMessage(Message msg) method is executed in which thread, regardless of which thread the handler is new, but only with the Looper it binds.
 * Looper is all bound to its thread (one-to-one correspondence), so handler is equivalent to the thread binding behind Looper. Handler constructs without Looper parameters by default
 * With the Looper binding of the current thread, the main thread configures Looper by default, while the sub-thread needs to call Looper.prepare() before it is configured by the system, so the initial handler in the sub-thread
 * Initialization should be placed between Looper.prepare() and Looper.loop() (this is a dead cycle). The foundation is not solid enough, and it is not known if loop() will be used in sub-threads.
 * Memory leak
 */
Handler mHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        //This part of the operation is performed in sub-threads
        return false;
    }
});

Of course, you can also customize a thread class to provide an interface. The interface method can be defined according to your own needs. The run method in the thread is a dead loop. Then the main thread calls the interface, and the sub-thread performs the subsequent operation (which is not recommended).

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    MyThread myThread = new MyThread();
    OnMessageArrivedListener onMessageArrivedListener = myThread.getOnMessageArrivedListener();
    new Thread(myThread).start();
    //Call at your own time
    onMessageArrivedListener.onMessageArrived();
}

class MyThread implements Runnable{
    
    private boolean hasArrived = false;
    private OnMessageArrivedListener onMessageArrivedListener = new OnMessageArrivedListener() {
        @Override
        public void onMessageArrived() {
            hasArrived = true;
        }
    };
    public OnMessageArrivedListener getOnMessageArrivedListener(){
        return onMessageArrivedListener;
    }
    
    @Override
    public void run(){
        while(true){
            if(hasArrived){
                //Execute subthread operations
                break;
            }
        }
    }
}

interface OnMessageArrivedListener{
    void onMessageArrived();
}

Can you send a message using handler in the main thread, and the customized sub-thread can perform subsequent operations after it receives the message? Yes, it's a bit of a hassle. You need to have your own Looper in the sub-thread before you can use it. This is very troublesome. Although the sub-thread can open Looper or handler directly, the main thread does not know when to use it. This is strongly not recommended.

 

2. Subthreads send messages and main threads perform operations

This is the simplest and most useful one.

We can create a global or final modified handler object in the main thread, call newThread().start() after initialization, and then use handler directly in the sub-thread, which is very simple and convenient.

private Handler mHandler;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //The operations here are executed in the main thread
            return false;
        }
    });
    new Thread(new Runnable() {
        @Override
        public void run() {
            mHandler.sendEmptyMessage(1);
        }
    }).start();
}

Another way is to create handler objects in sub-threads. As mentioned earlier, it is also very simple and convenient to create handler objects in which thread handleMessage is executed regardless of where new it is executed.

new Thread(new Runnable() {
    @Override
    public void run() {
        Handler handler = new Handler(getMainLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //The operations here are executed in the main thread
                return false;
            }
        });
    }
}).start();

3. Communication between sub-threads and sub-threads

Personal suggestion is to use the main thread as the intermediary. When a sub-thread has finished executing, it sends a message to the main thread. After the main thread receives the message, it creates the next sub-thread, which is equivalent to indirectly realizing the communication between sub-threads and sub-threads.

If you feel unsatisfied with this, I also think of a way, but I personally do not recommend this method, I suggest to go to other blogs to find a better way.

This method is similar to the previous one, that is, both sub-threads refer to the same interface object, create the interface instance object in the receiving sub-threads, and call the interface method after the sending sub-threads perform the operation. The method is defined according to their own needs. The receiving sub-threads refer to the same interface instance object to achieve the purpose of obtaining messages.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    GetMessageThread getMessageThread = new GetMessageThread();
    SendMessageThread sendMessageThread = new SendMessageThread();
    //At this point they are just children... They're just a class object, not a sub-thread.
    sendMessageThread.setOnMessageArrivedListener(getMessageThread.getOnMessageArrivedListener());
    //After executing the following code, they're there. Pay attention to the order, although it may not be useful.
    new Thread(getMessageThread).start();
    new Thread(sendMessageThread).start();
}

interface OnMessageArrivedListener{
    void onMessageArrived(Object object);//All are customized and can be modified according to requirements. The return value type is also customized.
}

class GetMessageThread implements Runnable{
    private boolean isOk = false;
    private OnMessageArrivedListener onMessageArrivedListener = new OnMessageArrivedListener() {
        @Override
        public void onMessageArrived(Object object) {
            //Determine the value of isOk based on the message received
            //as
            isOk = true;
        }
    };

    //Return the interface object to the sub-thread sending the message
    public OnMessageArrivedListener getOnMessageArrivedListener(){
        return onMessageArrivedListener;
    }

    @Override
    public void run(){
        while (true){
            if(isOk){
                //Execution operation
                break;
            }
        }
    }
}

class SendMessageThread implements Runnable{
    private OnMessageArrivedListener onMessageArrivedListener;
    public void setOnMessageArrivedListener(OnMessageArrivedListener onMessageArrivedListener){
        this.onMessageArrivedListener = onMessageArrivedListener;
    }
    
    @Override
    public void run(){
        //Execute the operation and call the interface method as needed
        Object o = new Object();//Decide what to pass on
        while(true){
            if(onMessageArrivedListener != null){
                onMessageArrivedListener.onMessageArrived(o);
                break;
            }
        }
    }
}

This method because my own computer system foundation is not very solid, I do not know whether it will cause a deadlock, the designer said, this is really terrible! So I strongly don't recommend this method either.

Topics: Android