简体   繁体   中英

How can I resolve warnings about memory leaks when using android Handler?

I'm a beginner studying android. I was using a Handler while learning Thread. By the way, I used Handler to warn memory leak in android studio. I searched a lot of different questions, but I do not have a section that corresponds to my case. How can I resolve the warning about memory leaks when using handlers?

public class HandlerActivity extends AppCompatActivity implements Runnable {

    ProgressBar pb;
    TextView txtRate;
    Button btnStart;
    static int value;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.progress);

        pb = findViewById(R.id.pb);
        txtRate = findViewById(R.id.txtRate);
        btnStart = findViewById(R.id.btnStart);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Thread th = new Thread(HandlerActivity.this);
                th.start();
            }
        });
    }

        Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            pb.setProgress(value);
            txtRate.setText("Process : " + value + "%");
        }
    };

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            value = i;

            handler.sendEmptyMessage(0);

            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(HandlerActivity.this, "Progress Done !", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

Basically TextView or any other View s hold a reference of a Context object representing the corresponding Activity . When you keep a strong reference of any View in a Thread you are not only storing the View object but also a Context object representing the Activity that created it. Now as Thread s are not tied with the Activity life cycle, they will continue to run even after the Activity is destroyed. If this is the case the Thread will hold a destroyed Activity 's reference through that View object thus create a memory leak.

The above mentioned problem can be solved by storing a weak reference of the View object so that GC can garbage collect it when necessary. In the following way you can get rid of memory leak:

public class HandlerActivity extends AppCompatActivity implements Runnable {
    WeakReference<ProgressBar> pb;
    WeakReference<TextView> txtRate;
    Button btnStart;
    static int value;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        pb = new WeakReference<>(findViewById(R.id.pb)); // you may require to cast findViewById() to ProgressBar
        txtRate = new WeakReference<>(findViewById(R.id.txtRate));  // you may require to cast findViewById() to TextView
        ...
    }

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(pb.get()!=null) pb.get().setProgress(value);
            if(txtRate.get()!=null) txtRate.get().setText("Process : " + value + "%");
        }
    };

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            // Its always recommended to check if activity is running and stop the thread if not running
            if(isFinishing() || isDestroyed()) {
                return;
            }
        }
    }
}

Roaim, Mark Keen With their help, I solved this and gave the completed answer myself. Thanks to Roaim, Mark Keen.

public class HandlerActivity extends AppCompatActivity implements Runnable {

ProgressBar pb;
TextView txtRate;
Button btnStart;
int value;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.progress);

    pb = findViewById(R.id.pb);
    txtRate = findViewById(R.id.txtRate);
    btnStart = findViewById(R.id.btnStart);

    btnStart.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Thread th = new Thread(HandlerActivity.this);
            th.start();
        }
    });
}

private static class WeakHandler extends Handler {
    private final WeakReference<HandlerActivity> mWeakActivity;

    private WeakHandler(HandlerActivity AppCompatActivity) {
        mWeakActivity = new WeakReference<>(AppCompatActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        HandlerActivity _activity = mWeakActivity.get();

        if (_activity != null) {

            switch (msg.what) {
                case 0:
                    _activity.pb.setProgress(_activity.value);
                    _activity.txtRate.setText("Process : " + _activity.value + "%");
                    break;
            }
        }
    }
}

private final WeakHandler mHandler = new WeakHandler(this);

@Override
public void run() {
    for (int i = 1; i <= 100; i++) {
        value = i;

        mHandler.sendEmptyMessage(0);

        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(HandlerActivity.this, "Progress Done !.", Toast.LENGTH_SHORT).show();
        }
    });
}

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM