【发布时间】:2014-03-08 06:38:47
【问题描述】:
我一直在尝试了解在执行后台任务时如何处理配置更改。根据我的研究,我发现最好的解决方案是将任务放在带有setRetainInstance(true) 集的无头片段中。到处都有关于这个话题的讨论,例如。 here 和 here,但我无法让其中任何一个为我工作。
我想要实现的是:
- 将我的 AsyncTask 放在单独的文件中(作为外部类)。
- 从 Headless Fragment 内部启动 AsyncTask。
- 显示进度对话框,而不是进度条。
- 将 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