Этот класс обработчика должен быть статическим, иначе могут возникнуть утечки: IncomingHandler
Я занимаюсь разработкой приложения для Android 2.3.3 с сервисом. У меня есть это внутри этого сервиса для связи с основной деятельностью:
public class UDPListenerService extends Service
{
private static final String TAG = "UDPListenerService";
//private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");
private UDPListenerThread myThread;
/**
* Handler to communicate from WorkerThread to service.
*/
private Handler mServiceHandler;
// Used to receive messages from the Activity
final Messenger inMessenger = new Messenger(new IncomingHandler());
// Use to send message to the Activity
private Messenger outMessenger;
class IncomingHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
}
}
/**
* Target we publish for clients to send messages to Incoming Handler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
[ ... ]
}
И здесь, final Messenger mMessenger = new Messenger(new IncomingHandler());
Я получаю следующее предупреждение Lint:
This Handler class should be static or leaks might occur: IncomingHandler
Что это значит?
8 ответов
Если IncomingHandler
класс не является статическим, он будет иметь ссылку на ваш Service
объект.
Handler
Все объекты одной и той же цепочки имеют общий объект Looper, в который они отправляют сообщения и читают из них.
Как сообщения содержат цель Handler
До тех пор, пока в очереди сообщений есть сообщения с целевым обработчиком, этот обработчик нельзя собирать. Если обработчик не статичен, ваш Service
или же Activity
не может быть мусора, даже после уничтожения.
Это может привести к утечкам памяти, по крайней мере, в течение некоторого времени, пока сообщения остаются в очереди. Это не большая проблема, если вы не публикуете сообщения с большой задержкой.
Ты можешь сделать IncomingHandler
статичны и имеют WeakReference
к вашим услугам:
static class IncomingHandler extends Handler {
private final WeakReference<UDPListenerService> mService;
IncomingHandler(UDPListenerService service) {
mService = new WeakReference<UDPListenerService>(service);
}
@Override
public void handleMessage(Message msg)
{
UDPListenerService service = mService.get();
if (service != null) {
service.handleMessage(msg);
}
}
}
Смотрите этот пост от Romain Guy для дальнейшего использования
Как уже упоминали другие, предупреждение Lint связано с потенциальной утечкой памяти. Вы можете избежать предупреждения Lint, передав Handler.Callback
при строительстве Handler
(т.е. вы не подкласс Handler
и нет Handler
нестатический внутренний класс):
Handler mIncomingHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
Насколько я понимаю, это не избежит потенциальной утечки памяти. Message
объекты содержат ссылку на mIncomingHandler
объект, который содержит ссылку на Handler.Callback
объект, который содержит ссылку на Service
объект. Пока есть сообщения в Looper
очередь сообщений, Service
не будет GC. Однако это не будет серьезной проблемой, если у вас нет сообщений с длительной задержкой в очереди сообщений.
Вот общий пример использования слабой ссылки и класса статического обработчика для решения проблемы (как рекомендуется в документации Lint):
public class MyClass{
//static inner class doesn't hold an implicit reference to the outer class
private static class MyHandler extends Handler {
//Using a weak reference means you won't prevent garbage collection
private final WeakReference<MyClass> myClassWeakReference;
public MyHandler(MyClass myClassInstance) {
myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
}
@Override
public void handleMessage(Message msg) {
MyClass myClass = myClassWeakReference.get();
if (myClass != null) {
...do work here...
}
}
}
/**
* An example getter to provide it to some external class
* or just use 'new MyHandler(this)' if you are using it internally.
* If you only use it internally you might even want it as final member:
* private final MyHandler mHandler = new MyHandler(this);
*/
public Handler getHandler() {
return new MyHandler(this);
}
}
Этот способ хорошо сработал для меня, поддерживает код в чистоте, сохраняя место обработки сообщения в его собственном внутреннем классе.
Обработчик, который вы хотите использовать
Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
Внутренний класс
class IncomingHandlerCallback implements Handler.Callback{
@Override
public boolean handleMessage(Message message) {
// Handle message code
return true;
}
}
С помощью ответа @Sogger я создал общий обработчик:
public class MainThreadHandler<T extends MessageHandler> extends Handler {
private final WeakReference<T> mInstance;
public MainThreadHandler(T clazz) {
// Remove the following line to use the current thread.
super(Looper.getMainLooper());
mInstance = new WeakReference<>(clazz);
}
@Override
public void handleMessage(Message msg) {
T clazz = mInstance.get();
if (clazz != null) {
clazz.handleMessage(msg);
}
}
}
Интерфейс:
public interface MessageHandler {
void handleMessage(Message msg);
}
Я использую это следующим образом. Но я не уверен на 100%, если это утечка. Может быть, кто-то мог бы прокомментировать это:
public class MyClass implements MessageHandler {
private static final int DO_IT_MSG = 123;
private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);
private void start() {
// Do it in 5 seconds.
mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_IT_MSG:
doIt();
break;
}
}
...
}
Я не уверен, но вы можете попробовать инициализировать обработчик для обнуления в onDestroy()
Если вы используете Kotlin, просто удалите ключевое слово при объявлении вложенного класса.
Вложенные классы в Kotlin по умолчанию являются статическими , объявляя их с помощьюinner
делает их нестатичными.
Измените вложенное объявление подкласса Handler с
class myService : Service() {
inner class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}
}
к
class myService : Service() {
class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}
}
Я запутался. В найденном мною примере полностью отсутствует статическое свойство и используется поток пользовательского интерфейса:
public class example extends Activity {
final int HANDLE_FIX_SCREEN = 1000;
public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
int imsg;
imsg = msg.what;
if (imsg == HANDLE_FIX_SCREEN) {
doSomething();
}
}
};
}
Что мне нравится в этом решении, так это то, что при попытке смешать переменные класса и метода нет проблем.