Is this Runnable safe from memory leak?

2019-02-22 01:19发布

I am a total beginner in Java and have created a simple Java Android snippet where in a Runnable after 1,5 seconds I change the TextView from Hello World to Hola Mundo. It works flawlessly, basically a WeakReference should prevent this memory leak from happening right? I have a doubt if there's absolutely no memory leak whenever device orientation occurs. I would love to check this but can't manage to change orientation in my emulated Android.

This is the code:

package com.example.helloworld;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;
import android.util.Log;
import java.lang.ref.WeakReference;

public class HelloWorldActivity extends Activity
{
    private Handler h = new Handler();
    private static TextView txtview;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtview = (TextView) findViewById(R.id.mainview);

        h.postDelayed(new WeakRunnable(txtview),1500);
    }

    private static final class WeakRunnable implements Runnable {
        private final WeakReference<TextView> mtextview;

        protected WeakRunnable(TextView textview){
            mtextview = new WeakReference<TextView>(textview);
        }

            @Override
            public void run() {
                TextView textview = mtextview.get();
                if (textview != null) {
                    txtview.setText("Hola Mundo");
                    textview = null; // No idea if setting to null afterwards is a good idea
                }
                Log.d("com.example.helloworld", "" + textview);
            }
    }           

}

EDIT

It's safe from memory leaks but a few answers were also concerned with UI thread blocking. In fact this code runs the Handler in the main (UI) thread. To spawn a new thread I'm spawning a thread manually as follows:

package com.example.helloworld;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;
import android.util.Log;
import java.lang.ref.WeakReference;

public class HelloWorldActivity extends Activity
{

    private static TextView txtview;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtview = (TextView) findViewById(R.id.mainview);

        Thread t = new Thread(new WeakRunnable(txtview));
        t.start();
    }

    private static final class WeakRunnable implements Runnable {
        private final WeakReference<TextView> mtextview;

        protected WeakRunnable(TextView textview){
            mtextview = new WeakReference<TextView>(textview);
        }

            @Override
            public void run() {
                TextView textview = mtextview.get();
                if (textview != null) {
                    /*
                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    */
                    txtview.setText("Hola Mundo");
                    textview = null;
                }
                Log.d("com.example.helloworld", "" + Thread.currentThread().getName()); // Outputs "Thread-<num>" if not running on UI thread
            }
    }           

}

The issue now is that I can't seem to delay the spawned thread in any way, otherwise it works.

This:

try {
    Thread.sleep(1500);
} catch (InterruptedException e) {
    e.printStackTrace();
}

makes the app quit itself and I don't get why. Something tells me I'm delaying it the wrong way.

EDIT2

Thanks to the link @EugenMatynov give me: update ui from another thread in android I understood why the app quitted. It all comes down to the reason You can't call UI methods from threads other than the main thread. and it's bad practice to update the UI from another thread.

4条回答
姐就是有狂的资本
2楼-- · 2019-02-22 01:38

I think your code is leak free if you use :

private static Handler h = new Handler(); 

or

txtview.postDelayed(new WeakRunnable(txtview),1500);

because you have stored the view as a WeakReference. the method:

txtview.postDelayed(new WeakRunnable(txtview),1500);

simply call main handler of the UI thread so if the activity is destroyed the view is null and the runnable dose nothing.

also because of the weakreference the activity can be garbage collected because there is no strong reference to it.

查看更多
疯言疯语
3楼-- · 2019-02-22 01:41

I have a doubt if there's absolutely no memory leak whenever device orientation occurs.

It could be. For 1.5seconds. After the queue is emptied the handler can be garbage collected, and also the old Activity. To be safe override onPause, and call handler.removeCallbacks(null); to clear the Handler's queue

查看更多
smile是对你的礼貌
4楼-- · 2019-02-22 01:42

h.postDelayed(new WeakRunnable(txtview),1500); I think it will be blocking UI Thread. here is a good sample for memory leak. https://github.com/badoo/android-weak-handler

查看更多
▲ chillily
5楼-- · 2019-02-22 01:52

Please do this, otherwise you will be blocking UIThread and it is not recommended. To do this, you can also use a TimerTask, check it here: http://developer.android.com/reference/java/util/TimerTask.html

import android.widget.TextView;
import android.util.Log;
import java.lang.ref.WeakReference;

public class HelloWorldActivity extends Activity
{
    private Handler h = new Handler();
    private static TextView txtview;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtview = (TextView) findViewById(R.id.mainview);        

        h.postDelayed(new Runnable() {
           @Override
           public void run() {
              changeText();
           }
        }, 1500);
    }

    public void changeText(){
       txtview.setText("Hola mundo.");
       h.removeCallbacksAndMessages(null);
    }          

}

By the way, you can change orientation in your emulator this way: Ctrl+F12

查看更多
登录 后发表回答