Android [intermediate tutorial] in-depth analysis of Android message mechanism

Source: Internet
Author: User

This article was seen in a forum. The author wrote quite well and analyzed the handler message mechanism of Android. Let's take a look:

In Android, messages are often used during information interaction between threads or threads. If we are familiar with the Internal principles of these basic things, it will make it easy and better for us to build a system, avoid some low-level errors. Before learning about the message mechanism in Android, we should first understand several message-related classes:

1. Message

A message object, as its name implies, is a class that records message information. This class has several important fields:

A. arg1 and arg2: We can use two fields to store the integer values we need to pass. in service, we can store the service ID.

B. OBJ: this field is of the object type. We can use this field to transmit multiple entries to the receiver of the message.

C. what: this field can be said to be a message identifier. In message processing, we can perform different processing based on different values of this field, similar to when we are processing a button event, switch (v. GETID () determines which button is clicked.

When using message, we can create a message instance through new message (). However, for Android, we recommend that you use message. Obtain () or handler. obtainmessage () to obtain the message object. This is not necessarily to create a new instance directly, but to check whether there is any available message instance from the message pool. If there is a message instance, the instance is taken out and returned. If no message instance is available in the message pool, a new message object is created based on the specified parameter. By analyzing the source code, we can know that the Android system instantiates 10 message objects in the message pool by default.

2. messagequeue

A message queue is used to store the data structure of a message object and store messages according to the "first-in-first-out" principle. Storage does not actually mean saving, but concatenates message objects in a linked list. The messagequeue object does not need to be created by ourselves, but is managed by a loose object. A thread can have at most one messagequeue. We can use logoff. myqueue () to obtain the messagequeue in the current thread.

3. Logoff

Messagequeue manager, in a thread, if there is a loose object, there must be a messagequeue object, and only one loose object and one messagequeue object. In the Android system, except for the main thread, there is a default logoff object. By default, other threads do not have logoff objects. If we want the newly created thread to have a logoff object, we should first call The logoff. Prepare () method, and then call the logoff. Loop () method. The typical usage is as follows:

Class looperthread extends thread {public handler mhandler; Public void run () {lorule. Prepare (); // other operations to be processed lorule. Loop ();}}

If there is a logoff object in our thread, we can get it through logoff. mylogoff (). In addition, we can also get the logoff. getmainlogoff () in the current application system.Main thread. Note that if the logoff object is located in the main thread of the application, lorule. mylorule () and lorule. getmainlorule () obtain the same object.

4. Handler

Message processor. Through the handler object, we can encapsulate the message object, and then add the message object to messagequeue through sendmessage (MSG); When messagequeue loops to this message, the handlemessage () method of the handler object corresponding to the message object will be called to process it. Because the handlemessage () method processes messages, we should write a class that inherits from handler and then process the required operations in handlemessage.

The following code is used to analyze how messages are processed in Android. First, paste the test code:

/***** @ Author coolszy * @ blog login class messageservice extends Service {Private Static final string tag = "messageservice"; Private Static final int Kuka = 0; private loose looper; private servicehandler handler;/*** because the processing of messages is in the handlemessage () method of handler, We need to write the class * inherited from the handler class, and then in handlemessage () write the required functional code * @ author coolszy **/private final class servicehand Ler extends handler {public servicehandler (Looper loler) {super (logoff) ;}@ override public void handlemessage (Message MSG) {// determine which message switch (MSG) is based on what field. what) {Case Kuka: // obtain the OBJ field of MSG. We can compile the required function code log here. I (TAG, "The OBJ field of MSG:" + MSG. OBJ); break; // other cases default: break;} // if our service has completed the task, stop service stopself (MSG. arg1) ;}@ override public void oncreate () {log. I (TAG, "messageservice --> oncreate ()"); // by default, the service runs in the main thread, and the service is generally time-consuming, if // is placed in the main thread, the interaction between the program and the user will be affected. Therefore, put the service // in a separate thread and execute handlerthread thread = new handlerthread ("messagedemothread", process. thread_priority_background); thread. start (); // obtain the logoff object logoff = thread in the current thread. getlooper (); // create a handler object and pass the logoff parameter so that handler, // logoff, and messagequeue are connected to handler = new servicehandler (loler );} @ override public int onstartcommand (intent, int flags, int startid) {log. I (TAG, "messageservice --> onstartcommand ()"); // obtain a message instance message MSG = handler from the message pool. obtainmessage (); // arg1 stores the thread ID. In the handlemessage () method, // we can stop the service MSG by using the stopself (startid) method. arg1 = startid; // MSG flag of MSG. what = Kuka; // here I create a date object, assign the value to the OBJ field // in reality, we can pass the object date = new date (); MSG through obj. OBJ = date; // Add MSG to the messagequeue handler. sendmessage (MSG); Return start_sticky;} @ override public void ondestroy () {log. I (TAG, "messageservice --> ondestroy ()") ;}@ override public ibinder onbind (intent) {return NULL ;}}

Running result:


Note: In the test code, we use the handlerthread class, which is a subclass of thread and will create a logoff object during the running of this class, using this class saves us the trouble of writing thread subclasses and creating logoff.

Next we will analyze the running process of the program:

1. oncreate ()

First, the oncreate () method will be called when the service is started. In this method, we create a handlerthread object and provide the thread name and priority.

Then we call the START () method. executing this method will call the run () method of the handlerthread object:

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

In the run () method, the system calls the logoff loop () method to the thread:

public static final void loop() {        Looper me = myLooper();        MessageQueue queue = me.mQueue;        while (true) {            Message msg = queue.next(); // might block            //if (!me.mRun) {            //    break;            //}            if (msg != null) {                if (msg.target == null) {                    // No target is a magic identifier for the quit message.                    return;                }                if (me.mLogging!= null) me.mLogging.println(                        ">>>>> Dispatching to " + msg.target + " "                        + msg.callback + ": " + msg.what                        );                msg.target.dispatchMessage(msg);                if (me.mLogging!= null) me.mLogging.println(                        "<<<<< Finished to    " + msg.target + " "                        + msg.callback);                msg.recycle();            }        }    }

Through the source code, we can see that the loop () method is an endless loop, and the message object will be continuously obtained from the messagequeue object. If the messagequeue object does not have a message object, this loop ends, then continue the loop. If a message object exists, execute
Msg.target.dispatchmessage(msg.pdf, but what is the value of this msg.tar get field? We will temporarily stop tracking source code and return it to the oncreate () method. After the thread executes the START () method, we can get the logoff object of the thread, and then a new servicehandler object, we pass the logoff object to the servicehandler constructor to establish a connection between handler, loiter, and messagequeue.

2. onstartcommand ()

After the onstart () method is executed, the onstartcommand () method is executed. First, we get a message instance from the message pool, and assign values to the arg1, what, and OBJ fields of the message object. Next, call the sendmessage (MSG) method. We track the source code. This method will call sendmessagedelayed (MSG,
0) method, and the sendmessagedelayed () method will call sendmessageattime (MSG, systemclock. uptimemillis () + delaymillis). In this example, We need to extract the sentence code msg.tar get
= This: the target of MSG points to this, and this is the servicehandler object. Therefore, the target field of MSG points to the servicehandler object. At the same time, this method calls the enqueuemessage (MSG,
Uptimemillis) method:

final boolean enqueueMessage(Message msg, long when) {        if (msg.when != 0) {            throw new AndroidRuntimeException(msg                    + " This message is already in use.");        }        if (msg.target == null && !mQuitAllowed) {            throw new RuntimeException("Main thread not allowed to quit");        }        synchronized (this) {            if (mQuiting) {                RuntimeException e = new RuntimeException(                    msg.target + " sending message to a Handler on a dead thread");                Log.w("MessageQueue", e.getMessage(), e);                return false;            } else if (msg.target == null) {                mQuiting = true;            }            msg.when = when;            //Log.d("MessageQueue", "Enqueing: " + msg);            Message p = mMessages;            if (p == null || when == 0 || when < p.when) {                msg.next = p;                mMessages = msg;                this.notify();            } else {                Message prev = null;                while (p != null && p.when <= when) {                    prev = p;                    p = p.next;                }                msg.next = prev.next;                prev.next = msg;                this.notify();            }        }        return true;    }

The main task of this method is to add the message object to messagequeue (the most basic thing about the data structure, which can be understood by drawing on your own ).

Handler. sendmessage () --> handler. sendmessagedelayed () --> handler. sendmessageattime () --> msg.tar get = This; queue. enqueuemessage => Add MSG to the Message Queue

3. handlemessage (MSG)

After onstartcommand () is executed, the methods in our service are executed. How is handlemessage () called? When get. dispatchmessage (MSG); then the dispatchmessage () method in the servicehandler object is executed:

public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

This method first checks whether callback is empty. We do not assign a value to callback during tracking. Therefore, the callback field is empty, so the handlemessage () method will be executed in the end, that is, the rewrite method in the servicehandler class. In this method, the code is determined based on the value of what field.

So far, we can see that a message is sent by handler, messagequeue is in the queue, logoff is extracted, and then return to handler's arms again. This circle also helps us change the synchronous operation into an asynchronous operation.

 

Source code download: Handler message mechanism

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.