ProgramingTip

AsyncTask는 활동이 파괴 된 경우에도 중지되지 않습니다.

bestdevel 2020. 11. 7. 10:20
반응형

AsyncTask는 활동이 파괴 된 경우에도 중지되지 않습니다.


활동이 생성 될 때 실행을 시작하고 백그라운드에서 작업을 수행하는 AsyncTask 개체가 있습니다 (최대 100 개의 이미지 다운로드). 모든 것이 잘 작동하지만 내가 수없는 특이한 행동이 있습니다.

예 : Android 화면의 방향이 변경되면 활동이 파괴되고 다시 생성됩니다. 그래서 onRetainNonConfigurationInstance () 메소드를 재정의하고 AsyncTask에서 실행 된 모든 다운로드 데이터를 저장합니다. 이 작업을 수행하는 목적은 방향 변경 중에 활동이 파괴 될 때마다 AsyncTask를 실행하지 않는 것이지만, 로그에서 볼 수 이전 AsyncTask가 여전히 실행 중입니다. (데이터는 저장됩니다)

활동의 onDestroy () 메소드에서 AsyncTask를 시도했지만 여전히 AsyncTask를 실행하고 있습니다.

정말 이상한 동작이며 누군가가 AsyncTask를 중지 / 취소하는 올바른 절차를 알려줄 수 있습니다. 정말 감사 할 것입니다.

감사합니다


@Romain Guy의 대답이 맞습니다. 그럼에도 불구하고 정보 보완을 추가하고 오래 실행되는 AsyncTask 및 네트워크 지향적 작업에 더 많이 사용할 수있는 라이브러리 또는 2에 대한 포인터를 제공하고 싶습니다.

AsyncTasks는 백그라운드에서 작업을 수행하도록 설계되었습니다. 그리고 예, cancel방법을 사용하여 중지 할 수 있습니다 . 인터넷에서 항목을 다운로드 할 때 IO 차단 상태 일 때 할 때 관리 하는 것이 좋습니다. 다음과 같이 다운로드를 구성해야합니다.

public void download() {
    //get the InputStream from HttpUrlConnection or any other
    //network related stuff
    while( inputStream.read(buffer) != -1 && !Thread.interrupted() ) {
      //copy data to your destination, a file for instance
    }
    //close the stream and other resources
}

Thread.interrupted플래그 사용하면 종료가 차단 io 상태를 종료하는 데 도움이됩니다. 같은 방법은 cancel메서드 호출에 더 반응합니다 .

AsyncTask 디자인 결함

그러나 AsyncTask가 너무 오래 지속되면 두 가지 다른 문제에 직면하게됩니다.

  1. AsyncTask의 결과를 얻을 수 있습니다. 사실, 네, 할 수있는 방법이 될 것입니다.
  2. AsyncTask는 잘 문서화되어 있지 않습니다. 순진하지만 마모는 작업을 구현하고 사용하면 메모리 누수가 구현하고 있습니다.

소개하고 싶은 라이브러리 인 RoboSpice백그라운드 서비스를 사용하여 다음 종류의 요청을 실행합니다. 네트워크 요청을 위해 설계되었습니다. 요청 결과의 자동 캐싱과 같은 추가 기능을 제공합니다.

AsyncTasks가 장기 실행에 나쁜 이유는 다음과 실행될 수 있습니다. 다음 추론은 RoboSpice 동기 발췌 에서 발췌 한 것입니다. RoboSpice 사용이 Android 플랫폼에서 요구 사항을 채우는 이유를 설명하는 앱입니다.

AsyncTask 및 활동 수명주기

AsyncTask는 활동 인스턴스의 수명주기를 확인합니다. 활동 내에서 AsyncTask를 시작하고 장치를 회전하면 활동이 파괴되고 새 인스턴스가 생성됩니다. 그러나 AsyncTask는 죽지 않을 것입니다. 완료 될 때까지 계속 살아갑니다.

완료되면 AsyncTask는 새 활동의 UI를 업데이트하지 않습니다. 실제로 더 이상 표시되지 않는 활동의 이전 인스턴스를 업데이트합니다. 이로 인해 java.lang.IllegalArgumentException 유형의 예외가 저장 될 수 있습니다. 예를 들어, 활동 내에서보기를 검색하기 위해 findViewById를 사용하는 경우 창 관리자에보기가 첨부되지 않은 경우.

메모리 누수 문제

AsyncTask를 활동의 내부 클래스로 만드는 것이 매우 편리합니다. AsyncTask는 작업이 완료되거나 진행 중일 때 Activity의 뷰를 조작해야 할 활동의 내부 클래스를 사용하는 것이 좋습니다. 내부 클래스는 외부 클래스의 모든 필드에 직접 액세스 할 수 있습니다.

그럼에도 불구하고 내부 클래스는 외부 클래스 인스턴스 인 활동에 대해 언급하는 참조를 보유 할 것임을 의미합니다.

장기적으로 이로 인해 메모리 누수가 발생합니다. AsyncTask가 오래 지속되면 활동이 "살아있는"상태를 유지하는 반면 Android는 더 이상 표시 할 수 없기 때문입니다. 활동은 가비지 수집 수 있습니다. Android가 기기의 리소스를 보존하는 핵심 기술 될 것입니다.

작업 진행률이 감소합니다.

몇 가지 해결 방법을 사용하여 장기 실행 관리 작업을 만들고 활동의 수명주기에 따라 수명주기를 수행 할 수 있습니다. 당신도 당신의 활동의 중지시 방법에 AsyncTask를 취소 또는 당신은 당신의 완료 작업을 완료하도록, 그리고 진행 상황을 잃어 수가 활동의 다음 인스턴스로 다시 링크 .

이것은 RobopSpice에서 어떻게 동기를 부여하는지 보여 주지만 복잡하게 코드는 일반적이지 않습니다. 또한 사용자가 활동을 그만두고 돌아 오면 작업 진행 상황을 잃게됩니다. 한 재 연결 해결 방법이있는 AsyncTask와 더 간단하지만 많은 것들이 발생합니다.

Android 서비스 사용

가장 좋은 방법은 서비스를 사용하여 장기 실행 백그라운드 작업을 실행하는 것입니다. 이것이 바로 RoboSpice가 제안한 솔루션입니다. 다시 말하지만 용으로 설계 네트워크와 관련이없는 항목 확장 할 수 있습니다. 이 라이브러리에는 많은 기능이 있습니다 .

인포 그래픽 덕분에 30 초 내 아이디어를 얻을 수도 있습니다 .


장기 실행 작업에 AsyncTasks를 사용하는 것은 정말 매우 나쁜 생각입니다. 그럼에도 불구하고 1 ~ 2 초 나중에보기를 업데이트하는 것과 같이 수명이 짧은 경우에는 괜찮습니다.

RoboSpice Motivations 앱 을 다운로드하는 것이 좋습니다 .이 은 내용을 자세히 설명하고 네트워크 관련 작업을 수행하는 다양한 방법에 대한 샘플과 견본을 제공합니다.


네트워크와 관련이없는 작업 (예 : 캐싱 없음)을 위해 RoboSpice의 대안을 찾고있는 테이프를 살펴볼 수도 있습니다 .


Romain Guy가 옳습니다. 실제로 수행 작업은 어떤 경우에도 자체 작업을 완료해야합니다. 누군가가 작업을 취소하거나 중지하기를 원하는지 확인해야합니다.

AsyncTask여러 번 루프에서 동작을 가정 해 봅시다 . 그런 다음 isCancelled()모든 루프를 확인해야 합니다.

while ( true ) {
    if ( isCancelled())
        break;
    doTheTask();
}

doTheTask() 실제 작업이며 모든 루프에서 수행하기 전에 작업을 취소해야합니다.

일반적으로 당신은 당신에 플래그를 설정해야 우리합니다 AsyncTask클래스 또는에서 적절한 결과를 반환 doInBackground()당신에, 너무 onPostExecute()당신이 당신의 작업이 중간에 취소 된 경우 당신이 원하는 또는 무엇을 완료 할 수 난 웬지 -, 당신은 확인할 수 있습니다.


다음은 문제를 해결하지 못하지만 방지합니다. 앱 매니페스트에서 다음을 수행합니다.

    <activity
        android:name=".(your activity name)"
        android:label="@string/app_name"
        android:configChanges="orientation|keyboardHidden|screenSize" > //this line here
    </activity>

추가하지 않으면 구성 변경시 변경 될 때 일부 변경을 수행 할 다음 활동을 재정의하면됩니다.

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

        //your code here
    }

활동은 방향 변경시 다시 생성됩니다. 맞습니다. 그러나이 이벤트가 보관 될 때마다 asynctask를 계속할 수 있습니다.

당신은 그것을 확인

@Override
protected void onCreate(Bundle savedInstanceState) { 
     if ( savedInstanceState == null ) {
           startAsyncTask()
     } else {
           // ** Do Nothing async task will just continue.
     }
}

-건배


로부터 MVC의 관점,은이다 활동 컨트롤러 ; ControllerView보다 오래 지속되는 작업을 수행 하는 것이 잘못된 것입니다 (android.view.View에서 파생 된 일반적으로 기존 클래스를 결합). 따라서 AsyncTasks를 시작 하는 것은 모델 의 책임 이어야합니다 .


asynctask가 된 풀에없는 경우 (병렬 처리) asynctask는 이미 CPU에서 실행 중로 실행 중지 또는 취소 명령은 CPU가 해제 실행 됨 수행됨 (cpu는 async task). 그러나 더 많은 풀에 있으면 작업이 추가되고 하나씩 실행됩니다. 명령을 실행하는 동안 작업을 실행하는 동안 작업을 중지 할 수 있습니다.


은 사용할 수 당신 있습니다 class MagicAppRestart에서 이 게시물하는 과정 죽일 모든 AsyncTasks와 함께를; Android는 활동 스택을 복원합니다 (사용자는 아무 것도보고하지 않습니다). 프로세스를 다시 시작하기 전에 유일한 알림이 호출 점에 유의해야합니다 onPause(). 에 따라 안드로이드 응용 프로그램 수명주기 , 응용 프로그램 절차 종료합니다.

나는 그것을 시도하는 것입니다. 그럼에도 불구하고 지금은 Application 클래스의 한 참조와 같은 "더 문명화 된"메서드를 사용할 계획입니다 (내 AsyncTasks는 수명이 짧고 메모리를 많이 소모하지 않기 때문에 그렇습니다).

다음은 사용할 수있는 몇 가지 코드입니다.

MagicAppRestart.java

package com.xyz;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

/** This activity shows nothing; instead, it restarts the android process */
public class MagicAppRestart extends Activity {
    // Do not forget to add it to AndroidManifest.xml
    // <activity android:name="your.package.name.MagicAppRestart"/>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.exit(0);
    }
    public static void doRestart(Activity anyActivity) {
        anyActivity.startActivity(new Intent(anyActivity.getApplicationContext(), MagicAppRestart.class));
    }
}

나머지는 com.xyz.AsyncTaskTestActivity 의 새 Android 프로젝트를 위해 Eclipse에서 만든 것입니다 .

AsyncTaskTestActivity.java

package com.xyz;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class AsyncTaskTestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d("~~~~","~~~onCreate ~~~ "+this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    public void onStartButton(View view) {
        Log.d("~~~~","~~~onStartButton {");
        class MyTask extends AsyncTask<Void, Void, Void> {

            @Override
            protected Void doInBackground(Void... params) {
                // TODO Auto-generated method stub
                Log.d("~~~~","~~~doInBackground started");
                try {
                    for (int i=0; i<10; i++) {
                        Log.d("~~~~","~~~sleep#"+i);
                        Thread.sleep(200);
                    }
                    Log.d("~~~~","~~~sleeping over");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Log.d("~~~~","~~~doInBackground ended");
                return null;
            }
            @Override
            protected void onPostExecute(Void result) {
                super.onPostExecute(result);
                taskDone();
            }
        }
        MyTask task = new MyTask();
        task.execute(null);
        Log.d("~~~~","~~~onStartButton }");
    }
    private void taskDone() {
        Log.d("~~~~","\n\n~~~taskDone ~~~ "+this+"\n\n");
    }
    public void onStopButton(View view) {
        Log.d("~~~~","~~~onStopButton {");
        MagicAppRestart.doRestart(this);
        Log.d("~~~~","~~~onStopButton }");
    }
    public void onPause() {   Log.d("~~~~","~~~onPause ~~~ "+this);   super.onPause(); }
    public void onStop() {    Log.d("~~~~","~~~onStop ~~~ "+this);    super.onPause(); }
    public void onDestroy() { Log.d("~~~~","~~~onDestroy ~~~ "+this); super.onDestroy(); }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button android:text="Start" android:onClick="onStartButton" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
    <Button android:text="Stop" android:onClick="onStopButton" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xyz"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk android:minSdkVersion="7" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AsyncTaskTestActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MagicAppRestart"/>
    </application>
</manifest>

및 로그 (관련 부분의주의 onPause이라고합니다 ) :

D/~~~~    (13667): ~~~onStartButton {
D/~~~~    (13667): ~~~onStartButton }
D/~~~~    (13667): ~~~doInBackground started
D/~~~~    (13667): ~~~sleep#0
D/~~~~    (13667): ~~~sleep#1
D/~~~~    (13667): ~~~sleep#2
D/~~~~    (13667): ~~~sleep#3
D/~~~~    (13667): ~~~sleep#4
D/~~~~    (13667): ~~~sleep#5
D/~~~~    (13667): ~~~sleep#6
D/~~~~    (13667): ~~~sleep#7
D/~~~~    (13667): ~~~sleep#8
D/~~~~    (13667): ~~~sleep#9
D/~~~~    (13667): ~~~sleeping over
D/~~~~    (13667): ~~~doInBackground ended
D/~~~~    (13667): 
D/~~~~    (13667): 
D/~~~~    (13667): ~~~taskDone ~~~ com.xyz.AsyncTaskTestActivity@40516988
D/~~~~    (13667): 




D/~~~~    (13667): ~~~onStartButton {
D/~~~~    (13667): ~~~onStartButton }
D/~~~~    (13667): ~~~doInBackground started
D/~~~~    (13667): ~~~sleep#0
D/~~~~    (13667): ~~~sleep#1
D/~~~~    (13667): ~~~sleep#2
D/~~~~    (13667): ~~~sleep#3
D/~~~~    (13667): ~~~sleep#4
D/~~~~    (13667): ~~~sleep#5
D/~~~~    (13667): ~~~onStopButton {
I/ActivityManager(   81): Starting: Intent { cmp=com.xyz/.MagicAppRestart } from pid 13667
D/~~~~    (13667): ~~~onStopButton }
D/~~~~    (13667): ~~~onPause ~~~ com.xyz.AsyncTaskTestActivity@40516988
I/ActivityManager(   81): Process com.xyz (pid 13667) has died.
I/WindowManager(   81): WIN DEATH: Window{4073ceb8 com.xyz/com.xyz.AsyncTaskTestActivity paused=false}
I/ActivityManager(   81): Start proc com.xyz for activity com.xyz/.AsyncTaskTestActivity: pid=13698 uid=10101 gids={}
I/ActivityManager(   81): Displayed com.xyz/.AsyncTaskTestActivity: +44ms (total +65ms)
D/~~~~    (13698): ~~~onCreate ~~~ com.xyz.AsyncTaskTestActivity@40517238

참고 URL : https://stackoverflow.com/questions/2531336/asynctask-wont-stop-even-when-the-activity-has-destroyed

반응형