Handler로 인한 메모리 유출 및 해결 방법

Activity에서 내부Handler 클래스를 정의한 경우 다음 코드가 사용됩니다.
public class MainActivity extends Activity {
 
    private  Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //TODO handle message...
        }
    };
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendMessageDelayed(Message.obtain(), 60000);
 
        //just finish this activity
        finish();
    }
}

그런 다음 Android Lint 도구를 실행하면 메모리 유출 경고가 표시됩니다.
This Handler class should be static or leaks might occur (com.example.ta.MainActivity.1)
Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak
In Android, Handler classes should be static or leaks might occur. Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.

이유:
안드로이드 앱이 시작될 때, 메인 라인의 Looper 대상을 먼저 만들고, Looper는 간단한 메시지 대기열을 실현하여, 그 안의 메시지 대상을 하나하나 처리합니다.운영 스레드 Looper 객체는 전체 애플리케이션 수명주기에 걸쳐 존재합니다
주 라인에서Handler를 초기화할 때, 이Handler는 Looper의 메시지 대기열과 연결됩니다.메시지 대기열에 보내는 메시지는 이 메시지를 보내는handler 대상을 인용하여 시스템이handler#handle 메시지(Message)를 호출하여 이 메시지를 나누어 처리할 수 있도록 한다
Java에서는 비정적(익명) 내부 클래스가 외부 클래스 객체를 참조합니다.정적 내부 클래스는 외부 클래스의 대상을 인용하지 않는다
외부 클래스가 Activity인 경우 Activity 유출이 발생합니다
Activity finish 이후 지연 메시지는 주 스레드 메시지 대기열에 1분 동안 계속 존재한 다음 메시지를 처리합니다.이 메시지는 Activity의handler 대상을 인용했고 이handler는 이Activity를 인용했다.이러한 인용 대상은 이 메시지가 처리될 때까지 유지되며, 이로 인해 이 Activity 대상은 회수할 수 없게 되어 위에서 말한 Activity 유출을 초래하게 된다.
이 문제를 수정하려면 Lint에서 제시한 대로Handler 클래스를 정적으로 정의한 다음 WeakReference를 통해 외부의Activity 대상을 유지하십시오.
private Handler mHandler = new MyHandler(this);
private static class MyHandler extends Handler{
    private final WeakReference<Activity> mActivity;
    public MyHandler(Activity activity) {
        mActivity = new WeakReference<Activity>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        System.out.println(msg);
        if(mActivity.get() == null) {
            return;
        }
    }
}

따라서 Activity에서 내부 클래스를 사용할 때 내부 클래스의 생명 주기를 제어할 수 있는지 항상 고려해야 한다. 그렇지 않으면 정적 내부 클래스로 정의하는 것이 좋다.
참고 자료:http://blog.chengyunfeng.com/?p=468

좋은 웹페이지 즐겨찾기