Several methods of inter-thread communication in Android

One: Handler implements communication between threads

         Andriod provides Handler and Looper to meet the communication between threads. For example, a child thread downloads a picture from the network. When it is downloaded, it will send a message to the main thread. This message is passed through the Handler bound to the main thread.

In Android, the threads here are divided into threads with message loops and threads without message loops. Threads with message loops generally have a Looper, which is a new concept in android. Our main thread (UI thread) is the thread of a message loop. For this message loop mechanism, we introduce a new mechanism Handle, we have a message loop, we need to send corresponding messages to the message loop, custom messages generally have their own corresponding processing, message sending and clearing, message If you have a looper, encapsulate these in Handle. Note that Handle is only for those threads with Looper, whether it is UI thread or sub-thread, as long as you have Looper, I can add things to your message queue and do the corresponding processing.
But there is another point here, that is, as long as it is related to UI, it cannot be placed in the child thread, because the child thread cannot operate the UI, but can only perform other non-UI operations such as data and system.

  In Android, the threads here are divided into threads with message loops and threads without message loops. Threads with message loops generally have a Looper, which is a new concept in android. Our main thread (UI thread) is the thread of a message loop. In response to this message loop mechanism, we introduce a new mechanism Handler. We have a message loop, and we need to send corresponding messages to the message loop. Custom messages generally have their own corresponding processing, message sending and clearing. These are encapsulated in Handler. Note that Handler is only for those threads with Looper, whether it is UI thread or sub-thread, as long as you have Looper, I can add things to your message queue and do the corresponding processing.

But there is another point here, that is, as long as it is related to UI, it cannot be placed in the child thread, because the child thread cannot operate the UI, but can only perform other non-UI operations such as data and system.

 

  When a Handler is created, it will be bound to the message queue of this thread. If it is created in the main thread, there is no need to write code to create the message queue. The default message queue will be created in the main thread. But if it is a child thread, you must initialize the thread's message queue before creating a Handler.

2. Solve the problem of communication between threads: use AsyncTask:

With AsyncTask (abstract class), you can switch from child thread to main thread very easily even if you don't know anything about asynchronous message processing mechanism. (The implementation principle behind AsyncTask is also based on the asynchronous message processing mechanism, and Android has helped us encapsulate it)

Basic usage:

1. Create a class to inherit the AsyncTask abstract class, and you can specify three generic parameters for the AsyncTask class:

         Params: Parameters that need to be passed in when executing AsyncTask, which can be used in background tasks;

         Progress: When the background task is executed, if the current progress needs to be displayed on the interface, the generic type specified here is used as the progress unit;

         Result: After the task is executed, if the result needs to be returned, use the generic type specified here as the return value type;

An example is as follows:

ClassDownloadTask extends AsyncTask<Void, Integer, Boolean>{

……

}

Here, the generic type of the first parameter is specified as void: it means that there is no need to pass parameters to the background task when executing AsyncTask;

The second parameter generic type is specified as Integer: Indicates the use of integer data as the progress display unit;

The third parameter, the generic type, is specified as Boolean: it means that Boolean data is used to feedback the execution result;

2. Rewrite several methods in the AsyncTask class to complete the customization of the task:

①onPreExecute(): This method is called before the background task starts to execute, and is used to perform some initialization operations on the interface, such as displaying a progress bar dialog box.

②doInBackground(Params...): All the code in this method will run in the child thread, and the time-consuming tasks should be processed here. Once the task is completed, the execution result of the task can be returned through the return statement. If the third generic parameter type of AsyncTask is specified as Void, the task execution result can not be returned. [Note that UI operations cannot be performed in this method. If you need to update UI elements, such as feedback on the execution progress of the current task, you can call the publishProgress(Progress...) method to complete.

③onProgressUpdate(Progress...): When the publishProgress(Progress...) method is called again in the background task, this method will be called soon, and the parameters carried in the method are passed in the background task. In this method, the UI can be operated, and the interface elements can be updated accordingly using the values ​​in the parameters.

④onPostExecute(Rusult): When the background task is executed and returns through the return statement, this method will be called soon. The returned data will be passed to this method as a parameter, and some finishing work will be done here, such as reminding the end of the task, closing the progress bar dialog box, etc.

3. Start the task: new an instance of this class, and then call its execute() method;

Three: EventBus realizes communication between threads

Background introduction

If you've learned design patterns, you'll definitely use the Observer pattern when you want to notify other components of something happening. Well, since you can think of this design pattern, let’s take a look at EventBus, an amazing Android open source framework. The main function is to replace Intent, Handler, and BroadCast to pass messages between Fragment, Activity, Service, and thread. His most powerful advantages are low overhead, concise code, and decoupled code.

  • Event: event
  • Subscriber: event subscriber, receiving specific events.
  • Publisher: Event publisher, used to notify Subscriber that an event has occurred.

Among them, Event can be any type of object. Subscriber is a function that starts with the agreed onEvent, specifically onEvent, onEventMainThread, onEventBackgroundThread, onEventAsync. Publisher can send events anywhere in any thread through post(Object).

The official diagram intuitively illustrates the architecture of this observer mode :

write picture description here

Follow the documentation for the open source library components:

  1. Add in the project gradle: compile 'de.greenrobot:eventbus:2.4.0'.
  2. Follow the documentation HOWTO.md.

The difference between the 4 functions of Subscriber starting with onEvent:

  • onEvent: The event is processed on the same thread as the event is sent, so the event processing time should not be too long, otherwise it will affect the event sending thread.

  • onEventMainThread: The processing of the event will be executed in the UI thread. The event processing time can not be too long, and the notorious ANR will appear if it is too long.

  • onEventBackgroundThread: The processing of the event will be performed in a background thread. Although the name is BackgroundThread, the event processing is in the background thread, but the event processing time should not be too long, because if the thread sending the event is a background thread, the event will be executed directly on the current background thread; if the current thread is the UI thread, the event will be It is added to a queue, and a thread processes these events in turn. If an event takes too long to process, it will block the dispatch or processing of subsequent events.

  • onEventAsync: Event processing will be executed in a separate thread, mainly used to perform time-consuming operations in the background thread, each event will open a thread, but it is best to limit the number of threads.

Four: Introduce several methods in detail

1、runOnUiThread()

The child thread holds the current Activity reference (if it is Activity mActivity;), you can call the runOnUiThread(Runnable r) method of mActivity.

2. post() and postDelay()

If the child thread holds a reference to a View and wants to update the View, it can call the post(Runnable r) or postDelay(Runnable r) method of the View object.

Handler objects also have a post() method. In fact, in the Android source code, these post() methods are implemented by the following third method: Handler + Message.

3、Handler + Message或者Handler + Thread + Message

When the main thread is established, there is a Looper by default, which can process the message queue.

Create a Handler object on the main thread, override its handleMessage() method, and implement UI update in this method.

Example:

copy code
private static final int MSG_CODE = 1001;

private Handler mHandler = new Handler()

{

@Override

public void handleMessage(Message msg)

{

// Receive and process the message


if(msg.what == MSG_CODE)

{

// UI update

}

}

};



public void doSomething()

{

new Thread()

{

@Override

public void run()

{

// Sub thread sends information


Message msg = mHandler.obtainMessage(MSG_CODE);

msg.sendToTarget();

}

}.start();

}
copy code

4、Broadcast

The broadcast is sent in the child thread, the broadcast is received in the main thread and the UI is updated

5、AsyncTask

AsyncTask can easily open a new thread and return the result to the UI thread, without requiring the developer to manually open a new thread or using a Handler, which is very convenient.

It should be noted that AsyncTask is an abstract class, and the meanings of its three generic parameters are as follows:
AsyncTask<Param, Progress, Result>
Param: The parameter type sent to the newly opened thread
Progress: The type representing the progress of the task processing.
Result: The type of the value returned to the UI thread after the thread task is processed.

There are four abstract functions in this class, onPreExecute(), doInBackground(Params... param),
onProgressUpdate(Progress... progress), onPostExecute(Result result).
Except for the doInBackground(Params...) method, the other three methods run on the UI thread.

Customize a class that inherits AsyncTask and implements at least the doInBackground() function. During the execution of this function, you can call publishProgress(Progress...) at any time to report its execution progress. Another method, onProgressUpdate(Progress... progress), is triggered at this point to update the progress of the task processing on some controls in the UI thread, such as the ProgressBar.

You can also wait for doInBackground() to finish executing and enter the onPostExecute() method before updating the UI controls.

Can cancel the task opened by AsyncTask at any time and in any thread (call the cancel(boolean mayInterruptIfRunning) method of the custom AsynTask subclass)

The usage example is as follows:

//If I remember correctly, this example should be cut from the official website during the previous summary

copy code
public void onClick(View v) {

   new DownloadImageTask().execute("http://example.com/image.png");

}



private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

   /** The system calls this to perform work in a worker thread and

     * delivers it the parameters given to AsyncTask.execute() */

   protected Bitmap doInBackground(String... urls) {

       return loadImageFromNetwork(urls[0]);

   }

   

   /** The system calls this to perform work in the UI thread and delivers

     * the result from doInBackground() */

   protected void onPostExecute(Bitmap result) {

       mImageView.setImageBitmap (result);

   }

}
copy code

6、EventBus

  • What is EventBus

    EventBus is an efficient publish/subscribe event bus mechanism under Android. The role is to replace the traditional Intent, Handler, Broadcast or interface functions to transfer data and execute methods between Fragment, Activity, Service, and threads. It is characterized by concise code and is a publish-subscribe design pattern (Publish/Subsribe), or observer design pattern.

 

  • Download EventBus

    1. Download the EventBus library:

    2. Put EventBus-2.4.0.jar into libs

 

  • How to use EventBus

    1. Define events, define a class, and inherit the default Object, which is used to distinguish events and transmit data. This example is MsgEvent1 and MsgEvent2
    2. Add subscribers: EventBus.getDefault().register(this); Using the class as subscribers, the framework will obtain all methods and their parameters through reflection mechanism.
        The subscriber's class can define one or more of the following methods to receive events:

copy code
         public  void onEvent(MsgEvent1 msg)

        public  void onEventMainThread(MsgEvent1 msg)

        public  void onEventBackgroundThread(MsgEvent1 msg)

        public  void onEventAsync(MsgEvent1 msg)
copy code

   3. The publisher publishes the event: EventBus.getDefault().post(new MsgEvent1("message 1 sent by the main thread"));
      Once this method is executed, all subscribers will execute the method defined in the second step.
   4. Unsubscribe: EventBus.getDefault().unregister(this); When the subscriber is no longer used or closed, it is best to unsubscribe and no longer accept event messages.
   5. Note: The parameter of the publisher's post method is of type Object, that is, any event can be published. When a subscriber accepts a message, it can be executed as long as any of the four methods in the second step are defined and the parameters are the same as those published by the publisher. Publishers can also receive messages through the second step, and subscribers can also send messages to themselves as publishers.

    • Code implementation (this example is the interaction between two Fragments, it can also be the interaction between Service, Activity, Fragment and any class)
    • Click the entry in the left panel to send the event, the right panel (another Fragment) receives the event, displays the interface, and prints the log.
    • Code download http://yunpan.cn/cctFTVuWtyIgK access password 66ed

 

      

1. Main interface construction:
java

 

copy code
public class MainActivity extends FragmentActivity {

        @Override
        protected  void onCreate (Bundle savedInstanceState) {
                 super .onCreate (savedInstanceState);
                setContentView(R.layout.activity_main);
        }

}
copy code

 

xml

copy code
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="?android:attr/dividerHorizontal"
    android:orientation="horizontal"
    android:showDividers="middle"
    android:baselineAligned="false"
    tools:context="com.itheima.eventbusdemo.MainActivity" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.itheima.eventbusdemo.LeftFragment"
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/right_fragment"
        android:name="com.itheima.eventbusdemo.RightFragment"
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="3" />

</LinearLayout>
copy code

2. 定一个事件类MsgEvent1 (MsgEvent2与此一致):

copy code
public class MsgEvent1 {
        private String msg;
        
        public MsgEvent1(String msg) {
                super();
                this.msg = msg;
        }
        public String getMsg() {
                return msg;
        }
}
copy code

3. 将右面板作为订阅者, 执行方法并接收数据:

copy code
public class RightFragment extends Fragment {
        private TextView tv;
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);                
                // 界面创建时,订阅事件, 接受消息
                EventBus.getDefault().register(this);
        }
        @Override
        public void onDestroy() {
                super.onDestroy();
                // 界面销毁时,取消订阅
                EventBus.getDefault().unregister(this);
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
        // 布局只有一个TextView,不再贴代码
                View view = inflater.inflate(R.layout.fragment_right, null);
                tv = (TextView) view.findViewById(R.id.tv);
                return view;
        }
        
        /**
         * 与发布者在同一个线程
         * @param msg 事件1
         */
        public void onEvent(MsgEvent1 msg){
                String content = msg.getMsg() 
                                + "\n ThreadName: " + Thread.currentThread().getName() 
                                + "\n ThreadId: " + Thread.currentThread().getId();
                System.out.println("onEvent(MsgEvent1 msg)收到" + content);
        }
        
        /**
         * 执行在主线程。
         * 非常实用,可以在这里将子线程加载到的数据直接设置到界面中。
         * @param msg 事件1
         */
        public void onEventMainThread(MsgEvent1 msg){
                String content = msg.getMsg() 
                                + "\n ThreadName: " + Thread.currentThread().getName() 
                                + "\n ThreadId: " + Thread.currentThread().getId();
                System.out.println("onEventMainThread(MsgEvent1 msg)收到" + content);
                tv.setText(content);
        }
        
        /**
         * 执行在子线程,如果发布者是子线程则直接执行,如果发布者不是子线程,则创建一个再执行
         * 此处可能会有线程阻塞问题。
         * @param msg 事件1
         */
        public void onEventBackgroundThread(MsgEvent1 msg){
                String content = msg.getMsg() 
                                + "\n ThreadName: " + Thread.currentThread().getName() 
                                + "\n ThreadId: " + Thread.currentThread().getId();
                System.out.println("onEventBackgroundThread(MsgEvent1 msg)收到" + content);
        }
        
        /**
         * 执行在在一个新的子线程
         * 适用于多个线程任务处理, 内部有线程池管理。
         * @param msg 事件1
         */
        public void onEventAsync(MsgEvent1 msg){
                String content = msg.getMsg() 
                                + "\n ThreadName: " + Thread.currentThread().getName() 
                                + "\n ThreadId: " + Thread.currentThread().getId();
                System.out.println("onEventAsync(MsgEvent1 msg)收到" + content);
        }
        
        /**
         * 与发布者在同一个线程
         * @param msg 事件2
         */
        public void onEvent(MsgEvent2 msg){
                String content = msg.getMsg() 
                                + "\n ThreadName: " + Thread.currentThread().getName() 
                                + "\n ThreadId: " + Thread.currentThread().getId();
                System.out.println("onEvent(MsgEvent2 msg)收到" + content);
                tv.setText(content);
        }
}
copy code

4. 在左面板发布消息。(任意类都可以发布消息)

copy code
public class LeftFragment extends ListFragment {

        @Override
        public void onViewCreated(View view, Bundle savedInstanceState) {
                super.onViewCreated(view, savedInstanceState);

                String[] strs = new String[]{"主线程消息1", "子线程消息1", "主线程消息2"};
                setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, strs));
        }
        
        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
                switch (position) {
                case 0:
                        // 主线程
                        System.out.println(
                                "----------------------主线程发的消息1" 
                                + " threadName: "+ Thread.currentThread().getName() 
                                + " threadId: " + Thread.currentThread().getId());
                        EventBus.getDefault().post(new MsgEvent1("主线程发的消息1"));
                        break;
                case 1:
                        // 子线程
                        new Thread(){
                                public void run() {
                                        System.out.println(
                                                "----------------------子线程发的消息1" 
                                                + " threadName: "+ Thread.currentThread().getName() 
                                                + " threadId: " + Thread.currentThread().getId());
                                        EventBus.getDefault().post(new MsgEvent1("子线程发的消息1"));
                                };
                        }.start();
                        
                        break;
                case 2:
                        // 主线程
                        System.out.println(
                                        "----------------------主线程发的消息2" 
                                        + " threadName: "+ Thread.currentThread().getName() 
                                        + " threadId: " + Thread.currentThread().getId());
                        EventBus.getDefault().post( new MsgEvent2("Message 2 sent by the main thread" ));
                         break ;
                }
        }
        
}
copy code

Click the left entry respectively, Log output analysis

 

EventBus framework principle flow chart

        


1. Publisher is the publisher, and publishes the message event Event to the event bus through the post() method
. 2. EventBus is the event bus. It traverses all the subscribers who have registered the event, finds four methods such as onEvent inside, and distributes the Event
3. Subscriber is a subscriber and receives messages sent by the event bus. That is, the onEvent method is executed. Note that the parameter type must be the same as the parameter published by the publisher.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325528044&siteId=291194637