티스토리 뷰
'This AsyncTask class should be static or leaks might occur' 경고 수정하기
맨날치킨 2022. 12. 8. 15:05Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.
Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.
아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.
Warning: This AsyncTask class should be static or leaks might occur
경고: 이 AsyncTask 클래스는 static이어야하며 누수가 발생할 수 있습니다.
문제 내용
I am getting a warning in my code that states:
제 코드에서 경고가 발생합니다.
This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask)
이 AsyncTask 클래스는 정적이어야 하며, 누수가 발생할 수 있습니다(익명 android.os.AsyncTask).
The complete warning is:
전체 경고는 다음과 같습니다:
This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask) A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts.
이 AsyncTask 클래스는 static이어야 하며, 그렇지 않으면 leaks가 발생할 수 있습니다(익명 android.os.AsyncTask). static 필드는 contexts를 누출할 수 있습니다. 비 static 내부 클래스에는 outer 클래스에 대한 암시적 참조가 있습니다. 예를 들어, 해당 outer 클래스가 Fragment나 Activity인 경우 이 참조는 긴 작업 핸들러/로더/작업이 Activity에 대한 참조를 유지하므로 Activity가 쓰레기 수집되는 것을 방지합니다. 마찬가지로 이러한 긴 작업 핸들러/로더/작업에서 activity나 fragment에 대한 직접적인 필드 참조는 leaks를 유발할 수 있습니다. ViewModel 클래스는 Views나 비 어플리케이션 Context를 참조해서는 안 됩니다.
This is my code:
이것은 제 코드입니다:
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
return null;
}
}.execute();
How do I correct this?
이 문제를 어떻게 해결할 수 있나요?
높은 점수를 받은 Solution
How to use a static inner AsyncTask class
정적 내부 AsyncTask 클래스를 사용하는 방법
To prevent leaks, you can make the inner class static. The problem with that, though, is that you no longer have access to the Activity's UI views or member variables. You can pass in a reference to the Context
but then you run the same risk of a memory leak. (Android can't garbage collect the Activity after it closes if the AsyncTask class has a strong reference to it.) The solution is to make a weak reference to the Activity (or whatever Context
you need).
메모리 누수를 방지하기 위해서 내부 클래스를 static으로 만들 수 있습니다. 그러나 그렇게 하면 더 이상 Activity의 UI 뷰나 멤버 변수에 접근할 수 없는 문제가 발생합니다. Context에 대한 참조를 전달할 수 있지만 그렇게 하면 메모리 누수와 같은 위험성이 있습니다. (AsyncTask 클래스가 강한 참조를 유지하고 있다면 Android는 Activity가 닫힌 후에도 Garbage Collection 할 수 없습니다.) 해결책은 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;
}
}
}
Notes
참고
- As far as I know, this type of memory leak danger has always been true, but I only started seeing the warning in Android Studio 3.0. A lot of the main
AsyncTask
tutorials out there still don't deal with it (see here, here, here, and here). - You would also follow a similar procedure if your
AsyncTask
were a top-level class. A static inner class is basically the same as a top-level class in Java. - If you don't need the Activity itself but still want the Context (for example, to display a
Toast
), you can pass in a reference to the app context. In this case theAsyncTask
constructor would look like this: private WeakReference<Application> appReference; MyTask(Application context) { appReference = new WeakReference<>(context); }
- There are some arguments out there for ignoring this warning and just using the non-static class. After all, the AsyncTask is intended to be very short lived (a couple seconds at the longest), and it will release its reference to the Activity when it finishes anyway. See this and this.
- Excellent article: How to Leak a Context: Handlers & Inner Classes
▪ 내가 아는 한 이러한 유형의 메모리 누출 위험은 항상 존재했지만, 나는 안드로이드 스튜디오 3.0에서만 경고를 볼 수 있었다. 여전히 많은 AsyncTask 자습서들은 이 문제를 다루지 않고 있다(여기, 여기, 여기, 그리고 여기 참조).
▪ Top-level class 인 경우와 유사한 절차를 따르면 된다. Java에서 static inner class 는 기본적으로 top-level class 와 동일하다.
▪ Activity 자체가 필요하지 않지만 Context(예: Toast를 표시하기 위해)가 필요한 경우, 앱 context에 대한 참조를 전달할 수 있다. 이 경우 AsyncTask 생성자는 다음과 같이 보일 것이다:
▪ private WeakReference appReference; MyTask(Application context) { appReference = new WeakReference<>(context); } 이 warning을 무시하고 non-static class를 사용하는 것에 대한 일부 주장들도 있다.
▪ 결국 AsyncTask는 매우 짧은 기간(최대 몇 초) 동안 유지되도록 의도되어 있으며, 마침내 Activity에 대한 참조를 해제할 것이다. 이와 관련하여 이 글과 이 글을 참조하라.
▪ How to Leak a Context: Handlers & Inner Classes라는 훌륭한 기사도 있다.
Kotlin
In Kotlin just don't include the inner
keyword for the inner class. This makes it static by default.
Kotlin에서는 inner 키워드를 내부 클래스에 포함하지 않으면 자동으로 정적 클래스가 되므로 문제가 해결됩니다.
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
}
}
}
가장 최근 달린 Solution
This AsyncTask
class should be static or leaks might occur because
이 AsyncTask 클래스는 static이어야 하며, 그렇지 않으면 메모리 누수가 발생할 수 있습니다.
- When
Activity
is destroyed,AsyncTask
(bothstatic
ornon-static
) still running - If inner class is
non-static
(AsyncTask
) class, it will have reference to the outer class (Activity
). - If a object has no references point to it,
Garbage Collected
will release it. If a object is unused andGarbage Collected
can not release it => leak memory
▪ Activity가 파괴될 때 AsyncTask (정적 또는 비정적)이 계속 실행됩니다.
▪ 내부 클래스가 비정적 (AsyncTask) 클래스인 경우, 외부 클래스 (Activity)에 대한 참조가 있습니다.
▪ 참조가 없는 객체는 가비지 컬렉션이 해제합니다. 사용되지 않은 객체가 가비지 컬렉션에 의해 해제되지 않으면 메모리 누수가 발생합니다.
=> If AsyncTask
is non-static
, Activity
won't release event it is destroyed => leak
=> 만약 AsyncTask가 non-static이라면, Activity가 파괴되더라도 해제되지 않아 메모리 누수가 발생할 수 있습니다.
Solution for update UI after make AsyncTask as static class without leak
AsyncTask 클래스를 static으로 만들고 UI 업데이트를 안전하게 처리하려면..
1) Use WeakReference
like @Suragch answer
2) Send and remove Activity
reference to (from) AsyncTask
1. @Suragch의 답변과 같이 WeakReference를 사용합니다.
2. AsyncTask에 Activity 참조를 보내고 제거합니다.
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);
}
}
}
출처 : https://stackoverflow.com/questions/44309241/warning-this-asynctask-class-should-be-static-or-leaks-might-occur
'개발 > 안드로이드' 카테고리의 다른 글
'R cannot be resolved' 오류 수정하기(이클립스 or 안드로이드 스튜디오 환경) (0) | 2022.12.09 |
---|---|
Handler() Warning 메시지 없이 사용하기 (0) | 2022.12.08 |
apk 설치 오류 수정하기 (0) | 2022.12.08 |
웹뷰에 html이 정상적으로 로드되지 않을 때 시도해 볼만한 것 (0) | 2022.12.08 |
텍스트뷰 링크를 클릭할 수 있도록 하는 방법 (0) | 2022.12.07 |