Интересно, что ни один из ответов на этой странице не упоминает два крайних случая, надеюсь, никто не возражает, если я их добавлю:
Родовые словари в .NET не являются потокобезопасными, а иногда могут бросать NullReference
или даже (чаще) a KeyNotFoundException
при попытке получить доступ к ключу из двух параллельных потоков. Исключение в этом случае является довольно ошибочным.
Если код NullReferenceException
задан кодом unsafe
, вы можете посмотреть на переменные указателя , и проверьте их на IntPtr.Zero
или что-то в этом роде. Это одно и то же («исключение нулевого указателя»), но в небезопасном коде переменные часто переводятся в типы значений / массивы и т. Д., И вы ударяете головой о стену, задаваясь вопросом, как тип значения может исключение.
(Еще одна причина для небезопасного использования небезопасного кода, если вам это нужно)
НАЙДЕНО РЕШЕНИЕ: Я добавил слушателя действий перед загрузкойDialog.show () следующим образом:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
Таким образом, когда я нажимаю кнопку «Назад», вышеуказанный OnCancelListener отменяет оба диалога и задачу. Также вы можете добавить финиш (), если вы хотите закончить всю операцию по нажатой клавише. Не забудьте объявить свою задачу async как переменную вроде этого:
MyAsyncTask myTask=null;
и выполнить вашу задачу async следующим образом:
myTask = new MyAsyncTask();
myTask.execute();
У меня была аналогичная проблема - по сути, я получал NPE в асинхронной задаче после того, как пользователь уничтожил действие. После изучения проблемы с переполнением стека я принял следующее решение:
volatile boolean running;
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
running=true;
...
}
public void onDestroy() {
super.onDestroy();
running=false;
...
}
Затем я периодически проверяю «если работает» в моем асинхронном коде. Я испытал это, и теперь я не могу «сломать» свою деятельность. Это прекрасно работает и имеет преимущество в том, чтобы быть проще, чем некоторые из решений, которые я видел на SO.
Вы можете просто попросить об отмене, но на самом деле не прекратите его. См. Этот ответ .
создать некоторые переменные-члены в вашей деятельности, такие как
YourAsyncTask mTask;
Dialog mDialog;
использовать их для вашего диалога и задачи;
в onPause () просто вызвать
if(mTask!=null) mTask.cancel();
if(mDialog!=null) mDialog.dismiss();
Из SDK:
Отмена задачи
Задачу можно отменить в любое время, вызвав cancel (boolean). Вызов этого метода приведет к последующим вызовам isCancelled (), чтобы вернуть true. После вызова этого метода onCancelled (Object) вместо onPostExecute (Object) будет вызываться после того, как doInBackground (Object []) вернется. Чтобы гарантировать, что задача будет отменена как можно быстрее, вы всегда должны проверять возвращаемое значение isCancelled () периодически из doInBackground (Object []), если это возможно (внутри цикла, например.)
blockquote>Итак, ваш код подходит для прослушивателя диалога:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { myTask.cancel(true); //finish(); } });
Теперь, как я уже упоминал ранее из SDK, вам нужно проверить, отменена ли задача или нет, для этого вам нужно проверить isCancelled () внутри метода onPreExecute ().
Например:
if (isCancelled()) break; else { // do your work here }
Я потратил некоторое время на то, чтобы понять это, все, что я хотел, было простым примером того, как это сделать, поэтому я подумал, что опубликую, как я это сделал. Это некоторый код, который обновляет библиотеку и имеет диалоговое окно прогресса, показывающее, сколько книг было обновлено и отменяется, когда пользователь отклоняет диалог:
private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
private ProgressDialog dialog = new ProgressDialog(Library.this);
private int total = Library.instance.appState.getAvailableText().length;
private int count = 0;
//Used as handler to cancel task if back button is pressed
private AsyncTask<Void, Integer, Boolean> updateTask = null;
@Override
protected void onPreExecute(){
updateTask = this;
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
updateTask.cancel(true);
}
});
dialog.setMessage("Updating Library...");
dialog.setMax(total);
dialog.show();
}
@Override
protected Boolean doInBackground(Void... arg0) {
for (int i = 0; i < appState.getAvailableText().length;i++){
if(isCancelled()){
break;
}
//Do your updating stuff here
}
}
@Override
protected void onProgressUpdate(Integer... progress){
count += progress[0];
dialog.setProgress(count);
}
@Override
protected void onPostExecute(Boolean finished){
dialog.dismiss();
if (finished)
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
else
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
}
}
Я хотел бы улучшить код. Когда вы нажимаете aSyncTask
, автоматически вызывается onCancelled()
(метод обратного вызова aSyncTask
), и вы можете скрыть свой progressBarDialog
.
Вы также можете включить этот код:
public class information extends AsyncTask<String, String, String>
{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... arg0) {
return null;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
this.cancel(true);
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
dialog.hide(); /*hide the progressbar dialog here...*/
super.onCancelled();
}
}
В большинстве случаев, когда я использую AsyncTask, моя бизнес-логика находится в отдельном бизнес-классе, а не в пользовательском интерфейсе. В этом случае у меня не было бы цикла на doInBackground (). Примером может служить процесс синхронизации, который потребляет службы и сохраняет данные один за другим.
В конечном итоге я передаю свою задачу бизнес-объекту, чтобы он мог обрабатывать отмену. Моя настройка такова:
public abstract class MyActivity extends Activity {
private Task mTask;
private Business mBusiness;
public void startTask() {
if (mTask != null) {
mTask.cancel(true);
}
mTask = new mTask();
mTask.execute();
}
}
protected class Task extends AsyncTask<Void, Void, Boolean> {
@Override
protected void onCancelled() {
super.onCancelled();
mTask.cancel(true);
// ask if user wants to try again
}
@Override
protected Boolean doInBackground(Void... params) {
return mBusiness.synchronize(this);
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
mTask = null;
if (result) {
// done!
}
else {
// ask if user wants to try again
}
}
}
public class Business {
public boolean synchronize(AsyncTask<?, ?, ?> task) {
boolean response = false;
response = loadStuff(task);
if (response)
response = loadMoreStuff(task);
return response;
}
private boolean loadStuff(AsyncTask<?, ?, ?> task) {
if (task != null && task.isCancelled()) return false;
// load stuff
return true;
}
}