IT story

경고 :이 AsyncTask 클래스는 정적이거나 누수가 발생할 수 있습니다.

hot-time 2020. 4. 23. 08:02
반응형

경고 :이 AsyncTask 클래스는 정적이거나 누수가 발생할 수 있습니다.


내 코드에 다음과 같은 경고가 표시됩니다.

이 AsyncTask 클래스는 정적이거나 누수가 발생할 수 있습니다 (익명 android.os.AsyncTask)

완전한 경고는 다음과 같습니다.

이 AsyncTask 클래스는 정적이거나 누출이 발생할 수 있습니다 (익명 android.os.AsyncTask) 정적 필드는 컨텍스트를 누출합니다. 비 정적 내부 클래스에는 외부 클래스에 대한 암시 적 참조가 있습니다. 해당 외부 클래스가 예를 들어 Fragment 또는 Activity 인 경우이 참조는 장기 실행 핸들러 / 로더 / 태스크가 가비지 수집을 방해하는 활동에 대한 참조를 보유 함을 의미합니다. 마찬가지로, 더 오래 실행되는 인스턴스의 활동 및 단편에 대한 직접 필드 참조는 누출을 일으킬 수 있습니다. ViewModel 클래스는 절대로 뷰 또는 비 응용 컨텍스트를 가리켜서는 안됩니다.

이것은 내 코드입니다.

 new AsyncTask<Void,Void,Void>(){

        @Override
        protected Void doInBackground(Void... params) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });

            return null;
        }
    }.execute();

이 문제를 어떻게 해결합니까?


비 정적 내부 클래스는 포함 클래스에 대한 참조를 보유합니다. AsyncTask내부 클래스로 선언 하면 포함 Activity클래스 보다 오래 살 수 있습니다 . 포함하는 클래스에 대한 암시 적 참조 때문입니다. 이렇게하면 활동이 가비지 수집되지 않으므로 메모리 누수가 발생합니다.

문제를 해결하려면 익명, 로컬 및 내부 클래스 대신 정적 중첩 클래스를 사용하거나 최상위 클래스를 사용하십시오.


정적 내부 AsyncTask 클래스를 사용하는 방법

누출을 방지하기 위해 내부 클래스를 정적으로 만들 수 있습니다. 그러나 문제는 더 이상 액티비티의 UI 뷰 또는 멤버 변수에 액세스 할 수 없다는 것입니다. 에 대한 참조를 전달할 수 Context있지만 동일한 메모리 누수 위험이 있습니다. (AsyncTask 클래스에 강한 참조가있는 경우 Android를 닫은 후에는 Android에서 Activity를 가비지 수집 할 수 없습니다.) 해결책은 Activity (또는 Context필요한 것) 를 약하게 참조하는 것 입니다.

public class MyActivity extends AppCompatActivity {

    int mSomeMemberVariable = 123;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor 
        new MyTask(this).execute();
    }

    private static class MyTask extends AsyncTask<Void, Void, String> {

        private WeakReference<MyActivity> activityReference;

        // only retain a weak reference to the activity 
        MyTask(MyActivity context) {
            activityReference = new WeakReference<>(context);
        }

        @Override
        protected String doInBackground(Void... params) {

            // do some long running task...

            return "task finished";
        }

        @Override
        protected void onPostExecute(String result) {

            // get a reference to the activity if it is still there
            MyActivity activity = activityReference.get();
            if (activity == null || activity.isFinishing()) return;

            // modify the activity's UI
            TextView textView = activity.findViewById(R.id.textview);
            textView.setText(result);

            // access Activity member variables
            activity.mSomeMemberVariable = 321;
        }
    }
}

메모

  • 내가 아는 한,이 유형의 메모리 누수 위험은 항상 사실이지만 Android Studio 3.0에서만 경고가 표시되기 시작했습니다. 많은 주요 AsyncTask튜토리얼은 여전히 ​​다루지 않습니다 ( here , here , herehere 참조 ).
  • 당신이 AsyncTask최상위 클래스 라면 비슷한 절차를 따를 것 입니다. 정적 내부 클래스는 기본적으로 Java의 최상위 클래스와 동일합니다.
  • 액티비티 자체는 필요하지 않지만 여전히 컨텍스트 (예 :)를 표시 Toast하려는 경우 앱 컨텍스트에 대한 참조를 전달할 수 있습니다. 이 경우 AsyncTask생성자는 다음과 같습니다.

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • 이 경고를 무시하고 정적이 아닌 클래스를 사용한다는 몇 가지 주장이 있습니다. 결국 AsyncTask는 매우 짧은 수명 (최대 2 초)이며 활동이 완료되면 액티비티에 대한 참조를 해제합니다. 참조 .
  • 훌륭한 기사 : 컨텍스트를 누출하는 방법 : 처리기 및 내부 클래스

코 틀린

Kotlin에서는 내부 클래스에 대한 키워드포함시키지 마십시오inner . 기본적으로 정적입니다.

아직 Kotlin을 잘하지 못하므로 개선 될 수 있다면 아래 코드를 수정하십시오.

class MyActivity : AppCompatActivity() {

    internal var mSomeMemberVariable = 123

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor
        MyTask(this).execute()
    }

    private class MyTask
    internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {

        private val activityReference: WeakReference<MyActivity> = WeakReference(context)

        override fun doInBackground(vararg params: Void): String {

            // do some long running task...

            return "task finished"
        }

        override fun onPostExecute(result: String) {

            // get a reference to the activity if it is still there
            val activity = activityReference.get()
            if (activity == null || activity.isFinishing) return

            // modify the activity's UI
            val textView = activity.findViewById(R.id.textview)
            textView.setText(result)

            // access Activity member variables
            activity.mSomeMemberVariable = 321
        }
    }
}

AsyncTask클래스는 정적이어야합니다. 그렇지 않으면 누수가 발생할 수 있습니다.

  • Activity파괴, AsyncTask(둘 static또는 non-static)은 여전히 실행
  • 내부 클래스가 non-static( AsyncTask) 클래스 인 경우 외부 클래스 ( Activity)를 참조하게됩니다 .
  • 오브젝트에 대한 참조가 없으면 Garbage Collected해제합니다. 객체를 사용하지 Garbage Collected 않고 해제 할 수없는 경우 => 메모리 누수

=> 경우 AsyncTask입니다 non-static, Activity=> 누출이 파괴 이벤트를 공개하지 않습니다

누출없이 AsyncTask를 정적 클래스로 만든 후 업데이트 UI 솔루션

1) 사용 WeakReference과 같은 @Suragch 답
2) 보내기 및 제거 Activity를 참조 (에서)AsyncTask

public class NoLeakAsyncTaskActivity extends AppCompatActivity {
    private ExampleAsyncTask asyncTask;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        ...

        // START AsyncTask
        asyncTask = new ExampleAsyncTask();
        asyncTask.setListener(new ExampleAsyncTask.ExampleAsyncTaskListener() {
            @Override
            public void onExampleAsyncTaskFinished(Integer value) {
                // update UI in Activity here
            }
        });
        asyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        asyncTask.setListener(null); // PREVENT LEAK AFTER ACTIVITY DESTROYED
        super.onDestroy();
    }

    static class ExampleAsyncTask extends AsyncTask<Void, Void, Integer> {
        private ExampleAsyncTaskListener listener;

        @Override
        protected Integer doInBackground(Void... voids) {
            ...
            return null;
        }

        @Override
        protected void onPostExecute(Integer value) {
            super.onPostExecute(value);
            if (listener != null) {
                listener.onExampleAsyncTaskFinished(value);
            }
        }

        public void setListener(ExampleAsyncTaskListener listener) {
            this.listener = listener;
        }

        public interface ExampleAsyncTaskListener {
            void onExampleAsyncTaskFinished(Integer value);
        }
    }
}

참고 URL : https://stackoverflow.com/questions/44309241/warning-this-asynctask-class-should-be-static-or-leaks-might-occur

반응형