android 안드로이드 - 활동이 파괴 된 경우에도 AsyncTask가 중지되지 않습니다.




4 Answers

@Romain Guy가 준 답변이 맞습니다. 그럼에도 불구하고, 정보를 보완하고 장기 실행 AsyncTask 및 네트워크 지향 asynctasks에 사용할 수있는 라이브러리 또는 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는 잘 설명되어 있지 않습니다. 직관적 인 asynctask의 구현과 사용은 순진하지만 메모리 누출을 초래할 수 있습니다.

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

AsyncTasks가 장기 실행 작업에 좋지 않은 이유는 다음과 같습니다. 다음 추론은 RoboSpice 동기 부여에 대한 적용입니다. RoboSpice를 사용하면 Android 플랫폼에서 필요를 채우는 이유가 설명됩니다.

비동기 태스크 및 활동 라이프 사이클

AsyncTasks는 Activity 인스턴스의 라이프 사이클을 따르지 않습니다. 액티비티 내에서 AsyncTask를 시작하고 장치를 회전하면 액티비티가 삭제되고 새 인스턴스가 생성됩니다. 그러나 AsyncTask는 죽지 않을 것입니다. 그것은 완료 될 때까지 계속 살 것입니다.

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

메모리 누출 문제

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

그럼에도 불구하고 내부 클래스가 외부 클래스 인스턴스 인 활동에 대한 보이지 않는 참조를 보유한다는 것을 의미합니다.

장기적으로 메모리 누수가 발생합니다. AsyncTask가 오래 지속되면 활동이 "활성"상태를 유지하지만 안드로이드는 더 이상 표시 할 수 없으므로 제거하려고합니다. 액티비티는 가비지 수집 될 수 없으며 Android에서 장치의 리소스를 보존하기위한 핵심 메커니즘입니다.

진행 상황이 사라집니다.

몇 가지 해결 방법을 사용하여 장기 실행 asynctask를 작성하고 활동의 라이프 사이클에 따라 라이프 사이클을 관리 할 수 ​​있습니다. 액티비티의 onStop 메소드에서 AsyncTask를 취소 하거나 비동기 태스크가 완료되도록하고 진행 상황을 느슨하게하고 액티비티의 다음 인스턴스로 다시 연결할 수 있습니다 .

이것은 가능하며 우리는 RobopSpice의 동기를 보여 주지만 복잡해지며 코드가 실제로 일반적이지 않습니다. 또한 사용자가 작업을 중단하고 다시 돌아 오는 경우에도 작업 진행 상태가 느슨해집니다. 위와 같은 문제는 AsyncTask와 비슷한 간단한 것이지만 Loaders에서 나타납니다.

Android 서비스 사용

가장 좋은 방법은 서비스를 사용하여 장기간 백그라운드 작업을 실행하는 것입니다. 이것이 바로 RoboSpice가 제안한 해결책입니다. 다시 말하지만, 그것은 네트워킹을 위해 설계되었지만 네트워크가 아닌 것들까지 확장 될 수 있습니다. 이 라이브러리에는 많은 기능이 있습니다 .

심지어 infographics 덕분에 30 초 이내에 아이디어를 얻을 수 있습니다.

장기 실행 작업을 위해 AsyncTasks를 사용하는 것은 매우 나쁜 생각입니다. 그럼에도 불구하고 1 ~ 2 초가 지나면보기를 업데이트하는 것과 같이 짧은 생존자에게는 괜찮습니다.

RoboSpice Motivations 응용 프로그램 을 다운로드하는 것이 좋습니다. 실제로이 내용을 자세히 설명하고 샘플과 데모를 통해 네트워크 관련 작업을 수행 할 수 있습니다.

RoboSpice의 대안이 아닌 네트워크 관련 작업 (예 : 캐싱없이)을 찾고 있다면 Tape 살펴볼 수도 있습니다.

죽이기 wait

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

예를 들어 : 안드로이드 화면의 방향이 바뀌면 활동이 파괴되고 다시 생성됩니다. 따라서 onRetainNonConfigurationInstance () 메서드를 재정의하고 AsyncTask에서 실행되는 다운로드 한 모든 데이터를 저장합니다. 이 작업을 수행하는 나의 목적은 활동이 삭제 될 때마다 AsyncTask를 실행하지 않도록하는 것입니다. 그러나 오리엔테이션 변경 중에 작성되지만 이전 AsyncTask가 여전히 실행 중임을 로그에서 확인할 수 있습니다. (데이터는 올바르게 저장됩니다.)

액티비티의 onDestroy () 메소드에서 AsyncTask를 취소하려고 시도했지만 로그에 AsyncTask가 실행중인 것으로 표시됩니다.

이것은 정말 이상한 행동이며 누군가가 나에게 AsyncTask를 중지 / 취소하는 올바른 절차를 말할 수 있다면 정말로 감사 할 것입니다.

감사




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

    <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
    }



MVC 관점에서 Activity는 Controller입니다 . ControllerView 보다 오래 걸리는 작업 (android.view.View에서 파생 됨, 대개 기존 클래스를 재사용)을 수행하는 것은 잘못된 것입니다. 따라서 AsyncTasks를 시작하는 것은 모델 의 책임이어야합니다.




이 게시물의 class MagicAppRestart 를 사용하여 모든 AsyncTasks와 함께 프로세스class MagicAppRestart 할 수 있습니다. 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 만 호출 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 ~~~ [email protected]
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 ~~~ [email protected]
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 ~~~ [email protected]



Related

android android-asynctask