A Brief Analysis of Handler Use in Android

Keywords: Android Java xml encoding

1. Handler usage elicitation

Now as a customer, there is a need to start counting down when opening the Activity interface and jump to a new interface when the countdown ends (a thoughtful friend may immediately think that if the countdown is opened automatically, it is similar to the welcome flash page of each APP). Here is the following picture:

As a beginner, you may feel that it is ok ay to open a sub-thread containing an inverted loop directly. The specific implementation is as follows:

1.1 The Layout interface code is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.mly.panhouye.handlerdemo.Main2Activity">
    <TextView
        android:gravity="center"
        android:textSize="30sp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="NO DATA"
        android:id="@+id/tv"/>
</LinearLayout>

1.2 The Java implementation code is as follows:

public class Main2Activity extends AppCompatActivity {
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tv = (TextView) findViewById(R.id.tv);
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=5;i>0;i--){
                    tv.setText(String.valueOf(i));
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //Jump to other interfaces after the timing is over
                startActivity(new Intent(Main2Activity.this,Main3Activity.class));
                //Add to finish Method Destroy the countdown interface in the task stack so that the new interface exits directly when it falls back instead of returning to the interface again.
                finish();
            }
        }).start();
    }

The logic is simple, but when you point to the interface, you will find that the program runs out. The error log in logcat is as follows (only UI threads can change the UI interface):

Thus we find that in Android development, for example, in the example above, we often complete some operations through a thread, and then synchronously display the corresponding view control UI. Through the example above, we also know that Android can not update UI directly through sub-threads. In this case, Android provides an asynchronous message processing mechanism called Handler.

2. Handler implementation method

Using handler implementation, modify the java code Main2Activity.java as follows:

package com.mly.panhouye.handlerdemo;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
/**
 * Handler:
 * 1 The message object processed is Message, which is understood as the encapsulation object of the message data to be delivered.
 * Message what : Markup, used to distinguish multiple messages
 * Message arg1,arg2 : Used to transfer data of type int
 * Message obj : Objects of any type can be passed
 */
public class Main2Activity extends AppCompatActivity {
    public static final int UPDATE = 0x1;
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tv = (TextView) findViewById(R.id.tv);
        begin();//Method of opening countdown and jumping pages
    }
    //Message Processor,Create a Handler Subclass object,The purpose is to rewrite Handler Method of handling messages(handleMessage())
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE:
                    tv.setText(String.valueOf(msg.arg1));
                    break;
            }
        }
    };

    public void begin(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=5;i>0;i--){
                    Message msg = new Message();
                    msg.what = UPDATE;
                    msg.arg1 = i;
                    handler.sendMessage(msg);
                    try {
                        Thread.sleep(1000);//Dormancy for one second
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //Printing log
                    Log.i("tag",Main2Activity.this+"-"+ i);
                }
                //Jump to other interfaces after the timing is over
                startActivity(new Intent(Main2Activity.this,Main3Activity.class));
                //Add to finish Method Destroy the countdown interface in the task stack so that the new interface exits directly when it falls back instead of returning to the interface again.
                finish();
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //log Printing for testing activity Destruction
        Log.i("tag","destory");
    }
}

3. Handler Implementation Principle

Asynchronous message processing using Handler is mainly composed of four parts: Message, Handler, MessageQueue and Looper.

(1) Message, the message passed between threads, is used for data interaction between different threads. what field in Message is used to mark and distinguish multiple messages, arg1 and arg2 fields are used to transfer data of type int, and obj can transfer any type of field.

(2) Handler for sending and processing messages. sendMessage() is used to send messages, handleMessage() is used to process messages and perform corresponding UI operations.

(3) MessageQueue, message queue (FIFO), is used to store messages sent by Handler. A thread has only one message queue.

(4) Looper, which can be understood as the manager of a message queue, when a message is found in the MessageQueue, Looper will pass the message to the handleMessage() method. Similarly, a thread has only one Looper.

The principle of Handler implementation is as follows:

Combining the code example above and the implementation process shown above, to implement asynchronous message processing using Handler, we first need to create Handler objects in the main thread and overwrite handleMessage() methods. Then when UI operations are required in the sub-thread, we create a Message object and send this message through Handler. This message is then added to the MessageQueue queue for processing, while Looper tries to extract the message from the MessageQueue and distribute it back to the handleMessage() method of Handler. Since Halldler is created in the main thread, the code in the handleMessage() method will also run in the main thread at this time, so that the sub-thread can realize the purpose of UI thread operation through the handler mechanism.

4. Handler Memory Leakage Analysis

4.1 Handler Memory Leakage Problem:

In the Hadler implementation code above, the Android The following questions will be raised in Studio:

The general idea is that the Handler class should be static, otherwise memory leaks will occur. The reason is also clear. Handler's declaration as a non-static internal class or an anonymous class may prevent garbage collection of external classes (you can learn about Android's gc recycling mechanism). Excessive memory leaks cause the program to occupy more memory than the system limits, resulting in OOM (memory overflow) and program errors.

4.2 Prevent Handler from causing memory leaks:

Method 1: Protect by program logic:

(1) Stop the corresponding background thread when closing Activity. Thread stops are equivalent to disconnecting handles and external links. Activities are naturally recycled at the right time.

(2) If the Handler is referenced by delay's Message, remove the message object from the message queue using Handler's removeCallbacks() method.

Method 2: Handler is declared as a static class, which does not hold objects of external classes, so Activity can be reclaimed at will. WeakReference is used here, which means that when there is insufficient memory, the system destroys or recycles the referenced objects to optimize memory. The optimized code is as follows:

package com.mly.panhouye.handlerdemo;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import java.lang.ref.WeakReference;

public class Main4Activity extends AppCompatActivity {
    public static final int UPDATE = 0x1;
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tv = (TextView) findViewById(R.id.tv);
        begin();//Method of opening countdown and jumping pages
    }
    //Handler Static inner class
    private static class MyHandler extends Handler {
        //Weak reference
        WeakReference<Main4Activity> weakReference;
        public MyHandler(Main4Activity activity) {
            weakReference = new WeakReference<Main4Activity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            Main4Activity activity = weakReference.get();
            if (activity != null) {
                activity.tv.setText(String.valueOf(msg.arg1));
            }
        }
    }
    private MyHandler handler = new MyHandler(this);
    public void begin() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                    for (int i = 5; i > 0; i--) {
                        Message msg = new Message();
                        msg.what = UPDATE;
                        msg.arg1 = i;
                        handler.sendMessage(msg);
                        try {
                            Thread.sleep(1000);//Dormancy for one second
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Log.i("tag", Main4Activity.this + "-" + i);
                    }
                    //Jump to other interfaces after the timing is over
                    startActivity(new Intent(Main4Activity.this, Main3Activity.class));
                    //Add to finish Method Destroy the countdown interface in the task stack so that the new interface exits directly when it falls back instead of returning to the interface again.
                    finish();
                }
        }).start();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
        Log.i("tag", "destory");
    }
}

5. summary

This time, we use handler to realize the effect of page Jump countdown. We just introduce the use of handler and the matters needing attention, but there are still bug s. If we quit the activity when the countdown is not completed, the sub-threads will still run in the background until the jump is completed. The effect and log s are as follows:

In view of this situation, I have a lot of troubles to deal with. I need to destroy the countdown activity and terminate the thread at the same time. I tried many methods, but failed to achieve them.

In fact, to achieve the countdown flash effect, you can use Android has a countDownTimer class to achieve, the following will do a simple implementation.

Posted by naveenbj on Wed, 10 Apr 2019 01:09:32 -0700