【问题标题】:Handling configuration changes while using AsyncTask with fragments在将 AsyncTask 与片段一起使用时处理配置更改
【发布时间】:2014-03-08 06:38:47
【问题描述】:

我一直在尝试了解在执行后台任务时如何处理配置更改。根据我的研究,我发现最好的解决方案是将任务放在带有setRetainInstance(true) 集的无头片段中。到处都有关于这个话题的讨论,例如。 herehere,但我无法让其中任何一个为我工作。

我想要实现的是:

  1. 将我的 AsyncTask 放在单独的文件中(作为外部类)。
  2. 从 Headless Fragment 内部启动 AsyncTask。
  3. 显示进度对话框,而不是进度条。
  4. 将 AsyncTask 的结果返回给父 Activity。

所以我选择了这个tutorial,它基本上做同样的事情,点击按钮它会运行一些虚拟任务并用进度条显示进度。但是将 AsyncTask 作为内部类。 我试图将 Asynctask 移动到外部类,当我尝试启动后台任务时应用程序崩溃了。

代码如下:

MainActivity(父活动)

 public class MainActivity extends FragmentActivity implements TaskCallbacks {
 private static final String TAG = MainActivity.class.getSimpleName();

 private static final String KEY_CURRENT_PROGRESS = "current_progress";
 private static final String KEY_PERCENT_PROGRESS = "percent_progress";

 private TaskFragment mTaskFragment;
 private ProgressBar mProgressBar;
 private TextView mPercent;
 private Button mButton;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 Log.i(TAG, "onCreate(Bundle)");
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);

// Initialize views
mProgressBar = (ProgressBar) findViewById(R.id.progress_horizontal);
mPercent = (TextView) findViewById(R.id.percent_progress);
mButton = (Button) findViewById(R.id.task_button);
mButton.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    if (mTaskFragment.isRunning()) {
      mTaskFragment.cancel();
    } else {
      mTaskFragment.start();
    }
  }
});

// Restore saved state
if (savedInstanceState != null) {
  mProgressBar.setProgress(savedInstanceState.getInt(KEY_CURRENT_PROGRESS));
  mPercent.setText(savedInstanceState.getString(KEY_PERCENT_PROGRESS));
}

FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag("task");

// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (mTaskFragment == null) {
  mTaskFragment = new TaskFragment();
  fm.beginTransaction().add(mTaskFragment, "task").commit();
}

if (mTaskFragment.isRunning()) {
  mButton.setText(getString(R.string.cancel));
} else {
  mButton.setText(getString(R.string.start));
}
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_CURRENT_PROGRESS, mProgressBar.getProgress());
outState.putString(KEY_PERCENT_PROGRESS, mPercent.getText().toString());
}

/****************************/
/***** CALLBACK METHODS *****/
/****************************/

@Override
public void onPreExecute() {
Log.i(TAG, "onPreExecute()");
mButton.setText(getString(R.string.cancel));
Toast.makeText(this, R.string.task_started_msg, Toast.LENGTH_SHORT).show();
}

@Override
public void onProgressUpdate(int percent) {
Log.i(TAG, "onProgressUpdate(" + percent + "%)");
mProgressBar.setProgress(percent * mProgressBar.getMax() / 100);
mPercent.setText(percent + "%");
}

@Override
public void onCancelled() {
Log.i(TAG, "onCancelled()");
mButton.setText(getString(R.string.start));
mProgressBar.setProgress(0);
mPercent.setText(getString(R.string.zero_percent));
Toast.makeText(this, R.string.task_cancelled_msg, Toast.LENGTH_SHORT).show();
}

@Override
public void onPostExecute() {
Log.i(TAG, "onPostExecute()");
mButton.setText(getString(R.string.start));
mProgressBar.setProgress(mProgressBar.getMax());
mPercent.setText(getString(R.string.one_hundred_percent));
Toast.makeText(this, R.string.task_complete_msg, Toast.LENGTH_SHORT).show();
}

无头碎片

public class TaskFragment extends Fragment {
public static final String TAG = TaskFragment.class.getSimpleName();

/**
* Callback interface through which the fragment can report the task's
* progress and results back to the Activity.
*/
 static interface TaskCallbacks {
public void onPreExecute();
public void onProgressUpdate(int percent);
public void onCancelled();
public void onPostExecute();
}

public TaskCallbacks mCallbacks;
public DummyTask mTask;
public boolean mRunning;

/**
* Android passes us a reference to the newly created Activity by calling this
* method after each configuration change.
*/
@Override
public void onAttach(Activity activity) {
Log.i(TAG, "onAttach(Activity)");
super.onAttach(activity);
if (!(activity instanceof TaskCallbacks)) {
  throw new IllegalStateException("Activity must implement the TaskCallbacks interface.");
}

// Hold a reference to the parent Activity so we can report back the task's
// current progress and results.
mCallbacks = (TaskCallbacks) activity;
}

/**
* This method is called only once when the Fragment is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

/**
* This method is <em>not</em> called when the Fragment is being retained
* across Activity instances.
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
super.onDestroy();
cancel();
}

/*****************************/
/***** TASK FRAGMENT API *****/
/*****************************/

/**
* Start the background task.
*/
public void start() {
if (!mRunning) {
  mTask = new DummyTask();
  mTask.execute();
  mRunning = true;
}
}

/**
 * Cancel the background task.
 */
public void cancel() {
if (mRunning) {
  mTask.cancel(false);
  mTask = null;
  mRunning = false;
 }
 }

 /**
 * Returns the current state of the background task.
 */
 public boolean isRunning() {
 return mRunning;
 }


 }

我的后台任务

 public class DummyTask extends AsyncTask<Void, Integer, Void> {

TaskCallbacks callbacks;


 @Override
 protected void onPreExecute() {
 // Proxy the call to the Activity
 callbacks.onPreExecute();
 //fragment.mRunning = true;
 }

 @Override
 protected Void doInBackground(Void... ignore) {
 for (int i = 0; !isCancelled() && i < 100; i++) {

  //Log.i(TAG, "publishProgress(" + i + "%)");
  SystemClock.sleep(100);
  publishProgress(i);
 }
 return null;
 }

 @Override
 protected void onProgressUpdate(Integer... percent) {
 // Proxy the call to the Activity
 callbacks.onProgressUpdate(percent[0]);
 }

 @Override
 protected void onCancelled() {
 // Proxy the call to the Activity
 callbacks.onCancelled();
 //fragment.mRunning = false;
 }

 @Override
 protected void onPostExecute(Void ignore) {
 // Proxy the call to the Activity
 callbacks.onPostExecute();
 //fragment.mRunning = false;
 }
 }       

我的日志猫

 03-08 06:41:51.517: E/AndroidRuntime(20497): FATAL EXCEPTION: main
 03-08 06:41:51.517: E/AndroidRuntime(20497): java.lang.NullPointerException
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at com.adp.retaintask.DummyTask.onPreExecute(DummyTask.java:22)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.os.AsyncTask.execute(AsyncTask.java:534)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at com.adp.retaintask.TaskFragment.start(TaskFragment.java:73)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at com.adp.retaintask.MainActivity$1.onClick(MainActivity.java:51)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.view.View.performClick(View.java:4102)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.view.View$PerformClick.run(View.java:17085)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.os.Handler.handleCallback(Handler.java:615)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at  android.os.Handler.dispatchMessage(Handler.java:92)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.os.Looper.loop(Looper.java:155)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at android.app.ActivityThread.main(ActivityThread.java:5454)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at java.lang.reflect.Method.invokeNative(Native Method)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at java.lang.reflect.Method.invoke(Method.java:511)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at   com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796)
 03-08 06:41:51.517: E/AndroidRuntime(20497):   at dalvik.system.NativeStart.main(Native Method)

谢谢。

【问题讨论】:

    标签: android android-fragments android-asynctask


    【解决方案1】:

    您需要将回调和片段传递给 DummyTask。

    /**
    * Start the background task.
    */
    public void start() {
    if (!mRunning) {
      mTask = new DummyTask(this, mCallbacks);
      mTask.execute();
      mRunning = true;
    }
    

    并在 DummyTask 中添加一个构造函数

    public DummyTask (TaskFragment fragment, TaskCallbacks callbacks) {
      this.framgent = fragment;
      this.callbacks = callbacks;
    } 
    

    然后你可以把 //fragment.mRunning = true; 东西回来了,它应该可以按预期工作。

    或者,您可以将 DummyTask 作为子类添加到 TaskFragment 并使用成员变量(然后可以访问)

    【讨论】:

    • 它有效。谢谢。但是在配置更改之后,进度条就挂了。
    猜你喜欢
    • 1970-01-01
    • 2019-01-25
    • 2017-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多