Ich verwende eine beibehaltenDialogFragment
, die einen Fortschrittsdialog während einer TCP-Verbindung in einer AsyncTask
ausgeführt. Die AsyncTask
wird in der Methode onStart()
von fragmebt ausgeführt und entweder abgebrochen, wenn die DialogFragment
abgebrochen wird, oder in der onStop()
-Methode des Fragments.Wann stoppen Sie eine AsyncTask in einem beibehaltenen DialogFragment?
Wenn meine Aufgabe abgeschlossen ist (d. H. TCP-Verbindung ist hergestellt oder fehlgeschlagen), oder abgebrochen wird, beende ich den Dialog mit der Methode dismiss()
.
Das Problem ist, wenn ich "Home" drücke, wenn die AsyncTask ausgeführt wird. In diesem Fall wird das Fragment onStop()
Methode aufgerufen, die die AsyncTask storniert, und versuchen, den Dialog zu schließen, aber dann bekomme ich die folgende Fehlermeldung:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
Ich sah es ein Verfahren dismissAllowingStateLoss() benannt ist, die diese Ausnahme vermeiden aber ich bin mir nicht sicher, ob es die beste Lösung ist, ich möchte nicht, dass es ein Problem "versteckt".
Das erwartete Verhalten ist das folgende: Wenn der Benutzer die Aktivität verlässt, möchte ich die Verbindung abbrechen, deshalb stoppe ich es in onStop()
. Hier
ist der Fragment-Code als Referenz:
public class TcpConnectionFragment extends DialogFragment {
private static final Logger logger = LoggerFactory.getLogger(TcpConnectionFragment.class);
private TcpConnectionTask connectionTask;
private String host;
private int port;
public static TcpConnectionFragment newInstance(String host, int port) {
Bundle bundle = new Bundle();
bundle.putString("ADDRESS", host);
bundle.putInt("PORT", port);
TcpConnectionFragment fragment = new TcpConnectionFragment();
fragment.setArguments(bundle);
return fragment;
}
private void startAsyncTask() {
logger.info("Starting asynchronous connection task");
connectionTask = new TcpConnectionTask(this, host, port);
connectionTask.execute();
}
private void stopAsyncTask() {
if (connectionTask == null)
return;
logger.info("Stopping asynchronous connection task");
connectionTask.cancel(false);
try {
SocketChannel channel = connectionTask.getSocketChannel();
if (channel != null)
channel.close();
} catch (IOException ignored) {
// We don't care about this error here, we can't recover.
}
}
public TcpConnectionFragment() {
super();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (getArguments() == null)
throw new IllegalArgumentException("Missing arguments");
host = getArguments().getString("ADDRESS");
port = getArguments().getInt("PORT");
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new MaterialDialog.Builder(getContext())
.title(R.string.tcp_connect_dialog_title)
.content(R.string.tcp_connect_dialog_text, host, port)
.progress(true, 0)
.build();
}
@Override
public void onStart() {
super.onStart();
startAsyncTask();
}
@Override
public void onStop() {
super.onStop();
stopAsyncTask();
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
logger.info("Cancelled dialog.");
stopAsyncTask();
}
/**
* This override is required to avoid a bug in the support library.
*/
@Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
/**
* Called by the {@link TcpConnectionTask} once connected.
*
* @param transport The transport created during the conenction.
*/
void onConnected(Transport transport) {
connectionTask = null;
dismiss();
}
/**
* Called by the {@link TcpConnectionTask} once the connection is cancelled.
*/
void onConnectionCancelled() {
connectionTask = null;
dismiss();
}
/*
* Called by the {@link TcpConnectionTask} if an error occurs during the connection.
*/
void onConnectionError(Exception error) {
connectionTask = null;
dismiss();
if (getParentFragment() == null || getParentFragment().getView() == null)
return;
Snackbar.make(getParentFragment().getView().findViewById(R.id.frame_layout),
"Error: " + error.getMessage(), Snackbar.LENGTH_LONG).show();
}
}
Derzeit nennen I() von AsyncTask Rückrufe entlassen, was bedeutet, I zu entlassen(), um den Dialog, wenn te Verbindung wurde hergestellt, abgebrochen oder ein Fehler ist aufgetreten. Wenn ich es in onPause() tue, wird es für den Pausenfall funktionieren, aber ich könnte immer noch Probleme haben, wenn die Aufgabe zum gleichen Zeitpunkt beendet wird, wenn der Benutzer Home drückt, nein? –
In diesem Fall würde ich ein Abbruchobjekt übergeben, das innerhalb der asynctask abgebrochen werden kann, so dass es möglich ist, etwas zu bereinigen, falls das Fragment stoppt. Ich benutze Bolts Tasks, das ist eine großartige Bibliothek zum Ausführen von asynchronen Aufgaben mit Abbruchobjekten. Überprüfen Sie es hier, wenn Sie es noch nicht wissen: https://github.com/BoltsFramework/Bolts-Android – miqueloi
Es gibt keine Möglichkeit zu überprüfen, ob das Fragment in einem "gültigen" Zustand im Callback ist? Um zu überprüfen, ob kill() sicher aufgerufen werden kann? Wenn ich es vermeiden kann, eine riesige Bibliothek zu benutzen, wäre es gut :-) –