2013-04-24 2 views
12

Я передаю обработчик, созданный по потоку mainUI от Activity, и передается потоку, который выполняет некоторую сетевую операцию, и когда я получаю результат, я возвращаю результат к активности с помощью обработчика ,Экземпляр действия все еще существует даже после того, как onDestroy() называется

Этот подход имел проблему утечки памяти, когда я прошел через эти ссылки:
Inner ClassHandler Memory Leak
Android Developers

Так я осуществил WeakReference и сохранил экземпляр активности с помощью WeakReference. Но я все еще вижу Activity экземпляр живой даже после уничтожения активности.

Я создал объект Handler внутри действия и передал экземпляр активности как слабое отношение к обработчику.
К моменту, когда мой Handler отвечает сообщением, переданным ему через 10 секунд, уничтожается Activity. Но слабая ссылка все еще имеет экземпляр Activity, и я вижу Toast, после того как Activity уничтожен.

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

import java.lang.ref.WeakReference; 

import android.os.Handler; 
import android.os.Message; 

public abstract class SingleParamHandler <T> extends Handler 
{ 
private WeakReference<T> mActivityReference; 

public SingleParamHandler(T activity) { 
    mActivityReference = new WeakReference<T>(activity); 
} 

@Override 
public void handleMessage(Message msg) { 
    if (mActivityReference.get() == null) { 
     return; 
    } 
    handleMessage(mActivityReference.get(), msg); 
} 

protected abstract void handleMessage(T activity, Message msg); 

} 

import android.app.Activity; 
import android.os.Bundle; 
import android.os.Message; 
import android.widget.Toast; 

public class MainActivity extends Activity { 

MyHandler<MainActivity> handler; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main1); 
    handler = new MyHandler<MainActivity>(this); 
    new Thread(new MyRunnable(handler)).start(); 
} 

public void onDestroy() { 
    super.onDestroy(); 
    System.out.println("######## Activity onDestroy() ###### "); 
} 

private class MyRunnable implements Runnable { 
    private Handler mHandler; 
    public MyRunnable(Handler handler) { 
     mHandler = handler; 
    } 

    public void run() { 
     try { 
      Thread.sleep(10000); 
      mHandler.sendMessage(Message.obtain(handler, 1)); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 


private static class MyHandler<T> extends SingleParamHandler<T> { 

    public MyHandler(T activity) { 
     super(activity); 
    } 

    @Override 
    public void handleMessage(T act, Message msg) { 
     if(msg.what == 1) { 
      Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();; 
     } 
    } 
} 

} 

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

Добавлена ​​ниже функции в SingleParamHandler

public void clear() { 
    mActivityReference.clear(); 
} 

И активность OnDestroy()

public void onDestroy() { 
    super.onDestroy(); 
    System.out.println("######## Activity onDestroy() ###### "); 
    handler.clear(); 
} 

ответ

5

Здесь не нужен WeakReference. Handler может просто содержать ссылку на Activity. В действии onDestroy() просто вызовите метод на MyHandler, который устанавливает ссылку на Activity на null. Проверьте на null в handleMessage().

Другим выбором будет следующее: в действии onDestroy() вызовите метод, который прерывает спящий поток, чтобы он отключился перед отправкой сообщения.

+0

Спасибо за логику. Могу ли я знать, что заставляет вас сказать, что слабое отношение не требуется, потому что ссылка, которую я разместил, говорит, что обработчик должен быть статичным и делать слабую ссылку на активность или услугу и проверять перед обработкой сообщения? разве это не противоречит? Ваши знания об этом будут оценены. !! – Mani

+0

@Mani Это предложение идет в том же направлении, что и мое (регистрация/незарегистрирование действий). Я делал меньше допущений. Дэвид, я все еще думаю в этом простом сценарии, было бы легче, если Handler проверит вместо 'Activity.isDestroyed()', потому что 'onDestroy()' не гарантируется выполнение? –

+1

В статье, в которой вы ссылались, содержится конкретная ситуация, и я не согласен со всем, что там написано. Рассматриваемый случай - это сообщение с задержкой, отправленное в «Обработчик». В статье описывается случай, когда сообщение отправляется в «Обработчик», который будет отправлен через 10 минут. Это сообщение имеет ссылку на «Handler», а «Handler» имеет ссылку на «Activity», и это означает, что «Handler» или «Activity» не смогут быть удалены сборщиком мусора **, в то время как это сообщение все еще находится в очереди **. –

3

Там нет никакой гарантии, что Android будет действительно удалить объект из памяти, если это не требуется, чтобы сделать это. Другими словами, объекты Activity могут оставаться в памяти даже после того, как был вызван onDestroy() (если имеется достаточно памяти). С другой стороны, нет никакой гарантии, что onDestroy()будет вызывать, если памяти недостаточно; Напротив, Android может убить весь ваш процесс после вызова onPause() на текущую активность (в зависимости от версии Android).

Я думаю, что есть лучший путь для достижения вашей цели. Что вы можете сделать, это приложить, отсоединить и, возможно, повторно подключить (например, при изменениях конфигурации). Действия вашей службы. Не надейтесь, что сборщик мусора сделает вам работу. Скорее, сделайте это явно.

Подкласс Activity и переопределить методы жизненного цикла, а также startActivity() и startActivityForResult(), чтобы ваша служба узнала, кто сейчас находится в ведении. Конечно, это только подход с наилучшими усилиями, поскольку некоторые обратные вызовы не гарантируются, но это имеет значение только в определенных ситуациях, которые не являются опасными. Например, ваша деятельность не будет отключена от вашего Сервиса в onPause(), но после этого ее можно будет убить. Но ваша служба работает в том же процессе, поэтому ее одновременно убивают.Или он работает в другом процессе, но тогда Android заметит сломанное соединение и может или не может убить службу; если нет, то все, что вам нужно сделать, - это реализовать его надежным способом, чтобы иметь возможность справиться с потерей соединения.

Update

После прочтения комментария: Вы правы, я не обращался, что конкретно.

я выяснить, как избежать сообщений посылается в обработчик, который создается в деятельности, которая разрушается

Учитывая ваш код выше, и при условии, что вы на самом деле просто хотите отобразить Toast сек с активностью до тех пор, пока она существует, должен помочь следующий подход.

  • Если ваш Thread должен обслуживать более одного действия, расширьте его так, чтобы Activities мог регистрироваться в потоке после его создания. Если ваш Thread просто служит одному Activity, перейдите по ссылке Activity вместе с ссылкой Handler на ваш Thread (Runnable).
  • Перед отправкой Thread отправляет сообщение через Handler, отметьте activity.isDestroyed(). Если действие не уничтожено, отправьте сообщение. Если действие уничтожено, не отправляйте сообщение.
  • В зависимости от того, какую тему следует сервер более чем один активность, либо выйти из его Runnable «s run() метод или установить его Activity ссылка на null, если он считает, что Activity был разрушен.

Это должно исправить ваш вышеуказанный код. Однако, если ваш сценарий растет, другие подходы могут быть более подходящими.

+1

Спасибо за разрешение. Я понимаю, что экземпляр Activity не будет удален becoz onDestroy(). Но я выясняю, как избежать отправки сообщений обработчику, который создается в результате действия, которое уничтожается ... !! Я знаю, что обработчик не привязан к Activity, это к основному потоку пользовательского интерфейса, но интересно, как с этим справиться? – Mani

+0

@Mani Обновлен мой ответ, надеюсь, это поможет! –

Смежные вопросы