programing

이 핸들러 클래스는 정적이어야 합니다. 그렇지 않으면 누출이 발생할 수 있습니다.들어오는 처리기

powerit 2023. 10. 15. 18:08
반응형

이 핸들러 클래스는 정적이어야 합니다. 그렇지 않으면 누출이 발생할 수 있습니다.들어오는 처리기

안드로이드 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동일한 스레드에 대한 개체는 모두 공통의 루퍼 개체를 공유하며, 루퍼 개체는 메시지를 게시하고 이 개체로부터 읽습니다.

메시지에 대상이 포함되어 있으므로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);
         }
    }
}

자세한 참조를 위해 로맹 가이의 이 게시물을 참조하십시오.

다른 사람들이 언급한 바와 같이 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메시지 큐,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;
        }
    }

    ...

}

확실하지는 않지만 핸들러가 디스트로이()에서 널인하도록 시도해 볼 수 있습니다.

저는 혼란스러워요.제가 찾은 예제는 정적 속성을 완전히 피하고 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();
                }
            }
        };
    }

이 솔루션이 마음에 드는 점은 클래스 변수와 메소드 변수를 섞는 데 문제가 없다는 것입니다.

만약 당신이 코틀린을 사용하고 있다면, 간단히 제거하세요.inner중첩 클래스를 선언할 때 키워드를 지정합니다.

Kotlin의 중첩된 클래스는 기본적으로 정적이며 다음과 함께 선언합니다.inner고정적이지 않은 상태로 만듭니다.

중첩 처리기 하위 클래스 선언을 다음에서 변경합니다.

class myService : Service() {

inner class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}

}

로.

class myService : Service() {

class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}

}

언급URL : https://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler

반응형