我阅读了很多关于如何保存实例状态以及如何处理屏幕旋转时活动被销毁的内容。
看起来有很多可能性,但我还没有弄清楚哪种方法最适合检索AsyncTask的结果。
我有一些AsyncTasks只是简单地重新启动并调用活动的isFinishing()
方法,如果活动正在完成,它们就不会更新任何内容。
问题在于,我有一个任务要求向Web服务发出请求,这可能成功或失败,重新启动任务将导致用户财务损失。
你会如何解决这个问题?可能的解决方案有什么优缺点?
我阅读了很多关于如何保存实例状态以及如何处理屏幕旋转时活动被销毁的内容。
看起来有很多可能性,但我还没有弄清楚哪种方法最适合检索AsyncTask的结果。
我有一些AsyncTasks只是简单地重新启动并调用活动的isFinishing()
方法,如果活动正在完成,它们就不会更新任何内容。
问题在于,我有一个任务要求向Web服务发出请求,这可能成功或失败,重新启动任务将导致用户财务损失。
你会如何解决这个问题?可能的解决方案有什么优缺点?
你可以在code.google.com/p/shelves查看我如何处理AsyncTask
和屏幕方向变化。有许多方法可以做到这一点,但我选择在此应用中取消任何当前正在运行的任务,保存其状态并在创建新的Activity
时使用保存的状态启动一个新任务。这很容易做到,它运行良好,而且作为一个额外的奖励,它可以在用户离开应用程序时停止您的任务。
你也可以使用onRetainNonConfigurationInstance()
将你的AsyncTask
传递给新的Activity
(但要注意不要通过这种方式泄漏先前的Activity
)。
android:configChanges="keyboardHidden|orientation"
东西不够用。
考虑当用户在您的AsyncTask运行时接收电话的情况。您的请求已经由服务器处理,因此AsyncTask正在等待响应。此时,您的应用程序进入后台,因为电话应用刚刚出现在前台。操作系统可能会杀死您的活动,因为它在后台运行。
我的第一条建议是确保您实际上需要在屏幕旋转时重置活动(默认行为)。每当我遇到旋转问题时,我都会将此属性添加到AndroidManifest.xml文件中的<activity>
标记中,并且一切正常。
android:configChanges="keyboardHidden|orientation"
这看起来很奇怪,但它实际上是将控制权交给你的onConfigurationChanged()
方法。如果您没有提供此方法,它只会重新测量布局,这似乎是处理旋转的完全足够的方式。
Application
实例有自己的生命周期 - 它也可能被操作系统杀死,因此这种解决方案可能会导致难以复现的错误。 - Vit KhudenkoonRetainNonConfigurationInstance
来存储asynctask,以将其从当前Activity对象解耦,并在方向更改后将其绑定到新的Activity对象上。这里有一个非常好的例子,介绍了如何使用AsyncTask和ProgressDialog:这里。安卓:后台处理 / 异步操作与配置更改
为了在后台进程中维护异步操作的状态,您可以借助片段。
请参照以下步骤:
第一步:创建一个没有标题的片段,例如后台任务,并在其中添加一个私有异步任务类。
第二步(可选步骤):如果您想在活动顶部放置一个加载游标,请使用以下代码:
第三步:在主活动中实现步骤 1 中定义的 BackgroundTaskCallbacks 接口。
class BackgroundTask extends Fragment {
public BackgroundTask() {
}
// Add a static interface
static interface BackgroundTaskCallbacks {
void onPreExecute();
void onCancelled();
void onPostExecute();
void doInBackground();
}
private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();
/**
* Start the async operation.
*/
public void start() {
Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
if (!isRunning) {
asyncOperation = new PerformAsyncOpeation();
asyncOperation.execute();
isRunning = true;
}
Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}
/**
* Cancel the background task.
*/
public void cancel() {
Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
if (isRunning) {
asyncOperation.cancel(false);
asyncOperation = null;
isRunning = false;
}
Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}
/**
* Returns the current state of the background task.
*/
public boolean isRunning() {
return isRunning;
}
/**
* Android passes us a reference to the newly created Activity by calling
* this method after each configuration change.
*/
public void onAttach(Activity activity) {
Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
super.onAttach(activity);
if (!(activity instanceof BackgroundTaskCallbacks)) {
throw new IllegalStateException(
"Activity must implement the LoginCallbacks interface.");
}
// Hold a reference to the parent Activity so we can report back the
// task's
// current progress and results.
callbacks = (BackgroundTaskCallbacks) activity;
Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}
public void onDetach() {
super.onDetach();
callbacks = null;
}
private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
if (callbacks != null) {
callbacks.onPreExecute();
}
isRunning = true;
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
}
protected Void doInBackground(Void... params) {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
if (callbacks != null) {
callbacks.doInBackground();
}
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
return null;
}
protected void onCancelled() {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
if (callbacks != null) {
callbacks.onCancelled();
}
isRunning = false;
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
}
protected void onPostExecute(Void ignore) {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
if (callbacks != null) {
callbacks.onPostExecute();
}
isRunning = false;
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
}
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
public void onStart() {
super.onStart();
}
public void onResume() {
super.onResume();
}
public void onPause() {
super.onPause();
}
public void onStop() {
super.onStop();
}
public class ProgressIndicator extends Dialog {
public ProgressIndicator(Context context, int theme) {
super(context, theme);
}
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.progress_indicator);
this.setCancelable(false);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}
@Override
public void show() {
super.show();
}
@Override
public void dismiss() {
super.dismiss();
}
@Override
public void cancel() {
super.cancel();
}
public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{
private static final String KEY_CURRENT_PROGRESS = "current_progress";
ProgressIndicator progressIndicator = null;
private final static String TAG = MyActivity.class.getSimpleName();
private BackgroundTask task = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(//"set your layout here");
initialize your views and widget here .............
FragmentManager fm = getSupportFragmentManager();
task = (BackgroundTask) fm.findFragmentByTag("login");
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (task == null) {
task = new BackgroundTask();
fm.beginTransaction().add(task, "login").commit();
}
// Restore saved state
if (savedInstanceState != null) {
Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
+ task.isRunning());
if (task.isRunning()) {
progressIndicator = new ProgressIndicator(this,
R.style.TransparentDialog);
if (progressIndicator != null) {
progressIndicator.show();
}
}
}
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// save the current state of your operation here by saying this
super.onSaveInstanceState(outState);
Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
+ task.isRunning());
outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
}
progressIndicator = null;
}
private void performOperation() {
if (!task.isRunning() && progressIndicator == null) {
progressIndicator = new ProgressIndicator(this,
R.style.TransparentDialog);
progressIndicator.show();
}
if (task.isRunning()) {
task.cancel();
} else {
task.start();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
}
progressIndicator = null;
}
@Override
public void onPreExecute() {
Log.i(TAG, "CALLING ON PRE EXECUTE");
}
@Override
public void onCancelled() {
Log.i(TAG, "CALLING ON CANCELLED");
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
}
public void onPostExecute() {
Log.i(TAG, "CALLING ON POST EXECUTE");
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
progressIndicator = null;
}
}
@Override
public void doInBackground() {
// put your code here for background operation
}
}
onPostExecute
中使用 LocalBroadcastManager
。LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));
您还需要确保在活动暂停时发送广播时,活动能够正确处理该情况。
setRetainInstance(true)
的实际作用的信息,可能会有所帮助。 - Timmmm