programing

비동기 작업이 정말 개념적으로 결함이 있는 것입니까? 아니면 제가 무언가를 놓치고 있는 것입니까?

padding 2023. 9. 5. 19:45
반응형

비동기 작업이 정말 개념적으로 결함이 있는 것입니까? 아니면 제가 무언가를 놓치고 있는 것입니까?

저는 몇 달 동안 이 문제를 조사해왔고, 그것에 대한 다른 해결책을 생각해냈는데, 그것들은 모두 엄청난 해킹이기 때문에 만족스럽지 않습니다.저는 여전히 디자인에 결함이 있는 수업이 틀에 들어갔다는 것을 믿을 수 없고 아무도 그것에 대해 이야기하지 않습니다. 그래서 저는 제가 무언가를 놓치고 있는 것이 틀림없다고 생각합니다.

는 문는입니다.AsyncTask 문에따면그것은르서그▁according은것▁to▁the면ation▁it▁document.

"스레드 및/또는 핸들러를 조작하지 않고도 UI 스레드에서 백그라운드 작업을 수행하고 결과를 게시할 수 있습니다."

예시는 예시적인 것이 있는지 계속해서 보여줍니다.showDialog()는 메드가호니다됩출에서 됩니다.onPostExecute()그러나 대화 상자를 표시하려면 항상 유효한 대화 상자에 대한 참조가 필요하기 때문에 이것은 전적으로 제게 고안된 처럼 보입니다.Context또한 AsyncTask는 컨텍스트 개체에 대한 강력한 참조를 보유하지 않아야 합니다.

그 이유는 분명합니다. 작업을 유발한 활동이 파괴되면 어떻게 합니까?화면이 뒤집혔기 때문에 항상 발생할 수 있습니다.작업에 생성된 컨텍스트에 대한 참조가 포함되어 있으면 쓸모없는 컨텍스트 개체를 보유하고 있을 뿐만 아니라(창이 파괴되고 UI 상호 작용이 예외적으로 실패합니다!) 메모리 누수가 발생할 위험도 있습니다.

제 한,은 다음과 같이 됩니다.onPostExecute()전혀 쓸모가 없습니다. 왜냐하면 컨텍스트에 액세스할 수 없다면 UI 스레드에서 이 방법을 실행하는 것이 무슨 소용이 있습니까?당신은 여기서 의미 있는 일을 할 수 없습니다.

가지 방법은 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "Handler例사.가 있습니다에, 없이 를 교환할 수 이렇게 하면 됩니다. 처리기가 컨텍스트와 작업을 느슨하게 바인딩하므로 누출 위험 없이 두 작업 간에 메시지를 교환할 수 있습니다.그러나 이는 비동기 작업의 전제, 즉 핸들러를 번거롭게 할 필요가 없다는 것을 의미합니다.또한 동일한 스레드에서 메시지를 보내고 받기 때문에 핸들러를 남용하는 것처럼 보입니다(UI 스레드에서 메시지를 만들고 포스트에서 메시지를 보냅니다).UI 스레드에서도 실행되는 execute().

무엇보다도, 이러한 해결 방법을 사용하더라도 컨텍스트가 파괴될 때 발생한 작업에 대한 기록이 없다는 문제가 있습니다.즉, 화면 방향 변경 후와 같이 컨텍스트를 다시 만들 때 작업을 다시 시작해야 합니다.이것은 느리고 낭비적입니다.

Droid-Fu 라이브러리에서 구현된 것처럼 이에 대한 저의 해결책은 다음과 같은 매핑을 유지하는 것입니다.WeakReferences 구성 요소 이름에서 고유한 응용 프로그램 개체의 현재 인스턴스로 이동합니다.비동기 작업이 시작될 때마다 해당 맵에 호출 컨텍스트를 기록하고, 모든 콜백 시 해당 매핑에서 현재 컨텍스트 인스턴스를 가져옵니다.이렇게 하면 오래된 컨텍스트 인스턴스를 참조하지 않고 콜백의 유효한 컨텍스트에 항상 액세스할 수 있으므로 해당 컨텍스트에서 의미 있는 UI 작업을 수행할 수 있습니다.또한 참조가 약하고 지정된 구성 요소의 인스턴스가 더 이상 존재하지 않을 때 지워지기 때문에 누출되지 않습니다.

여전히 복잡한 해결 방법이며 Droid-Fu 라이브러리 클래스 중 일부를 하위 클래스로 분류해야 하기 때문에 상당히 거슬리는 접근 방식입니다.

이제 제가 알고 싶은 것은 다음과제가 무언가를 크게 놓치고 있는 것일까요, 아니면 비동기 작업이 정말로 완전히 결함이 있는 것일까요?그것을 사용한 경험은 어떻습니까?당신은 이 문제들을 어떻게 해결했습니까?

당신의 의견에 감사합니다.

다음과 같은 것은 어떻습니까?

class MyActivity extends Activity {
    Worker mWorker;

    static class Worker extends AsyncTask<URL, Integer, Long> {
        MyActivity mActivity;

        Worker(MyActivity activity) {
            mActivity = activity;
        }

        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++) {
                totalSize += Downloader.downloadFile(urls[i]);
                publishProgress((int) ((i / (float) count) * 100));
            }
            return totalSize;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            if (mActivity != null) {
                mActivity.setProgressPercent(progress[0]);
            }
        }

        @Override
        protected void onPostExecute(Long result) {
            if (mActivity != null) {
                mActivity.showDialog("Downloaded " + result + " bytes");
            }
        }
    }

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

        mWorker = (Worker)getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = this;
        }

        ...
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new Worker(this);
        mWorker.execute(...);
    }
}

그 이유는 분명합니다. 작업을 유발한 활동이 파괴되면 어떻게 합니까?

로 에활해제다니합에서 으로 연결 합니다.AsyncTaskonDestroy()으로 새 을 에 다시 연결합니다.AsyncTaskonCreate()이를 위해서는 정적 내부 클래스 또는 표준 Java 클래스와 10줄의 코드가 필요합니다.

▁like처럼 보입니다.AsyncTask개념적으로 결함이 있는 것 이상입니다.호환성 문제로 인해 사용할 수도 없습니다.Android 문서는 다음과 같습니다.

처음 도입되었을 때 비동기 작업은 단일 백그라운드 스레드에서 연속적으로 실행되었습니다.DOUNCT를 시작으로 여러 작업이 병렬로 작동할 수 있도록 스레드 풀로 변경되었습니다.허니콤을 시작으로 병렬 실행으로 인한 일반적인 응용 프로그램 오류를 방지하기 위해 작업이 단일 스레드에서 다시 실행됩니다.실행을 , 을 사용할 수 . executeOnExecutor(Executor, Params...) 방법의 입니다. THREAD_POOL_EXECUTOR단, 사용에 대한 경고는 주석을 참조하십시오.

둘다요.executeOnExecutor()그리고.THREAD_POOL_EXECUTORAPI 레벨 11(Android 3.0.x, HONEIC)에 추가되었습니다.

이 두 개의 만두약당개만면든다를을 합니다.AsyncTask두 개의 파일을 다운로드하기 위해 두 번째 다운로드는 첫 번째 다운로드가 끝날 때까지 시작되지 않습니다.두 대의 서버를 통해 채팅하고 첫 번째 서버가 다운된 경우 첫 번째 서버에 대한 연결이 한 번 끝나기 전에 두 번째 서버에 연결하지 않습니다. (물론 새로운 API11 기능을 사용하지 않는 한, 이로 인해 코드가 2.x와 호환되지 않습니다.

2.x와 3.0+를 모두 대상으로 삼으려면 매우 까다로워집니다.

또한 문서에는 다음과 같이 나와 있습니다.

주의:작업자 스레드를 사용할 때 발생할 수 있는 또 다른 문제는 런타임 구성 변경(예: 화면 방향 변경)으로 인해 작업이 예기치 않게 다시 시작되어 작업자 스레드가 손상될 수 있습니다.이러한 재시작 중 하나를 수행하는 동안 작업을 지속할 수 있는 방법과 활동이 삭제되었을 때 작업을 올바르게 취소하는 방법을 보려면 쉘프 샘플 응용 프로그램의 소스 코드를 참조하십시오.

아마도 구글을 포함한 우리 모두가 잘못 사용하고 있을 것입니다.AsyncTaskMVC의 관점에서.

활동은 컨트롤러이며, 컨트롤러는 보기보다 오래 지속될 수 있는 작업을 시작해서는 안 됩니다.즉, 활동 수명 주기에 바인딩되지 않은 클래스의 모델에서 비동기 작업을 사용해야 합니다. 활동은 순환할 때 삭제됩니다.(View의 경우 Android 등에서 파생된 클래스를 프로그래밍하지 않습니다.위젯버튼, 하지만 할 수 있어요.일반적으로 보기에 대해 수행하는 작업은 xml뿐입니다.)

즉, 비동기 작업 파생물을 활동 방법에 배치하는 것은 잘못된 것입니다.OTOH, 만약 우리가 활동에서 비동기 작업을 사용하지 않아야 한다면, 비동기 작업은 매력을 잃게 됩니다: 그것은 빠르고 쉬운 해결책으로 광고되곤 했습니다.

비동기 작업의 컨텍스트를 참조하여 메모리 누수의 위험이 있다는 것이 사실인지 잘 모르겠습니다.

일반적으로 이러한 작업을 구현하는 방법은 활동 메소드 중 하나의 범위 내에서 새 비동기 작업 인스턴스를 만드는 것입니다.따라서 활동이 파괴되면 비동기 작업이 완료되면 해당 작업에 연결할 수 없고 가비지 수집 대상이 되지 않습니까?따라서 비동기 작업 자체가 실행되지 않기 때문에 활동에 대한 참조는 중요하지 않습니다.

활동에 대한 Week Reference를 유지하는 것이 더 강력할 것입니다.

public class WeakReferenceAsyncTaskTestActivity extends Activity {
    private static final int MAX_COUNT = 100;

    private ProgressBar progressBar;

    private AsyncTaskCounter mWorker;

    @SuppressWarnings("deprecation")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task_test);

        mWorker = (AsyncTaskCounter) getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(this);
        }

        progressBar = (ProgressBar) findViewById(R.id.progressBar1);
        progressBar.setMax(MAX_COUNT);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_async_task_test, menu);
        return true;
    }

    public void onStartButtonClick(View v) {
        startWork();
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new AsyncTaskCounter(this);
        mWorker.execute();
    }

    static class AsyncTaskCounter extends AsyncTask<Void, Integer, Void> {
        WeakReference<WeakReferenceAsyncTaskTestActivity> mActivity;

        AsyncTaskCounter(WeakReferenceAsyncTaskTestActivity activity) {
            mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(activity);
        }

        private static final int SLEEP_TIME = 200;

        @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < MAX_COUNT; i++) {
                try {
                    Thread.sleep(SLEEP_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(getClass().getSimpleName(), "Progress value is " + i);
                Log.d(getClass().getSimpleName(), "getActivity is " + mActivity);
                Log.d(getClass().getSimpleName(), "this is " + this);

                publishProgress(i);
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            if (mActivity != null) {
                mActivity.get().progressBar.setProgress(values[0]);
            }
        }
    }

}

왜 그냥 오버라이드를 하지 않습니까?onPause()합니다.AsyncTask거기서?

당신이 전적으로 옳습니다. 그래서 데이터를 가져오는 활동에 비동기 작업/로더를 사용하는 것에서 벗어나려는 움직임이 탄력을 받고 있습니다.새로운 방법 중 하나는 데이터가 준비되면 콜백을 제공하는 Voli 프레임워크를 사용하는 것입니다. 이는 MVC 모델과 훨씬 더 일치합니다.발리는 Google I/O 2013에서 보급되었습니다.왜 더 많은 사람들이 이것을 알지 못하는지 모르겠습니다.

개인적으로 저는 스레드를 확장하고 콜백 인터페이스를 사용하여 UI를 업데이트합니다.FC 문제 없이 AsyncTask를 제대로 작동시킬 수 없었습니다.또한 비차단 대기열을 사용하여 실행 풀을 관리합니다.

취소해도 되는 줄 알았는데 안 되네요.

여기 그들은 그것에 대해 RTFMing:

"작업이 이미 시작된 경우 mayInterruptIfRunning 매개 변수는 작업을 중지하기 위해 이 작업을 실행하는 스레드를 중단해야 하는지 여부를 결정합니다."

그러나, 그것은 스레드가 중단될 수 있다는 것을 의미하지는 않습니다.이는 Java의 문제이지 비동기 작업의 문제가 아닙니다."

http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d

비동기 작업을 활동, 컨텍스트, 컨텍스트 래퍼 등과 더욱 긴밀하게 결합된 것으로 생각하는 것이 좋습니다.그 범위가 완전히 이해되면 더 편리해집니다.

라이프사이클에 취소 정책이 있는지 확인하여 해당 정책이 결국 가비지 수집되고 더 이상 활동에 대한 참조를 유지하지 않으며 가비지 수집이 가능한지 확인합니다.

컨텍스트에서 이동하는 동안 비동기 작업을 취소하지 않으면 메모리 누수 및 NullPointer가 발생합니다.예외적으로 간단한 대화 상자 토스트와 같은 피드백을 제공해야 하는 경우 응용프로그램 컨텍스트의 한 톤을 사용하면 NPE 문제를 방지할 수 있습니다.

비동기 작업이 모두 나쁜 것은 아니지만 예상치 못한 함정에 빠질 수 있는 마법이 분명히 많이 일어나고 있습니다.

"그것으로 작업하는 경험"과 관련하여: 모든 비동기 작업과 함께 프로세스종료할 수 있습니다. 안드로이드는 사용자가 아무 말도 하지 않도록 활동 스택을 다시 만들 것입니다.

언급URL : https://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-or-am-i-just-missing-something

반응형