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