경고 :이 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 , here 및 here 참조 ). - 당신이
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);
}
}
}
'IT story' 카테고리의 다른 글
페이지가로드 될 때 기능을 실행하는 방법은 무엇입니까? (0) | 2020.04.23 |
---|---|
SQL Server에 "LIKE"와 "IN"결합 (0) | 2020.04.23 |
Visual Studio 2012에서 내 테스트를 찾지 못하는 이유는 무엇입니까? (0) | 2020.04.23 |
이미 체크인 한 디렉토리의 내용을 무시 하시겠습니까? (0) | 2020.04.23 |
Guid는 모두 0입니까? (0) | 2020.04.23 |