Android AsyncTask赢得';即使活动已被破坏,也不要停止

Android AsyncTask赢得';即使活动已被破坏,也不要停止,android,android-asynctask,Android,Android Asynctask,我有一个AsyncTask对象,它在创建活动时开始执行,并在后台执行操作(最多下载100幅图像)。一切正常,但有一种特殊的行为,我无法理解 例如:当android屏幕的方向改变时,活动将被销毁并再次创建。因此,我重写了onretainonconfigurationinstance()方法,并保存在AsyncTask中执行的所有下载数据。我这样做的目的是不让AsyncTask在每次定向更改期间创建的Activity被破坏时运行,但正如我在日志中看到的那样,前面的AsyncTask仍在执行。(但数据

我有一个
AsyncTask
对象,它在创建
活动时开始执行,并在后台执行操作(最多下载100幅图像)。一切正常,但有一种特殊的行为,我无法理解

例如:当android屏幕的方向改变时,
活动将被销毁并再次创建。因此,我重写了
onretainonconfigurationinstance()
方法,并保存在
AsyncTask
中执行的所有下载数据。我这样做的目的是不让
AsyncTask
在每次定向更改期间创建的
Activity
被破坏时运行,但正如我在日志中看到的那样,前面的
AsyncTask
仍在执行。(但数据保存正确)

我甚至尝试取消活动的
ondestory()
方法中的
AsyncTask
,但日志仍显示
AsyncTask
正在运行


这是一种非常奇怪的行为,如果有人能告诉我停止/取消
异步任务的正确过程,我会非常感激。事实上,异步任务在任何情况下都负责完成自己的任务。中断不是最好的方式,所以你应该不断地检查是否有人想让你取消或停止你的任务

假设您的
AsyncTask
在循环中多次执行某些操作。然后您应该在每个循环中检查
isCancelled()

while ( true ) {
    if ( isCancelled())
        break;
    doTheTask();
}
doTheTask()
是您真正的工作,在每次循环中执行之前,您都要检查任务是否应该取消


通常你应该在你的代码> AcyCastase[/Cuth]类中设置一个标志,或者从你的代码> DONBACKEATE()/CODE中返回一个适当的结果,这样,在你的<代码> OnPixExcel()/Cube >中,你可以检查你是否可以完成你想要的,或者如果你的工作在中间被取消了。

< P> @ RoMun-Gy给出的答案是正确的。尽管如此,我还是想补充一些信息,并给出一个指向库或2的指针,它可以用于长时间运行的AsyncTask,甚至可以用于面向网络的AsyncTask

异步任务是为在后台完成任务而设计的。是的,您可以使用
cancel
方法停止它。当你从网上下载东西时,我强烈建议你。您应按以下方式组织下载:

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
}
使用
线程.interrupted
标志将帮助线程正确退出阻塞io状态。您的线程将对
cancel
方法的调用做出更大的响应

异步任务设计缺陷 但如果任务持续时间过长,则将面临两个不同的问题:

  • 活动与活动生命周期的关系很差,如果活动终止,您将无法获得任务的结果。的确,是的,你可以,但这将是一条艰难的道路
  • 异步任务没有很好的文档记录。简单但直观的asynctask实现和使用会很快导致内存泄漏
  • ,我想介绍的库使用后台服务来执行此类请求。它是为网络请求而设计的。它提供了其他功能,如自动缓存请求的结果

    以下是异步任务不适合长时间运行任务的原因。下面的理由是对Experpts的改编:该应用解释了为什么使用RoboSpice可以满足Android平台的需求

    异步任务和活动生命周期 异步任务不遵循活动实例的生命周期。如果在活动内启动异步任务并旋转设备,则该活动将被销毁并创建新实例。但这项任务不会消亡。它将继续生存,直到它完成。

    当它完成时,AsyncTask不会更新新活动的UI。实际上,它更新了活动的前一个实例 不再显示。这可能导致java.lang.IllegalArgumentException类型的异常:如果 例如,使用findViewById检索活动内部的视图。

    内存泄漏问题 将异步任务创建为活动的内部类非常方便。因为AsyncTask将需要操纵视图 当任务完成或正在进行时,使用活动的内部类似乎很方便:内部类可以 直接访问外部类的任何字段。

    然而,这意味着内部类将在其外部类实例上持有一个不可见的引用:活动。

    从长远来看,这会产生内存泄漏:如果AsyncTask持续很长时间,它会使活动保持“活动状态” 而安卓希望摆脱它,因为它不能再显示了。活动不能被垃圾收集,这是一个中心问题 Android在设备上保留资源的机制。

    您的任务进度将丢失 您可以使用一些变通方法来创建长时间运行的异步任务,并根据活动的生命周期管理其生命周期。您可以选择,也可以让异步任务完成,而不影响其进度和性能

    这是可能的,我们展示了如何在RobopSpice中实现,但它变得复杂,代码也不是真正的通用代码。此外,如果用户离开活动并返回,您仍然会失去任务的进度。加载程序也会出现同样的问题,尽管它与上面提到的重新链接解决方法的AsyncTask相比更简单

    使用Android服务 最好的选择是使用服务来执行长时间运行的后台任务。这正是机器人世界提出的解决方案。同样,它是为网络设计的,但也可以扩展到与网络无关的东西。这个图书馆有一个图书馆

    你甚至可以了解我的想法
    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));
        }
    }
    
    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(); }
    }
    
    <?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>
    
    <?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>
    
    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
    
        <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
        }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
         if ( savedInstanceState == null ) {
               startAsyncTask()
         } else {
               // ** Do Nothing async task will just continue.
         }
    }