Я досліджував цю проблему вже місяцями, придумав різні її рішення, якими я не задоволений, оскільки всі вони масові хаки. Я досі не можу повірити, що клас, який зіпсувався в дизайні, ввійшов у рамки, і ніхто про це не говорить, тому, мабуть, мені просто щось не вистачає.
Проблема в с AsyncTask
. Згідно з документацією, це
"дозволяє виконувати фонові операції та публікувати результати в потоці інтерфейсу користувача, не маніпулюючи потоками та / або обробниками."
Потім приклад продовжує показувати, як showDialog()
викликається деякий зразковий метод onPostExecute()
. Це, однак, здається мені цілком надуманим , тому що для показу діалогового вікна завжди потрібна посилання на дійсну Context
, а AsyncTask ніколи не має чіткого посилання на контекстний об'єкт .
Причина очевидна: що робити, якщо діяльність знищується, що ініціювало завдання? Це може траплятися постійно, наприклад, тому що ви перевернули екран. Якщо завдання міститиме посилання на контекст, який його створив, ви не тільки тримаєтесь за непотрібним контекстним об'єктом (вікно буде зруйновано і будь-яка взаємодія з інтерфейсом вийде з ладу за винятком!), Ви навіть ризикуєте створити витік пам'яті.
Якщо моя логіка тут хибна, це означає: onPostExecute()
цілком марний, адже яке добре для цього методу запускати на потоці інтерфейсу, якщо у вас немає доступу до жодного контексту? Тут не можна зробити нічого значимого.
Одним із завдань було б передати екземпляри контексту AsyncTask, але Handler
екземпляр. Це працює: оскільки обробник невміло зв’язує контекст і завдання, ви можете обмінюватися повідомленнями між ними, не ризикуючи протікати (правда?). Але це означатиме, що передумова AsyncTask, а саме, що вам не потрібно турбуватися з обробниками, є неправильним. Це також здається зловживанням Handler, оскільки ви надсилаєте та отримуєте повідомлення в одному потоці (ви створюєте його в потоці інтерфейсу користувача та надсилаєте через нього в onPostExecute (), який також виконується в потоці інтерфейсу).
На додаток до цього, навіть із цим вирішенням, у вас все ще виникає проблема, що коли контекст руйнується, у вас немає запису про виконання завдань, які він виконував. Це означає, що вам потрібно перезапустити будь-які завдання під час створення контексту, наприклад, після зміни орієнтації екрана. Це повільно і марно.
Моє рішення цього (як реалізовано в бібліотеці Droid-Fu ) полягає в підтримці відображення WeakReference
s від імен компонентів до їх поточних примірників на унікальному об’єкті програми. Кожного разу, коли запускається AsyncTask, він записує контекст виклику на цій карті, і при кожному зворотному виклику він отримує поточний екземпляр контексту з цього відображення. Це гарантує, що ви ніколи не будете посилатися на нескладний контекстний екземпляр, і ви завжди матимете доступ до дійсного контексту в зворотному звороті, щоб ви могли робити значну роботу інтерфейсу там. Він також не протікає, оскільки посилання слабкі та очищаються, коли жодного примірника певного компонента більше не існує.
Але це складний спосіб вирішення і вимагає підкласи деяких класів бібліотек Droid-Fu, що робить це досить нав'язливим підходом.
Тепер я просто хочу знати: я просто масово щось пропускаю, чи AsyncTask справді повністю помилковий? Як ви працюєте з цим своїм досвідом? Як ви вирішили ці проблеми?
Дякуємо за ваш внесок