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;
}
}
}
}