[android] 이 핸들러 클래스는 정적이거나 누수가 발생할 수 있습니다. 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());다음과 같은 린트 경고가 나타납니다.

This Handler class should be static or leaks might occur: IncomingHandler

무슨 뜻이에요?



답변

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 경고는 잠재적 인 메모리 누수로 인한 것입니다. Handler.Callback구성 할 때 를 전달하여 Lint 경고를 피할 수 있습니다 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메시지 큐에서,이 ServiceGC되지 않습니다. 그러나 메시지 대기열에 메시지가 오래 지연되지 않는 한 심각한 문제는 아닙니다.


답변

다음은 약한 참조 및 정적 핸들러 클래스를 사용하여 문제점을 해결하는 일반적인 예입니다 (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 ()에서 처리기를 null로 처리 할 수 ​​있습니다.


답변

혼란 스러워요. 내가 찾은 예제는 정적 속성을 완전히 피하고 UI 스레드를 사용합니다.

    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();
                }
            }
        };
    }

이 솔루션에 대해 내가 좋아하는 것은 클래스와 메소드 변수를 혼합하는 데 아무런 문제가 없다는 것입니다.