Google Android - это несложно

Добро пожаловать на форум сайта startandroid.ru
Текущее время: 17 окт 2017, 06:49

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 19 ] 
Автор Сообщение
 Заголовок сообщения: Используем static Handler
СообщениеДобавлено: 18 апр 2013, 10:46 
Администратор
Аватар пользователя

Зарегистрирован: 07 янв 2012, 11:32
Сообщений: 1348
Благодарил (а): 0 раз.
Поблагодарили: 72 раз.
В уроках используется обычный Handler. Eclipse на это ругается: "This Handler class should be static or leaks might occur " ("Класс Handler должен быть static, иначе могут быть утечки памяти") и он абсолютно прав. Пофиксим это.

Приведу пример static Handler.

Код: [ Загрузить ] [ Скрыть ]
Using Java(TM) 2 Platform Standard Edition 5.0 Syntax Highlighting
  1. public class MainActivity extends Activity {
  2.  
  3.         Handler handler;
  4.         TextView tvTest;
  5.         int cnt = 0;
  6.  
  7.         @Override
  8.         protected void onCreate(Bundle savedInstanceState) {
  9.  
  10.                 super.onCreate(savedInstanceState);
  11.                 setContentView(R.layout.main);
  12.  
  13.                 tvTest = (TextView) findViewById(R.id.tvTest);
  14.                
  15.                 handler = new MyHandler(this);
  16.                 handler.sendEmptyMessageDelayed(0, 1000);
  17.         }
  18.  
  19.         void someMethod() {
  20.                 tvTest.setText("Count = " + cnt++);
  21.                 handler.sendEmptyMessageDelayed(0, 1000);
  22.         }
  23.  
  24.         @Override
  25.         protected void onDestroy() {
  26.                 if (handler != null)
  27.                         handler.removeCallbacksAndMessages(null);
  28.                 super.onDestroy();
  29.         }
  30.  
  31.         static class MyHandler extends Handler {
  32.  
  33.                 WeakReference<MainActivity> wrActivity;
  34.  
  35.                 public MyHandler(MainActivity activity) {
  36.                         wrActivity = new WeakReference<MainActivity>(activity);
  37.                 }
  38.  
  39.                 @Override
  40.                 public void handleMessage(Message msg) {
  41.                         super.handleMessage(msg);
  42.                         MainActivity activity = wrActivity.get();
  43.                         if (activity != null)
  44.                                 activity.someMethod();
  45.                 }
  46.         }
  47. }


Код в целом несложен: на экране просто идет счетчик, который работает через отложенные сообщения Handler.

В чем разница между обычным Handler и static? Чтобы понять это, необходимо знать две вещи:

1) Не static Handler содержит скрытую ссылку на Activity.
2) Сообщения предназначенные для Handler содержат ссылку на сам Handler (а следовательно и на Activity).

Два этих факта могут вызвать утечку памяти. Например, вы в системную очередь на выполнение помещаете (с помощью Handler) сообщение, которое должно выполниться через час. Затем закрываете приложение. Система хочет освободить память и поудалять из нее неиспользуемые объекты. И она удалила бы закрытое Activity и все его содержимое, но не может. Потому что сообщение, которое будет висеть еще час, хранит ссылку на Handler, а Handler хранит ссылку на Activity. В итоге одно сообщение (которое, скорее всего, уже никому не нужно) держит зря целое Activity и не дает системе удалить его из памяти.

Объявляя Handler как static, мы разрубаем цепь ссылок и Handler больше не хранит ссылку на Activity. Но! Handler обычно используется чтобы взаимодействовать с UI, и он должен уметь работать с Activity и его методами. А для этого он должен иметь ссылку на Activity.

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

Тут выручает Java-механизм слабых ссылок - WeakReference. Слабая ссылка не учитывается системой при очистке памяти. Если система видит, что Activity больше не нужно, но есть Handler, который хранит слабую ссылку на Activity, - слабая ссылка будет проигнорена, Activity будет удалено и утечек памяти не будет.

Мы в конструктор Handler-а передаем Activity и для хранения используем контейнер WeakReference. Метод get позволяет нам получить ссылку обратно из контейнера. А если WeakReference возвращает null, значит объект был удален из памяти, т.е. Activity было закрыто.

Это очень полезный механизм и применим не только к Handler. Рекомендую понять его и использовать для предотвращения утечек.

_________________
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 27 янв 2014, 10:24 

Зарегистрирован: 28 окт 2013, 11:13
Сообщений: 6
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
очень полезно. спасибо. вы меня спровоцировали почитать о рефлексии


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 28 июл 2014, 14:44 

Зарегистрирован: 28 июл 2014, 14:16
Сообщений: 2
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.
А как восстановить ссылку в MyHandler на MainActivity?
Например, при смене ориентации экрана, Activity пересоздается, а используя wrActivity.get() в MyHandler мы будем ссылаться на null или старый Activity.
А как найти ссылку на вновь созданный MainActivity?


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 29 июл 2014, 14:13 
Аватар пользователя

Зарегистрирован: 18 окт 2012, 11:17
Сообщений: 1098
Откуда: г. Красноярск
Благодарил (а): 26 раз.
Поблагодарили: 279 раз.
Sikambr писал(а):
А как восстановить ссылку в MyHandler на MainActivity?
Например, при смене ориентации экрана, Activity пересоздается, а используя wrActivity.get() в MyHandler мы будем ссылаться на null или старый Activity.
А как найти ссылку на вновь созданный MainActivity?


не должно быть так.
после переворота создается новый MyHandler в onCreate():
Код: [ Загрузить ] [ Скрыть ]
Using Java Syntax Highlighting
  1. handler = new MyHandler(this);


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 30 июл 2014, 12:04 

Зарегистрирован: 28 июл 2014, 14:16
Сообщений: 2
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.
klblk писал(а):
не должно быть так.

Вы правы!
Но только не по причине
klblk писал(а):
после переворота создается новый MyHandler в onCreate():
handler = new MyHandler(this);

а вот из за этого
Код: [ Загрузить ] [ Скрыть ]
  1. if (handler != null) 
  2.     handler.removeCallbacksAndMessages(null); 

У меня немного другая ситуация, Handler оборачивается в Message и всё это передается куда-то. Примерно так:
Код: [ Загрузить ] [ Скрыть ]
  1. dialog.setButton(DialogInterface.BUTTON_POSITIVE, "text", Message.obtain(new MyHandler()); 

а ссылку на MyHandler хранить не хотелось бы.
Единственное, что в голову лезет, так это то, что MainActivity может быть только один, вот его как-то и найти.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 18 янв 2015, 17:32 

Зарегистрирован: 18 янв 2015, 17:09
Сообщений: 1
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
У меня обратный вопрос - как сохранить состояние MainActivity и хэндлера при перевороте экрана ?
На примере того же счётчика, т.е. чтобы работа счётчика не прерывалась.
Или же такой подход будет неверным, и функцию счётчика следует возложить на тот же сервис, а из MainActivity при пересоздании - обращаться к сервису ?
Или же всё-таки можно сделать, например через свою реализацию onSaveInstanceState ?

Вопрос именно в идеологии.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 04 мар 2015, 14:16 

Зарегистрирован: 27 янв 2015, 23:17
Сообщений: 6
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.
Доброго всем дня.
Я реализую Handler в созданном мной классе. Применрно так:
Код: [ Загрузить ] [ Скрыть ]
  1. public class MyClass 
  2. private void someMethod1() 
  3.  
  4. private void someMethod2() 
  5.  
  6. void processMessage(Message msg) 
  7.     { 
  8.         switch (msg.what) 
  9.         { 
  10.             case myclass.MSG1: 
  11.             { 
  12.                 ... 
  13.                 someMethod1(); 
  14.                 break; 
  15.             } 
  16.  
  17.             case myclass.MSG2: 
  18.             { 
  19.                ... 
  20.                 someMethod2(); 
  21.                break; 
  22.             } 
  23.         } 
  24.     } 
  25.      
  26.     private final int MSG1    = 1; 
  27.     private final int MSG2    = 2; 
  28.     private final int MSG3    = 3; 
  29.  
  30.     static class wHandler extends Handler 
  31.     { 
  32.         WeakReference<MyClass> wrMyClass; 
  33.         public wHandler(MyClass myclass) 
  34.         { 
  35.             wrWheel = new WeakReference<MyClass>(myclass); 
  36.         } 
  37.  
  38.         @Override 
  39.         public void handleMessage(Message msg) 
  40.         { 
  41.             super.handleMessage(msg); 
  42.              
  43.             MyClass myclass = wrMyClass.get(); 
  44.              
  45.             myclass.processMessage(msg); 
  46.         } 
  47.     } 


Но ведь в таком случае метод processMessage() становится доступным любому, кто создал эксземпляр класса MyClass(). Как от этого защититься?


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 04 мар 2015, 14:45 
Аватар пользователя

Зарегистрирован: 18 окт 2012, 11:17
Сообщений: 1098
Откуда: г. Красноярск
Благодарил (а): 26 раз.
Поблагодарили: 279 раз.
private void processMessage(Message msg)?


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 04 мар 2015, 23:43 

Зарегистрирован: 27 янв 2015, 23:17
Сообщений: 6
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.
klblk писал(а):
private void processMessage(Message msg)?

Да, конечно. Спасибо. Надо же мне было так ступить... :)


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 05 мар 2015, 08:15 
Аватар пользователя

Зарегистрирован: 18 окт 2012, 11:17
Сообщений: 1098
Откуда: г. Красноярск
Благодарил (а): 26 раз.
Поблагодарили: 279 раз.
У меня тоже вопрос возник по Handler'у. Что если бы его будем создавать не в onCreate, а сразу инициализируем его:
Код: [ Загрузить ] [ Скрыть ]
Using Java Syntax Highlighting
  1. public class MainActivity extends Activity {
  2.         Handler handler = new MyHandler(this);
  3. ...
  4. }


Будут ли в данном случае неприятные последствия? Насколько я помню Java в данном случае он создастся в конструкторе MainActivity.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 05 мар 2015, 11:30 
Аватар пользователя

Зарегистрирован: 23 ноя 2013, 16:08
Сообщений: 1106
Откуда: Ukraine
Благодарил (а): 30 раз.
Поблагодарили: 175 раз.
klblk писал(а):
У меня тоже вопрос возник по Handler'у. Что если бы его будем создавать не в onCreate, а сразу инициализируем его:
Код: [ Загрузить ] [ Скрыть ]
Using Java Syntax Highlighting
  1. public class MainActivity extends Activity {
  2.         Handler handler = new MyHandler(this);
  3. ...
  4. }


Будут ли в данном случае неприятные последствия? Насколько я помню Java в данном случае он создастся в конструкторе MainActivity.

http://stackoverflow.com/questions/2079 ... ng-handler

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


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 25 мар 2015, 17:02 

Зарегистрирован: 27 фев 2015, 15:11
Сообщений: 4
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Жаль, что вы не привели пример кода с использованием мягких ссылок.
На всякий случай приведу код урока, который у меня получился в итоге.
Код: [ Загрузить ] [ Скрыть ]
  1. package ru.mytest.p0801_handler; 
  2.  
  3. import android.os.Handler; 
  4. import android.os.Message; 
  5. import android.support.v7.app.ActionBarActivity; 
  6. import android.os.Bundle; 
  7. import android.util.Log; 
  8. import android.view.View; 
  9. import android.widget.Button; 
  10. import android.widget.TextView; 
  11.  
  12. import java.lang.ref.WeakReference; 
  13. import java.util.concurrent.TimeUnit; 
  14.  
  15. public class MainActivity extends ActionBarActivity { 
  16.     final String LOG_TAG = "myLog"; 
  17.     TextView tvInfo; 
  18.     Button btnStart; 
  19.     static myHandler mHandler; 
  20.  
  21.     private static class myHandler extends Handler { 
  22.         private WeakReference<MainActivity> mTarget; 
  23.  
  24.         myHandler(MainActivity target) { 
  25.             mTarget = new WeakReference<MainActivity>(target); 
  26.         } 
  27.  
  28.         public void setTarget(MainActivity target) { 
  29.             mTarget.clear(); 
  30.             mTarget = new WeakReference<MainActivity>(target); 
  31.         } 
  32.  
  33.         @Override 
  34.         public void handleMessage(Message msg) { 
  35.             MainActivity activity = mTarget.get(); 
  36.             // обновляем TextView 
  37.             activity.tvInfo.setText("Закачано файлов: " + msg.what); 
  38.             if (msg.what == 10) activity.btnStart.setEnabled(true); 
  39.         } 
  40.     } 
  41.  
  42.     /** Called when the activity is first created. */ 
  43.     public void onCreate(Bundle savedInstanceState) { 
  44.         super.onCreate(savedInstanceState); 
  45.         setContentView(R.layout.main); 
  46.         tvInfo = (TextView) findViewById(R.id.tvInfo); 
  47.         btnStart = (Button) findViewById(R.id.btnStart); 
  48.         if(mHandler == null) mHandler = new myHandler(this); 
  49.         else mHandler.setTarget(this); 
  50.     } 
  51.  
  52.     public void onclick(View v) { 
  53.         switch (v.getId()) { 
  54.             case R.id.btnStart: 
  55.                 btnStart.setEnabled(false); 
  56.                 Thread t = new Thread(new Runnable() { 
  57.                     public void run() { 
  58.                         for (int i = 1; i <= 10; i++) { 
  59.                             // долгий процесс 
  60.                             downloadFile(); 
  61.                             mHandler.sendEmptyMessage(i); 
  62.                             // пишем лог 
  63.                             Log.d(LOG_TAG, "i = " + i); 
  64.                         } 
  65.                     } 
  66.                 }); 
  67.                 t.start(); 
  68.                 break; 
  69.             case R.id.btnTest: 
  70.                 Log.d(LOG_TAG, "test"); 
  71.                 break; 
  72.             default: 
  73.                 break; 
  74.         } 
  75.     } 
  76.  
  77.     void downloadFile() { 
  78.         // пауза - 1 секунда 
  79.         try { 
  80.             TimeUnit.SECONDS.sleep(1); 
  81.         } catch (InterruptedException e) { 
  82.             e.printStackTrace(); 
  83.         } 
  84.     } 


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 25 мар 2015, 22:05 
Аватар пользователя

Зарегистрирован: 09 янв 2012, 14:45
Сообщений: 2386
Откуда: Самара
Благодарил (а): 102 раз.
Поблагодарили: 321 раз.
Мягкие ссылки? Вы серьёзно? Тут народ кнопочку боится в XML написать, а базовую Java знает процентов 30 от силы, а тут мягкие ссылки. К сожалению (хотя наверное к счастью =) ), их никто особо и не знает. Об этой теме я даже заметку написал, в подписи. Первая.

_________________
Изображение

А тот ли ты путь выбрал, разработчик?
Хочешь знать ошибки ответ? Загляни в logcat!


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 12 май 2015, 07:38 
Аватар пользователя

Зарегистрирован: 02 май 2014, 13:13
Сообщений: 45
Откуда: Уфа
Благодарил (а): 0 раз.
Поблагодарили: 2 раз.
Цитата:
"This Handler class should be static or leaks might occur "


Обхожу это так:

Код: [ Загрузить ] [ Скрыть ]
  1. private Handler mHandler = new Handler(new Handler.Callback() { 
  2.         @Override 
  3.         public boolean handleMessage(Message msg) { 
  4.             if (msg.what == MSG) { 
  5.                 // your code 
  6.                 return true; 
  7.             } 
  8.             return false; 
  9.         } 
  10.     }); 

_________________
Русскоязычный чат Android разработчиков https://gitter.im/rus-speaking/android


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 24 июл 2015, 18:57 

Зарегистрирован: 28 июн 2015, 03:13
Сообщений: 36
Благодарил (а): 17 раз.
Поблагодарили: 1 раз.
onesoft писал(а):
Жаль, что вы не привели пример кода с использованием мягких ссылок.
На всякий случай приведу код урока, который у меня получился в итоге.
Код: [ Загрузить ] [ Скрыть ]
  1. package ru.mytest.p0801_handler; 
  2.  
  3. import android.os.Handler; 
  4. import android.os.Message; 
  5. import android.support.v7.app.ActionBarActivity; 
  6. import android.os.Bundle; 
  7. import android.util.Log; 
  8. import android.view.View; 
  9. import android.widget.Button; 
  10. import android.widget.TextView; 
  11.  
  12. import java.lang.ref.WeakReference; 
  13. import java.util.concurrent.TimeUnit; 
  14.  
  15. public class MainActivity extends ActionBarActivity { 
  16.     final String LOG_TAG = "myLog"; 
  17.     TextView tvInfo; 
  18.     Button btnStart; 
  19.     static myHandler mHandler; 
  20.  
  21.     private static class myHandler extends Handler { 
  22.         private WeakReference<MainActivity> mTarget; 
  23.  
  24.         myHandler(MainActivity target) { 
  25.             mTarget = new WeakReference<MainActivity>(target); 
  26.         } 
  27.  
  28.         public void setTarget(MainActivity target) { 
  29.             mTarget.clear(); 
  30.             mTarget = new WeakReference<MainActivity>(target); 
  31.         } 
  32.  
  33.         @Override 
  34.         public void handleMessage(Message msg) { 
  35.             MainActivity activity = mTarget.get(); 
  36.             // обновляем TextView 
  37.             activity.tvInfo.setText("Закачано файлов: " + msg.what); 
  38.             if (msg.what == 10) activity.btnStart.setEnabled(true); 
  39.         } 
  40.     } 
  41.  
  42.     /** Called when the activity is first created. */ 
  43.     public void onCreate(Bundle savedInstanceState) { 
  44.         super.onCreate(savedInstanceState); 
  45.         setContentView(R.layout.main); 
  46.         tvInfo = (TextView) findViewById(R.id.tvInfo); 
  47.         btnStart = (Button) findViewById(R.id.btnStart); 
  48.         if(mHandler == null) mHandler = new myHandler(this); 
  49.         else mHandler.setTarget(this); 
  50.     } 
  51.  
  52.     public void onclick(View v) { 
  53.         switch (v.getId()) { 
  54.             case R.id.btnStart: 
  55.                 btnStart.setEnabled(false); 
  56.                 Thread t = new Thread(new Runnable() { 
  57.                     public void run() { 
  58.                         for (int i = 1; i <= 10; i++) { 
  59.                             // долгий процесс 
  60.                             downloadFile(); 
  61.                             mHandler.sendEmptyMessage(i); 
  62.                             // пишем лог 
  63.                             Log.d(LOG_TAG, "i = " + i); 
  64.                         } 
  65.                     } 
  66.                 }); 
  67.                 t.start(); 
  68.                 break; 
  69.             case R.id.btnTest: 
  70.                 Log.d(LOG_TAG, "test"); 
  71.                 break; 
  72.             default: 
  73.                 break; 
  74.         } 
  75.     } 
  76.  
  77.     void downloadFile() { 
  78.         // пауза - 1 секунда 
  79.         try { 
  80.             TimeUnit.SECONDS.sleep(1); 
  81.         } catch (InterruptedException e) { 
  82.             e.printStackTrace(); 
  83.         } 
  84.     } 


Спасибо. Ваш код очень помог.
Но не совсем понятно:
1. В каком случае произойдёт вызов mHandler.setTarget(this); ??? Может ли вообще такое случиться и в какой ситуации?
Код: [ Загрузить ] [ Скрыть ]
  1. if(mHandler == null) mHandler = new myHandler(this); 
  2.         else mHandler.setTarget(this); 


2. Здесь на форуме, в главном примере, есть переопределение метода onDestroy:
Цитата:
@Override
protected void onDestroy() {
if (handler != null)
handler.removeCallbacksAndMessages(null);
super.onDestroy();
}

Нужно ли дописать его в данном случае?
Я так полагаю, что метод handler.removeCallbacksAndMessages(null); позволяет нам удалять только сообщения, но это не позволяет решить всю проблему утечки памяти, т.к. ссылки на объекты остаются??


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 26 июл 2015, 14:24 

Зарегистрирован: 26 июл 2015, 13:43
Сообщений: 1
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Друзья, извините, если вопрос глупый. Пытаюсь работать с потоками из фрагмента. Нужны ли в таком случае слабые ссылки? Если да, то нужно ли передавать в статический handler Activity (и если тоже "да", то как?).

Заранее спасибо.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 16 сен 2015, 00:45 
Аватар пользователя

Зарегистрирован: 09 сен 2015, 13:53
Сообщений: 19
Благодарил (а): 1 раз.
Поблагодарили: 5 раз.
K_Vladimir писал(а):
2. Здесь на форуме, в главном примере, есть переопределение метода onDestroy:
Цитата:
@Override
protected void onDestroy() {
if (handler != null)
handler.removeCallbacksAndMessages(null);
super.onDestroy();
}

Нужно ли дописать его в данном случае?
Я так полагаю, что метод handler.removeCallbacksAndMessages(null); позволяет нам удалять только сообщения, но это не позволяет решить всю проблему утечки памяти, т.к. ссылки на объекты остаются??


Мы удалили все сообщения из очереди в хендлере - таким образом хендлер готов чтобы его очистил GC.
Если хендлер создавать в статическом внутреннем классе или в отдельном классе тогда он не имеет ссылки на Активити, а значит не будет удерживать его от очистки GC.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 16 сен 2015, 00:56 
Аватар пользователя

Зарегистрирован: 09 сен 2015, 13:53
Сообщений: 19
Благодарил (а): 1 раз.
Поблагодарили: 5 раз.
итак я для себя выделил такие утечки:

1. Статическая переменная класса Activity содержит в себе ссылку на контекст.
Пример: http://android-developers.blogspot.ru/2 ... leaks.html Допустим у вас в onCreate() создается некий Drawable, который достаточно большой и вы хотите чтобы в случае смены ориентации экрана (при смене вызывается onDestroy() и onCreate() каждый раз) его не создавать заново, а взять из статической переменной класса. Все ок, но если вы связали этот Drawable например с каким-то View, то в статическом поле будет сидеть drawable c уже ссылкой на весь Активити, т.к. в каждом View есть ссылка на Контекст. И этот весь старый Активити сборщик мусора не уберет.
Решение: http://stackoverflow.com/questions/6567 ... on-android Решается это тем, что в методе оnDestroy делается unbind, то есть обнуляется связь у Drawable, чтобы он не тянул за собой весь предыдущий активити.

2. Утечка в случае с хендлером. Если хендлер объявить как внутренний класс в Активити, то может образоваться утечка, т.к. внутренний класс содержит скрытую ссылку на внешний класс (Активити) по этому Активити не очистится GC*, а так как в Хендлере будут сообщения которые ожидают своей отработки (а они не могут потому что Активити давно уже закрыта) то и Хендлер не очистится GC.
Пример и решение здесь: http://www.androiddesignpatterns.com/20 ... -leak.html Если коротко то, надо хендлер инициализировать как вложенный статический класс или как отдельный класс. А если необходимо в него передать все таки ссылку на контекст - передать ее через класс-переходник WeakReference что позволит удалить Активити даже при наличии слабой ссылки.

3. внутренние классы - все то же самое как и в п. 2 (внутренние классы содержат ссылки на внешние классы)
Здесь как еще один пример могут послужить анонимные внутренние классы ...new Runnable(){... или new Thread(){ они тоже содержат ссылку на внешний класс и если этот поток явно не потушить то он будет держать Активити пока ОС не вырубит все приложение.
http://www.androiddesignpatterns.com/20 ... leaks.html Здесь в статье показывается на диаграмме как забивается память при таких открываниях потока если десять раз поменять ориентацию экрана (покрутить телефон).
Решение: в этой же статье. Если коротко - определйте свои методы Thread и устанавливайте метод close() котторый бы убивал этот поток. В методе onDestroy() вызывайе myThread.close();

Единственное что мне здесь не понятно: в статье сказано что
After each configuration change, the Android system creates a new Activity and leaves the old one behind to be garbage collected. However, the thread holds an implicit reference to the old Activity and prevents it from ever being reclaimed. As a result, each new Activity is leaked and all resources associated with them are never able to be reclaimed. то есть каждый раз создается новое Активити, а старые из-за скрытой ссылки никогда уже не будут очищенны (разумеется пока работает приложение).
Так вот вопрос: когда поток отработает - он должен сам обнулиться, а значит и ссылка на Активити пропадет - следовательно Активити сможет быть очищен GC, так ли это или уже все, поток завис и не закончится никогда, а значит и Активити будет висеть до последнего?

* - GC - Garbage Collection ( http://www.oracle.com/webfolder/technet ... index.html )


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Используем static Handler
СообщениеДобавлено: 10 окт 2017, 14:28 

Зарегистрирован: 10 окт 2017, 14:05
Сообщений: 1
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
любой не статический внутренний класс (в том числе анонимный) содержит неявную ссылку на объект-энклозинг. В исходниках соотв. примера создаются объекты двух анонимных классов - хендлер и ранабл для потока и оба содержат неявную сильную ссылку на активю. Когда поток закончит исполнение, он релизнет ссылку на свой ранабл и, как следствие, на активю.

Мораль - лики возникают тогда, когда объект с бОльшим скоупом ссылается на объект с мЕньшим скоупом, а уж как это будет имплементировано - через статику, анонимные классы, статические внутренние с незачищенным листнером - дело десятое. Строго говоря, исходник из примера все равно может ликать активю, но не через хэндлер, а через ранабл


Вернуться наверх
 Профиль  
 
Показать сообщения за:  Сортировать по:  
Начать новую тему Ответить на тему  [ Сообщений: 19 ] 

Часовой пояс: UTC + 3 часа


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
cron
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB