programing

경고:이 비동기 작업 클래스는 정적이어야 합니다. 그렇지 않으면 누출이 발생할 수 있습니다.

oldcodes 2023. 10. 31. 22:39
반응형

경고:이 비동기 작업 클래스는 정적이어야 합니다. 그렇지 않으면 누출이 발생할 수 있습니다.

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

이 AsyncTask 클래스는 정적이어야 합니다. 그렇지 않으면 누출이 발생할 수 있습니다(익명의 Android.비동기 작업)

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

이 AsyncTask 클래스는 정적이어야 합니다. 그렇지 않으면 누출이 발생할 수 있습니다(익명의 Android.AsyncTask) 정적 필드에서 컨텍스트가 누출됩니다.정적이지 않은 내부 클래스는 외부 클래스를 암시적으로 참조합니다.해당 외부 클래스가 예를 들어 Fragment(절편) 또는 Activity(액티비티)인 경우, 이 참조는 장기간 실행 중인 핸들러/로더/태스크가 가비지 수집을 방지하는 액티비티에 대한 참조를 보유함을 의미합니다.마찬가지로 활동에 대한 직접적인 필드 참조와 이처럼 더 오래 실행되는 인스턴스의 조각이 누출의 원인이 될 수 있습니다.ViewModel 클래스는 Views 또는 non-application Context를 가리켜서는 안 됩니다.

이게 내 암호입니다.

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

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

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

            return null;
        }
    }.execute();

수정하려면 어떻게 해야 합니까?

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

누출을 방지하기 위해 내부 클래스를 정적으로 설정할 수 있습니다.그러나 이 경우의 문제점은 활동의 UI 보기 또는 구성원 변수에 더 이상 액세스할 수 없다는 것입니다.에 대한 참고 자료를 전달할 수 있습니다.Context그러나 메모리 누수의 위험이 동일하게 발생합니다. (AsyncTask 클래스에 액티비티에 대한 강력한 참조가 있는 경우 Android는 액티비티가 닫힌 후 가비지 컬렉션을 할 수 없습니다.)해결책은 활동(또는 무엇이든)에 대해 약하게 언급하는 것입니다.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;
        }
    }
}

메모들

  • 제가 알기로는 이런 종류의 메모리 유출 위험은 항상 있었지만 안드로이드 스튜디오 3.0에서 비로소 경고를 보기 시작했습니다.많은 메인들이AsyncTask자습서는 여전히 이 문제를 다루지 않습니다(여기, 여기, 여기, 여기, 여기 참조).
  • 만약 당신이 당신이 당신과 비슷한 절차를 따를 것입니다.AsyncTask최고 수준의 수업이었어요.정적 내부 클래스는 기본적으로 Java의 최상위 클래스와 동일합니다.
  • 활동 자체가 필요하지 않지만 컨텍스트를 원하는 경우(예: 표시)Toast), 앱 컨텍스트에 대한 참조를 전달할 수 있습니다.이 경우에.AsyncTask생성자는 다음과 같습니다.

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • 이 경고를 무시하고 비정규 클래스만 사용하는 것에 대한 주장이 있습니다.결국, 비동기 작업은 수명이 매우 짧도록 설계되었으며(길어야 몇 초), 어쨌든 작업이 완료되면 작업에 대한 참조가 표시됩니다.이거이거랑.
  • 훌륭한 기사:컨텍스트 유출 방법: 핸들러 & 내부 클래스

코틀린

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내부 계급으로서, 그것은 함유된 것보다 더 오래 살 수 있습니다.Activityclass에 입니다. 이것은 포함하는 클래스에 대한 암묵적인 참조 때문입니다.이렇게 하면 작업이 가비지(garbage) 수집되어 메모리가 누출되는 것을 방지할 수 있습니다.

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

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

  • .Activity파괴되고,AsyncTask 다다))static아니면non-static) 아직 실행 중입니다.
  • 이너클래스가non-static(AsyncTask) 클래스, 외부 클래스를 참조합니다(Activity).
  • 개체에 참조 포인트가 없는 경우Garbage Collected풀어줄 겁니다.개체가 사용되지 않는 경우 및Garbage Collected 해제할 수 없음 => 메모리 누수

=> 만약AsyncTask이다.non-static,Activity이벤트가 해제되지 않습니다 파괴됩니다 => 누출

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

) 사용하기WeakReference
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

반응형