[Android] 이 Handler 클래스는 정적이거나 누수가 발생할 수 있습니다. IncomingHandler


Answers

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의이 post 을 참조하십시오.

Question

나는 서비스로 안드로이드 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

무슨 뜻이에요?




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

    ...

}



린트 (Lint) 경고는 잠재적 인 메모리 누수로 인한 것이라고 다른 사람들이 언급했습니다. Handler Handler.Callback 할 때 Handler.Callback 을 전달하여 Lint 경고를 피할 수 있습니다 (즉, Handler 하위 클래스는 아니며 Handler 비 정적 인 내부 클래스는 없습니다).

Handler mIncomingHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
    }
});

내가 알기에 이것은 잠재적 인 메모리 누출을 피할 수는 없을 것이다. Message 객체는 Service 객체에 대한 참조를 보유하는 Handler.Callback 객체에 대한 참조를 보유하는 Handler.Callback 객체에 대한 참조를 보유합니다. Looper 메시지 대기열에 메시지가있는 한 Service 는 GC가 아닙니다. 그러나 메시지 대기열에 긴 지연 메시지가 없으면 심각한 문제가되지 않습니다.




이 방법은 저에게 효과적이며 내부 클래스에서 메시지를 처리하는 위치를 유지함으로써 코드를 깨끗하게 유지합니다.

사용할 핸들러

Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());

내부 클래스

class IncomingHandlerCallback implements Handler.Callback{

    @Override
    public boolean handleMessage(Message message) {

        // Handle message code

        return true;
    }



다음은 약한 참조 및 정적 핸들러 클래스를 사용하여 문제를 해결하는 일반적인 예입니다 (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);
  }
}



확실하지는 않지만 onDestroy ()에서 null에 대한 intialising 핸들러를 시도 할 수 있습니다.