The Android messaging mechanism and handler memory leaks _android

Source: Internet
Author: User
Tags getmessage message queue prev sleep static class

Handler

Every beginner Android development is not open handler this "ridge", why it is a ridge, first of all, this is one of the essence of Android architecture, and then most people are aware of it but do not know why. Today, see Handler.post This method decided to go over the source code to comb the implementation of the handler mechanism.

Update UI Asynchronously

First of all, a must-back formula "main thread does not do time-consuming operations, child threads do not update the UI", this rule should be beginner must know, how to solve the problem in the formula, this time handler appeared in front of us (Asynctask also do, In essence, however, the encapsulation of handler is a classic code (ignoring memory leaks here, we'll talk about it later):

First, create a new handler in the activity:

Private Handler Mhandler = new Handler () {
    @Override public
    void Handlemessage (msg) {
      Super.handlemessage (msg);
      Switch (msg.what) {case
        0:
          mtesttv.settext (' This is Handlemessage ');//update UI break
          ;
      }}
  ;

Then send a message in the child thread:

New Thread (new Runnable () {
      @Override public
      void Run () {
        try {
          thread.sleep (1000);//A time-consuming operation on a child thread. such as Request network
          mhandler.sendemptymessage (0);
        } catch (Interruptedexception e) {
          e.printstacktrace ()
      }}
    }). Start ();

Now that you have completed the asynchronous update of the UI on the main thread after the child thread's time-consuming operation is complete, but there is no post for the title, let's look at the post version:

New Thread (new Runnable () {
      @Override public
      void Run () {
        try {
          thread.sleep (1000);//A time-consuming operation on a child thread. such as Request network
          Handler Handler = new Handler ();
          Handler.post (New Runnable () {
            @Override public
            void Run () {
              Mtesttv.settext (' This is post ');//Update UI
            }
          });
        } catch (Interruptedexception e) {
          e.printstacktrace ();}}}
    ). Start ();

On the surface, the post method is passed a runnable, such as the opening of a child thread, but in the Chengri can not update the UI Ah, then the problem, this is how the situation? With this doubt, to turn over the handler source:

Let's look at what ordinary sendemptymessage look like:

Public final Boolean sendemptymessage (int what)
  {return
    sendemptymessagedelayed (what, 0);
  }
Public final Boolean sendemptymessagedelayed (int what, long Delaymillis) {
    msg = Message.obtain ();
    Msg.what = what;
    Return sendmessagedelayed (msg, delaymillis);
  

Encapsulates the parameters we passed in as a message and then calls Sendmessagedelayed:

Public Final Boolean sendmessagedelayed (Message msg, long Delaymillis)
  {
    if (Delaymillis < 0) {
      Delaymillis = 0;
    }
    Return Sendmessageattime (MSG, systemclock.uptimemillis () + delaymillis);
  

Call Sendmessageattime again:

public boolean sendmessageattime (msg, long Uptimemillis) {
    MessageQueue queue = mqueue;
    if (queue = = null) {
      runtimeexception e = new RuntimeException (This
          + sendmessageattime () called with no Mqueue ");
      LOG.W ("Looper", E.getmessage (), e);
      return false;
    }
    return Enqueuemessage (Queue, MSG, uptimemillis);
  

Okay, let's take a look at post ():

Public Final Boolean post (Runnable R)
  {return
    sendmessagedelayed (Getpostmessage (R), 0); The Getpostmessage method is the difference between two sending messages
  }

The method is only one sentence, the internal implementation and the ordinary SendMessage is the same, but only a little different, that is Getpostmessage (R) This method:

private static Message Getpostmessage (Runnable r) {message
    m = Message.obtain ();
    M.callback = R;
    return m;
  }

This method we found is also to encapsulate our incoming parameters into a message, only this time is M.callback = R, just msg.what=what, as for the messages of these attributes will not read

Android messaging mechanism

See here, we just know that post and SendMessage principles are encapsulated into message, but still do not know what the whole mechanism of handler is like, continue to explore.

Just saw those two methods to finally call the Sendmessageattime

public boolean sendmessageattime (msg, long Uptimemillis) {
    MessageQueue queue = mqueue;
    if (queue = = null) {
      runtimeexception e = new RuntimeException (This
          + sendmessageattime () called with no Mqueue ");
      LOG.W ("Looper", E.getmessage (), e);
      return false;
    }
    return Enqueuemessage (Queue, MSG, uptimemillis);
  

This method also calls the Enqueuemessage, see the name should be the message to join the queue meaning, click to see:

Private Boolean enqueuemessage (MessageQueue queue, MSG, long Uptimemillis) {
    msg.target = this;
    if (masynchronous) {
      msg.setasynchronous (true);
    }
    Return Queue.enqueuemessage (msg, uptimemillis);
  

Masynchronous this asynchronous-related first regardless, continue to pass the parameter to the queue's Enqueuemessage method, as for that MSG target assignment we'll look at the back, Now continue to enter the Enqueuemessage method of the MessageQueue class, with a longer approach, let's look at the key lines:

Message prev;
for (;;) {
  prev = p;
  p = p.next;
  if (p = = NULL | | When < p.when) {break
    ;
  }
  if (Needwake && p.isasynchronous ()) {
    needwake = false;
  }
}
Msg.next = p; Invariant:p = = Prev.next
prev.next = msg;

Sure enough, as the method name says, an infinite loop adds a message to the message queue (in the form of a linked list), but there's a way to get it out.

Looking at the MessageQueue method, we found next (), the code is too long, do not repeat, we know it is used to take the message out of the line. But where did this approach come from, not in handler, where we found the key figure of Looper, I called him a ring messenger, dedicated to getting messages from the message queue, the key code is as follows:

for (;;) {
   msg = Queue.next ();//might block
   ...
   Msg.target.dispatchMessage (msg);
   ...
   Msg.recycleunchecked ();
}

Simple and clear, we see the msg.target we just said, just handler in the msg.target=this, so we look at the DispatchMessage in handler:

public void DispatchMessage (message msg) {
    if (msg.callback!= null) {
      handlecallback (msg);
    } else {
      if (mcallback!= null) {
        if (Mcallback.handlemessage (msg)) {return
          ;
        }
      }
      Handlemessage (msg);
    }
  

1.MSG callback is not NULL, call Handlecallback method (Message.callback.run ())
2.mCallback is not empty, call Mcallback.handlemessage (msg)
3. Finally, if the other is null, execute handler's own Handlemessage (msg) method
The callback of MSG should have thought of what it is that we passed through Handler.post (Runnable R) to the Runnable run method, where we'll mention Java basics, The run method of calling a thread directly is equivalent to calling a method in a normal class, or executing it on the current thread, without opening a new thread.

So here we have solved the beginning of the puzzle, why we passed a runnable in post or the UI can be updated in the main thread.

Continue to see if the msg.callback is empty in the case of mcallback, this should look at the construction method:

1. Public Handler () {This (null, false);  
} 2.
  Public Handler (Callback Callback) {This (Callback, false);
} 3.
  Public Handler (Looper looper) {This (Looper, NULL, FALSE);
} 4.
  Public Handler (Looper looper, Callback Callback) {This (Looper, Callback, false);
} 5.
  Public Handler (Boolean async) {This (null, async);
} 6. Public Handler (Callback Callback, Boolean async) {if (Find_potential_leaks) {final class<? extends handler& Gt
      Klass = GetClass ();  if ((Klass.isanonymousclass () | | klass.ismemberclass () | | klass.islocalclass ()) && (Klass.getmodifiers () 
          & modifier.static) = = 0) {LOG.W (TAG, "the following Handler class should be STATIC or leaks might:" +
      Klass.getcanonicalname ());
    } Mlooper = Looper.mylooper (); if (Mlooper = = null) {throw new RuntimeException ("Can" t create handler inside thread that is has not called L
    Ooper.prepare () "); } Mqueue = MlooPer.mqueue;
    Mcallback = callback;
  masynchronous = async;
} 7.
    Public Handler (Looper looper, Callback Callback, Boolean async) {mlooper = Looper;
    Mqueue = Looper.mqueue;
    Mcallback = callback;
  masynchronous = async;

 }

The specific implementation is only the last two, already know how mcallback is coming, in the construction method passed in the line.

Finally, if both callbacks are empty, execute handler's own Handlemessage (msg) method, the Handlemessage method of the new handler rewrite that we are familiar with.

Looper

See here there is a doubt, that is, when we created the new handler did not pass any parameters, and there is no where to show the call to Looper about the method, that Looper creation and method call where? In fact, these things Android itself has done for us, in the program entrance Activitythread Main method inside we can find:

 public static void Main (string[] args) {
  ...
  Looper.preparemainlooper ();
  ...
  Looper.loop ();
  ...

Summarize

I've probably combed through the handler message mechanism, and the difference between the Post method and our usual SendMessage method. To sum up, mainly involves four classes of handler, message, MessageQueue, Looper:

Create a new handler, send a message via SendMessage or post, handler call Sendmessageattime and give it to MessageQueue

The Messagequeue.enqueuemessage method puts the message in the queue as a linked list

The loop method of Looper calls Messagequeue.next () to fetch the message, and calls Handler's dispatchmessage to process the message

In DispatchMessage, the Msg.callback, Mcallback, or the Post method, or the constructed method, are used to perform their callbacks, and if they are null, they perform the handlemessage of our most common rewrite.

Finally talk about the memory leak problem of handler
And take a look at our new handler code:

Private Handler Mhandler = new Handler () {
    @Override public
    void Handlemessage (msg) {
      ...
    }
  };

When an internal class (including an anonymous class) is used to create a handler, the handler object implicitly holds the reference to the activity.

Handler is usually accompanied by a time-consuming background thread that sends messages to update the UI after the task has finished executing. However, if a user shuts down the activity during a network request, and normally the activity is no longer used, it is likely to be reclaimed at the GC check, but the thread holds a handler reference because the thread has not finished executing, and how else does it send a message to handler? , this handler also holds a reference to the activity, which causes the activity not to be reclaimed (that is, a memory leak) until the network request ends.

In addition, if the postdelayed () method of the Handler is executed, there will be a chain of MessageQueue-> message-> and Handler activity before the set delay arrives. Causes your activity to be held in a reference and cannot be recycled.

One workaround, using weak references:

Static class MyHandler extends Handler {
  weakreference<activity > mactivityreference;
  MyHandler (activity activity) {
    mactivityreference= new weakreference<activity> (activity);
  }
  @Override public
  void Handlemessage (msg) {final activity activity
    = Mactivityreference.get ();
    if (activity!= null) {
      mimageview.setimagebitmap (mbitmap);
    }
  }
}

The above is to the Android handler message mechanism of data collation, follow-up continue to supplement the relevant information, thank you for your support of this site!

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.