What is Handler?

Handler is mainly used for asynchronous message processing. When a message is sent, it first enters a message queue, and the function that sends the message immediately returns. Another part removes the message one by one in the message queue, and then processes the message.

Handler memory leak

Code examples:

new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                SystemClock.sleep(3000);
                message.what = 3;
                handler1.sendMessage(message);
            }
        }).start();
Copy the code
private Handler handler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            startActivity(new Intent(TestActivity.this,ScrollingActivity.class));
            return false; }});Copy the code

As you can see from the code, the application is hibernating for 3 seconds, and the page is closed. When the 3 seconds are over, the application is automatically redirected to the ScrollingActivity page, indicating a memory leak.

Error model

  @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("lyb======"."onDestroy");
        handler1.removeMessages(3);
    }
Copy the code

If you close the page within 3 seconds and the message is not in the message queue, remove is null in onDestroy and does not work.

Systemclock. sleep(3000); In another API, handler1. SendMessageDelayed (message, 3000); This will push the message into the queue, but wake it up late so handler1.removemessages (3) is called in onDestroy(); If you want to use the first method, set handler1 null in onDestroy() and check if handler1 is null in sendMessage

Why can’t you create a Handler in a child thread

Looper.preparemainlooper () is called in this class because the application calls activityThread at startup. Call prepare(false); It’s a new looper, and that looper is the main thread’s looper, and that looper is in the threadLocalMap, so when you create a new handler in a child thread, you’re going to fetch the looper from the threadLocalMap, The looper will definitely not be found, so an exception will be thrown

Can TextView setText only be assigned to the main thread

Check for checkForRelayout(), and requestLayout() will be executed if else; invalidate(); We know that TextView inherits from View, so let’s look at requestLayout(); There’s a ViewRootImpl which is an interface that implements ViewParent, and this interface has requestLayout(); The implementation of this method calls the checkThread checkThread() and finds that

if(mThread ! = Thread.currentThread()) {throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
Copy the code
conclusion

If the setText is executed at the speed of invalidate(); Faster than checking thread requestLayout(); Does not throw an exception, and does otherwise

There’s a difference between new Handler and new Handler

private Handler handler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            startActivity(new Intent(TestActivity.this,ScrollingActivity.class));
            return false; }});Copy the code
private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg); }};Copy the code

According to the above two methods, Google recommends the first method

Let’s look at the source code and see how the handler retrieves the message. We know that a looper is automatically created in the main thread after the project starts, calling looper.prepareMainLooper (); ActivityThread creates an infinite loop that does not throw an exception because the jNI method handles the looper. Then we can see directly in the message queue to get the message messageQueue, ultimately calls the MSG. Target. DispatchMessage (MSG) method, we can see the change method

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

From this code we can see the difference at a glance.

ThreadLocal usage and principles

The looper is fetched using threadLocal. ThreadLocal is a T generic class whose key is Thread and value is T, or looper in current handler terms. Get the looper source code below

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue();
    }
Copy the code

If the map is not null, fetch the vlaue value from the map. In this code, value is looper. If null, setInitialValue() is called. Method to assign or create

   private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
Copy the code

Focus on

Looper can only be initialized once in the main thread. New looper () will cause an error

    private static void prepare(boolean quitAllowed) {
        if(sThreadLocal.get() ! =null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
Copy the code

An exception is raised directly because sThreadLocal is globally unique and static

Thank you

Thank you for reading and clicking “like” to keep me updated

Github