Android Ui update (2)

Source: Internet
Author: User

Android Ui update (2)
World in code

 

1. Review

The first part describes the methods and opinions on Ui thread update, and then describes the Thread Check Mechanism. Here we will analyze in detail the message system model in Android, which is the core of Ui update. Of course, what we will talk about here is no longer simply the scope of updating the Ui. However, it is worth learning and analyzing. In addition, there are a lot of explanations on this aspect on the Internet. This article is also a comprehensive summary and described and summarized with your own understanding. At the same time, I would like to thank those who have advanced accomplishments for their criticism and correction.

Refining

The message mechanism in Android mainly includes the following points, which are also explained here:
  
1. HandlerThe scheduled message and runnable object execute corresponding actions in different threads.
2. LogoffMessage pump, used to enable a message loop for a thread
3. MessageQueueA Message Queue that follows FIFO first-in-first-out rules and accesses the Message in the form of a linked list for Logoff to extract

2. Analysis

To facilitate analysis, take a look at the model diagram:
  
First, initialize a looper in a thread and prepare (prepare), and then create the processing Object Handler of the looper object. Then, when interactive changes are required, the looper object can be initialized in other threads (or its own threads) use handler to send messages to the Message Queue MessageQueue. Finally, logoff automatically extracts messages in an orderly manner (messages are suspended if no messages exist) and sends them to handler for message processing logic.
Orz, my concept description is still a mess, please refer to the Code Description:
For example, if we have a thread dedicated to a class of processing logic and only allow this thread to process this type of logic, how can we do this?
1.Define a logoff in a thread

Logoff. prepare (); // a bit more. See the following for details.

2.Define a Handler for processing messages

Handler = new Handler () {@ Override public void handleMessage (Message msg) {super. handleMessage (msg); // processing logic }};

3.Start logoff, start work, and poll messages

Logoff. loop (); // see the following for details. // if you want to stop it, use logoff. quit ();

4.The data or callback object callback to be processed by other threads is sent to the Message Queue MessageQueue through Handler in Message mode.

    handler.sendMessage(msg)

5.The logoff loop () method retrieves the message from the message queue and wakes up the processing logic.

// That is, the code for (;) in the loop () method {// obviously this endless loop is waiting for the Message to arrive and processing Message msg = queue. next (); // obtain a message if (msg = null) {return;} msg.tar get. dispatchMessage (msg); // call the handler bound to the message to execute the processing logic // other code ....}

6.Handler jumps to the process of executing the processing logic

Public void dispatchMessage (Message msg) {if (msg. callback! = Null) {// if a callback exists, call handleCallback (msg);} else {if (mCallback! = Null) {if (mCallback. handleMessage (msg) {return ;}} handleMessage (msg) ;}// call the callback method private static void handleCallback (Message message) {Message. callback. run ();}

The above is the entire process of the message system, and we will analyze the details one by one.

3. Go back to our UI update explanation

When ActivityManagerService creates a new process for the Android Application and starts the activity, the main thread ActivityThread is first created. Process. java @ start ("android. app. ActivityThread ",...) The static member function main that executes ActivityThread is loaded. Open this method:

Public static void main (String [] args) {// other code .. we only look at the useful parts, and the others have skipped the logoff. prepareMainLooper (); // prepare looper, note, bind to the current main thread ActivityThread thread = new ActivityThread (); // start a new ActivityThread thread. attach (false); // finally execute to activity // other code .. logoff. loop (); // start Logoff

This static function does two things: one is to create an ActivityThread instance in the main thread, and the other is to enable the main thread to enter the message loop through the logoff class.
Then, the code goes through a series of logic (ActivityThread. attach-> IActivityManager. attachApplication-> attachApplicationApplicationThread. scheduleLaunchActivity->... -> ActivityThread. initialize mlaunchactivity). The attach method of the activity is called.
We open the activity class. We can see that it defines the uiThread and Handler parameters.

ActivityThread mMainThread; // corresponding to the above ActivityThread Thread private Thread mUiThread; // The main Ui Thread final Handler mHandler = new Handler (); // This handler is used by the activity to process the Ui. Our own handler is actually equivalent to redefining this mHandler;

Let's take a look at the attach method of the activity:

Final void attach (Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances failed, configuration config, IVoiceInteractor voiceInteractor) {mUiThread = Thread. currentThread (); // the current main thread Ui thread mMainThread = aThread; // corresponding to the above ActivityThread}

Therefore, sendMessage is used when we want to update the UI, for example, runOnUiThread. Let's look at this method.

Public final void runOnUiThread (Runnable action) {/*** if the current Thread is not the Ui main Thread, use the defined mHandler */if (Thread. currentThread ()! = MUiThread) {mHandler. post (action) ;}else {action. run ();}}

Open post method:

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

Or a familiar recipe or taste .. Encapsulate the message and send it out. Well, let's look back at the four methods in the first article:
1. A new handler is used to sendMessage ();
2. Use new handler for post
However, mHandler, which has already defined the Activity, is redefined and encapsulated as a message for sending;
3. runOnUiThread
Similarly, the mHandler of the Activity is used to send messages;
4. view. post
Take a look at the Code:

    public boolean post(Runnable action) {        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null) {            return attachInfo.mHandler.post(action);        }        // Assume that post will succeed later        ViewRootImpl.getRunQueue().post(action);        return true;    }

AttachInfo should not be unfamiliar. When you attach a view to an Activity, some attributes of the activity will be appended to AttachInfo. Here, the mHandler is also called and then post .. After a circle, I came back.
  
To sum up, the entire UI update mechanism is actually a simple implementation of the Android message system model. At this point, we have finished updating the UI. As a supplement, let's take a thorough look at the entire messaging system model from the source code.

4. Excellent solution

Here, we will analyze the source code to understand the specific implementation of each part, and then integrate it with the content mentioned above, so that we can gain a deep understanding of the specific implementation and use it in different scenarios.
We will view them one by one in order.
First, let's talk about the message object. After all, it is the most basic element of other operations.

4.1 Message
Public final class Message implements Parcelable {// inherits Parcelable for data transmission/** several data types **/public int arg1; public int arg2; public Object obj; Bundle data; public int what; // long when, the identity of the message to be processed by handler; // when to process the Handler target; // The target handler Runnable callback that processes the message; // callback method int flags; // tag ID static final int FLAG_IN_USE = 1 <0; // available (recycling) static final int FLAG_ASYNCHRONOUS = 1 <1; static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; public Messenger replyTo; // optional object, which can be used to record the Message next of the sender or receiver; // The Next message of this message/*** opens a message pool to facilitate the reuse of messages cyclically, avoid generating new objects and allocating memory */private static final Object sPoolSync = new Object (); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50 ;}

Since we mentioned that the message pool uses obtain () to save memory resources, let's take a look at this obtain ()

/*** Return a new message instance from the message pool to avoid allocating new objects. */Public static Message obtain () {synchronized (sPoolSync) {if (sPool! = Null) {Message m = sPool; sPool = m. next; m. next = null; sPoolSize --; return m ;}} return new Message ();}

Then there is a series of applications based on this method: First call the obtain () method, and then assign values to parameters of the obtained Message instance to pass the parameter.

// Retrieve a Message object and assign the existing Message content to the public static Message obtain (Message orig) {Message m = obtain (); m. what = orig. what; m. arg1 = orig. arg1; m. arg2 = orig. arg2; m. obj = orig. obj; m. replyTo = orig. replyTo; if (orig. data! = Null) {m. data = new Bundle (orig. data);} m.tar get = orig.tar get; m. callback = orig. callback; return m;} public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj) {Message m = obtain (); m.tar get = h; m. what = what; m. arg1 = arg1; m. arg2 = arg2; m. obj = obj; return m;} // call obtain and assign values. public static Message obtain (Handler h) is not listed one by one ){//..} public static Message obtain (Handler h, Runnable callback ){//..} public static Message obtain (Handler h, int what ){//...} public static Message obtain (Handler h, int what, Object obj ){//...} public static Message obtain (Handler h, int what, int arg1, int arg2 ){//...}

Then, recycle and release the recycle, which returns a message pool instance. You cannot use this method after release.

Public void recycle () {clearForRecycle (); synchronized (sPoolSync) {if (sPoolSize <MAX_POOL_SIZE) {next = sPool; sPool = this; sPoolSize ++ ;}}} // clear all data void clearForRecycle () {flags = 0; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; when = 0; target = null; callback = null; data = null;} // copy the Message content public void copyFrom (Message o) {this. flags = o. flags &~ FLAGS_TO_CLEAR_ON_COPY_FROM; this. what = o. what; this. arg1 = o. arg1; this. arg2 = o. arg2; this. obj = o. obj; this. replyTo = o. replyTo; if (o. data! = Null) {this. data = (Bundle) o. data. clone () ;}else {this. data = null ;}}

The get and set methods and Parcelable reading and writing are followed.

4.2 Logoff

Let's first look at the attributes defined by him:

Public class logoff {private static final String TAG = logoff; static final ThreadLocal
  
   
SThreadLocal = new ThreadLocal
   
    
(); Private static Looper smainloue; // The only looper static instance final MessageQueue mQueue that corresponds to a main Thread; // Message Queue final Thread mThread; // The current bound Thread volatile boolean mRun; // whether to allow exit of private Printer mLogging; // log printing //.... various methods ....}
   
  

Generally, the operating system provides internal storage space for threads. A thread corresponds to a memory space. Therefore, it is convenient to define a unique looper instance for a thread: threadLocal <loloc> This is a bit similar to applying for memory size * malloc (sizeof loloc) in C, or we can simply understand that it only applies toNewLogoff.
The sMainLooper is the main thread looper of the current application. The difference is that it is applicable to the main thread.
Let's look at his constructor:

    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mRun = true;        mThread = Thread.currentThread();    }

This constructor is private, that is, it cannot be instantiated externally. In this way, the singleton mode ensures that the logoff is unique from the external thread. In addition, it initializes mQueue, mRun, and mThread attributes and binds the current thread. Find out what it calls instantiation:

// Reload. For more information, see the following prepare (boolean quitAllowed) method public static void prepare () {prepare (true );} /*** initialize the current thread as a Looper and coexist as a local variable *, and then create the handler and handler ** @ quitAllowed to determine whether to allow exit (cyclically retrieve messages) * Call loop () and quit () to enable and end the loop */private static void prepare (boolean quitAllowed) {if (sThreadLocal. get ()! = Null) {// ensure that a thread corresponds to a unique Looper throw new RuntimeException (Only one Looper may be created per thread);} sThreadLocal. set (new LOD (quitAllowed); // Initialize an instance in the thread}

The get and set of sThreadLocal, which is responsible for accessing the unique logoff corresponding to the thread in the memory.
At the same time, we will notice that there are two methods: prepareMainLooper and getMainLooper:

/*** Initialize the message retrieval logic of the current thread as the logoff and as the android app. * It is created in the current runtime environment and does not need to be manually called */public static void preparemainlogoff () {prepare (false); synchronized (Looper. class) {// lock, ensure that the instance has only one looper if (smainloif! = Null) {throw new IllegalStateException (The main Looper has already been prepared .);} sMainLooper = myLooper () ;}}/*** return the looper instance of the main thread of the current application */public static Looper getMainLooper () {synchronized (loz. class) {return sMainLooper ;}}

This part is called when the program is initialized. we generally do not use it, but we can also see that it only initializes the Logoff of the main thread.
Okay, the basic initialization has been completed. Let's take a look at the core part of this class and cyclically retrieve the message loop ()

/*** Start the cyclic message fetch operation of the queue until quit () is called to exit */public static void loop () {final Looper me = myloid (); if (me = null) {throw new RuntimeException (No Looper; low. prepare () wasn' t called on this thread .);} final MessageQueue queue = me. mQueue; // ensure that the current thread is the unique identifier of the local process. clearCallingIdentity (); final long ident = Binder. clearCallingIdentity (); // start to cyclically retrieve the Message operation for (;) {Message msg = queue. next (); // obtain the next message if (Msg = null) {// if the message queue does not have a message, the return is suspended;} // print the log section Printer logging = me. mLogging; if (logging! = Null) {logging. println (>>>>> Dispatching to + msg.tar get ++ msg. callback +: + msg. what);} // call the message processing logic (callback and handler processing) msg.tar get. dispatchMessage (msg); if (logging! = Null) {logging. println (<Finished to + msg.tar get ++ msg. callback);} // make sure that the current thread is not interrupted when processing the message logic. 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.tar get. getClass (). getName () ++ msg. callback + what = + msg. what);} // reclaim the message msg. recycle ();}}

There are basic notes, so you don't need to explain them too much. Msg.tar get. dispatchMessage has been mentioned earlier, and may be pulled out and walked out later.
The other is the basic get and print related to exception capture. If you are interested, you can take a look.

4.3 MessageQueue 4.4 Handler

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.