当前位置:网站首页>Handler、Message、Looper、MessageQueue

Handler、Message、Looper、MessageQueue

2022-06-25 11:15:00 User 9854323

List of articles

One 、 What is? handler

Two 、handler Message mechanism principle

handler Message mechanism principle : The essence is to implement a message queue between threads (MessageQueue). producer Handler When an asynchronous thread passes sendMessageDelayed() Add messages to MessageQueue, consumer Looper adopt loop() The middle loop will MessageQueue Medium msg Take it out and send it to generate this msg Of handler Of handleMessage() Processing in the main thread ;

3、 ... and 、Handler Message mechanism component

Handler

public Handler(Callback callback, boolean async) {

       //mLooper There is only one per thread Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

sendMessageDelayed take msg Send to message queue

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }


public boolean sendMessageAtTime(Message 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);
    }
   
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

looper take msg Distribute here , Then the thread that accepts the message processes msg

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //  Accept msg The thread that processes the message 
            handleMessage(msg);
        }
    }

Message

public final class Message implements Parcelable {
  
  /**
     * User defined message code , So that the recipient can recognize the content of this message . Every {@link Handler} Each has its own message code name 
     * Space , So you don't have to worry about conflicts with other handlers .
     */
    public int what;

    /**
     *  Optional Messenger, Replies to this message can be sent . Exactly how to use it * Semantics depend on the sender and receiver .
     */
    public Messenger replyTo;

    /**
     *  Optional fields , Indicates the... That sent the message uid. This is only for {@link Messenger} The published message is valid ; otherwise , It will be -1.
     */
    public int sendingUid = -1;
    
    /*package*/ int flags;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    //  We store the linked list of these things 
    /*package*/ Message next;

Looper

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
            }
            try {
            
                //lopper Here according to msg.target(handler) distribution msg
                
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

MessageQueue

// At the bottom is the form of linked list msg
Message next() {
       
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        for (;;) {
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
    }

3、 ... and 、Handler The three ways of using

Known from the following code ,handler Yes 3 Method of use :

Handler in looper Finally, put msg Send to this method :

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
           //1、 Threads run regularly 
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //2、mCallback The callback method 
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //3、handler Methods 
            handleMessage(msg);
        }
    }

private static void handleCallback(Message message) {
        message.callback.run();
    }

1)、 Run a thread regularly :public final boolean post(Runnable r) 2)、1) When not satisfied , Then when callBack Isn't empty , Callbacks callBack Of handleMessage() Method :

public Handler(Callback callback) {
        this(callback, false);
    }

public interface Callback {
        public boolean handleMessage(Message msg);
    }

3)、 above 1)、2) When they are not satisfied , call handler Of handleMessage() Method ;

1、 Run a thread regularly

example 1:

private Runnable mNextRunnable = new Runnable() {
		@Override
		public void run() {
			mHandler.removeCallbacks(mNextRunnable);
			if(isCycle()){
				setCurrentItem(getCurrentItem() + 1, true);
				mHandler.postDelayed(mNextRunnable, mDelayTime);
			}
		}
	};

example 2:

new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Activity activity = (Activity)context;
                            if (activity != null && !activity.isFinishing()) {
                                ToastUtil.makeToast(context,“ OK ”, Toast.LENGTH_SHORT).show();
                            }
                        }
                    }, 1000);

Known from the following code : When the time comes msg Add to the queue

MessageQueue in :
 boolean enqueueMessage(Message msg, long when) {       
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
           // When the time comes msg Add to the queue 
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
            }

2、1 When not satisfied , Then when callBack Isn't empty , Callbacks callBack Of handleMessage() Method

3、 above 1、2 When they are not satisfied , call handler Of handleMessage() Method ;

Four 、Handler Memory leak problem

reason :

1、 When using inner classes ( Including anonymous classes ) To create Handler When ,Handler Objects implicitly hold an external class object ( It's usually a Activity) References to ( How else can you get through Handler To operate Activity Medium View?). and Handler Usually with a time-consuming background thread ( For example, pulling pictures from the Internet ) Come together , This background thread finishes executing the task ( For example, after downloading the pictures ) after , Notify... By message mechanism Handler, then Handler Update the image to the interface . However , If the user turns off during a network request Activity, Under normal circumstances ,Activity No longer used , It could be in GC Recycled during inspection , But because the thread is not finished , And the thread holds Handler References to ( How else would it send a message to Handler?), This Handler And hold Activity References to , That leads to the Activity Cannot be recycled ( Memory leaks ), Until the end of the network request ( For example, after downloading the pictures ). 2、 in addition , If you execute Handler Of postDelayed() Method , This method will turn your Handler Load one Message, And put this Message push to MessageQueue in , So in what you set delay Before arriving , There will be one MessageQueue -> Message -> Handler -> Activity Chain , What caused you Activity Is held for reference and cannot be recycled .

give an example :

public class LeakCanaryActivity extends AppCompatActivity

    private  Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

            }
        };

        Message message = Message.obtain();
        message.what = 1;
        mHandler.sendMessageDelayed(message,10*60*1000);
    }

}

terms of settlement

Method 1 : Program logic code .
1. Closing Activity Stop your background thread .

Thread stopped , It's like cutting off Handler A line connected to the outside ,Activity It will be recycled at the right time .

 private void recycle() {
        mExecutor.shutdownNow();
    }
2. The message object is removed from the message queue

If your Handler Be being delay Of Message Holding quotes , Then use the corresponding Handler Of removeCallbacks() Method , Just remove the message object from the message queue .

/**
     * Delete any pending in the message queue Runnable
     */
    public final void removeCallbacks(Runnable r)
    {
        mQueue.removeMessages(this, r, null);
    }

example :

    private void recycle() {
        if (mHandler != null) {
            mHandler.removeCallbacks(null);
        }
    }
Method 2 : take Handler Declared as a static class ,activity Use weak references to assign values to

Static classes do not hold objects of external classes , So your Activity Can be recycled at will . The code is as follows :

static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        mImageView.setImageBitmap(mBitmap);
    }
}

But it's not that simple . After using the above code , You'll find that , because Handler No longer holds references to external class objects , Cause the program doesn't allow you to Handler In the operation Activity The object of . So you need to be in Handler Add a pair to Activity The weak references (WeakReference):

static class MyHandler extends Handler {
    WeakReference<Activity > mActivityReference;

    MyHandler(Activity activity) {
        mActivityReference= new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}
原网站

版权声明
本文为[User 9854323]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/176/202206251056178967.html