Это Runnable безопасно от утечки памяти?

Я абсолютный новичок в Java и создал простой фрагмент Java для Android, где в Runnable через 1,5 секунды я изменяю TextView от Hello World в Hola Mundo, Работает без нареканий, в основном WeakReference должно предотвратить эту утечку памяти, правильно? У меня есть сомнения, если нет абсолютно никакой утечки памяти, когда происходит ориентация устройства. Я хотел бы проверить это, но не могу изменить ориентацию в моем эмулируемом Android.

Это код:

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

}

РЕДАКТИРОВАТЬ

Это безопасно от утечек памяти, но несколько ответов были также связаны с блокировкой потока пользовательского интерфейса. На самом деле этот код запускает обработчик в основном потоке (UI). Чтобы создать новый поток, я создаю поток вручную следующим образом:

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

}

Проблема сейчас в том, что я не могу задержать порожденную нить, иначе она работает.

Это:

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

заставляет приложение выйти само, а я не понимаю почему. Что-то говорит мне, что я задерживаю это неправильно.

EDIT2

Благодаря ссылке @EugenMatynov дайте мне: обновить пользовательский интерфейс из другого потока в Android Я понял, почему приложение вышло. Все сводится к тому, что нельзя вызывать методы пользовательского интерфейса из потоков, отличных от основного потока. и это плохая практика, чтобы обновить пользовательский интерфейс из другого потока.

4 ответа

Решение

Я думаю, что ваш код без утечек, если вы используете:

private static Handler h = new Handler(); 

или же

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

потому что вы сохранили представление как WeakReference. метод:

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

просто вызовите основной обработчик потока пользовательского интерфейса, чтобы, если действие было уничтожено, представление было нулевым, а выполняемый - ничей.

также из-за слабой ссылки эта деятельность может быть подвергнута сборке мусора, поскольку на нее нет строгой ссылки.

У меня есть сомнения, если нет абсолютно никакой утечки памяти, когда происходит ориентация устройства.

Возможно. 1,5 секунды. После освобождения очереди обработчик может собирать мусор, а также старый объект Activity. Для безопасности переопределите onPause и позвоните handler.removeCallbacks(null); очистить очередь обработчика

h.postDelayed(новый WeakRunnable(txtview),1500); Я думаю, что это будет блокировать поток пользовательского интерфейса. Вот хороший пример утечки памяти. https://github.com/badoo/android-weak-handler

Пожалуйста, сделайте это, иначе вы будете блокировать UIThread, и это не рекомендуется. Для этого вы также можете использовать TimerTask, проверьте его здесь: 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);
    }          

}

Кстати, вы можете изменить ориентацию в эмуляторе следующим образом: Ctrl+F12

Другие вопросы по тегам