서비스로 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
메시지 큐에서,이 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 ()에서 처리기를 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();
}
}
};
}
이 솔루션에 대해 내가 좋아하는 것은 클래스와 메소드 변수를 혼합하는 데 아무런 문제가 없다는 것입니다.