Ich habe eine kleine Test-App erstellt, die mein Problem darstellt. Ich verwende ActionBarSherlock, um Registerkarten mit (Sherlock-) Fragmenten zu implementieren.
Mein Code:
TestActivity.java
public class TestActivity extends SherlockFragmentActivity {
private ActionBar actionBar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupTabs(savedInstanceState);
}
private void setupTabs(Bundle savedInstanceState) {
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
addTab1();
addTab2();
}
private void addTab1() {
Tab tab1 = actionBar.newTab();
tab1.setTag("1");
String tabText = "1";
tab1.setText(tabText);
tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "1", MyFragment.class));
actionBar.addTab(tab1);
}
private void addTab2() {
Tab tab1 = actionBar.newTab();
tab1.setTag("2");
String tabText = "2";
tab1.setText(tabText);
tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "2", MyFragment.class));
actionBar.addTab(tab1);
}
}
TabListener.java
public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
private final SherlockFragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
/* The following are each of the ActionBar.TabListener callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
// Check if the fragment is already initialized
if (preInitializedFragment == null) {
// If not, instantiate and add it to the activity
SherlockFragment mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(preInitializedFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
if (preInitializedFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(preInitializedFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// User selected the already selected tab. Usually do nothing.
}
}
MyFragment.java
public class MyFragment extends SherlockFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
return null;
}
@Override
protected void onPostExecute(Void result){
getResources().getString(R.string.app_name);
}
}.execute();
}
}
Ich habe den Thread.sleep
Teil hinzugefügt , um das Herunterladen von Daten zu simulieren. Der Code in der onPostExecute
soll die Verwendung der simulieren Fragment
.
Wenn ich den Bildschirm sehr schnell zwischen Quer- und Hochformat drehe, wird beim onPostExecute
Code eine Ausnahme angezeigt :
java.lang.IllegalStateException: Fragment MyFragment {410f6060} nicht an Aktivität angehängt
Ich denke, das liegt daran, dass MyFragment
in der Zwischenzeit eine neue erstellt wurde, die vor der AsyncTask
Fertigstellung an die Aktivität angehängt wurde. Der Code in onPostExecute
ruft einen nicht angehängten auf MyFragment
.
Aber wie kann ich das beheben?
quelle
mView = inflater.inflate(R.layout.my_layout, container, false)
Verwenden Sie diese Ansicht jetzt, wenn Sie Ressourcen abrufen möchten :mView.getResources().***
. Es hilft mir, diesen Fehler zu beheben.Context
, was an dein` mView` angehängt ist.mView
in onDestroy?Antworten:
Ich habe die sehr einfache Antwort gefunden:
isAdded()
:Um zu vermeiden,
onPostExecute
dass Sie angerufen werden, wenn dasFragment
nicht an das angeschlossenActivity
ist, müssen Sie dasAsyncTask
beim Anhalten oder Stoppen des abbrechenFragment
. DannisAdded()
wäre das nicht mehr nötig. Es ist jedoch ratsam, diese Überprüfung beizubehalten.quelle
isDetached()
, das auf API-Level 13 hinzugefügt wurdeDas Problem ist, dass Sie versuchen, mit getResources (). GetString () auf Ressourcen (in diesem Fall Zeichenfolgen) zuzugreifen, um die Ressourcen aus der Aktivität abzurufen. Siehe diesen Quellcode der Fragment-Klasse:
mHost
ist das Objekt, das Ihre Aktivität enthält.Da die Aktivität möglicherweise nicht angehängt ist, löst Ihr Aufruf von getResources () eine Ausnahme aus.
Die akzeptierte Lösung ist meiner Meinung nach nicht der richtige Weg, da Sie nur das Problem verstecken. Der richtige Weg besteht darin, die Ressourcen von einem anderen Ort abzurufen, von dem immer garantiert wird, dass er vorhanden ist, wie z. B. dem Anwendungskontext:
quelle
getString()
als mein Fragment angehalten wurde. DankeIch habe hier zwei verschiedene Szenarien gesehen:
1) Wenn ich möchte, dass die asynchrone Aufgabe trotzdem beendet wird: Stellen Sie sich vor, mein onPostExecute speichert empfangene Daten und ruft dann einen Listener auf, um Ansichten zu aktualisieren. Um effizienter zu sein, möchte ich, dass die Aufgabe trotzdem beendet wird, damit ich die Daten bereit habe, wenn der Benutzer kommt zurück. In diesem Fall mache ich normalerweise Folgendes:
2) Wenn die asynchrone Aufgabe nur beendet werden soll, wenn Ansichten aktualisiert werden können: In dem hier vorgeschlagenen Fall aktualisiert die Aufgabe nur die Ansichten, es ist kein Datenspeicher erforderlich, sodass keine Ahnung besteht, ob die Aufgabe beendet werden kann, wenn Ansichten vorhanden sind nicht mehr gezeigt. Ich mache das:
Ich habe kein Problem damit gefunden, obwohl ich auch eine (vielleicht) komplexere Methode verwende, die das Starten von Aufgaben aus der Aktivität anstelle der Fragmente umfasst.
Wünschte, das hilft jemandem! :) :)
quelle
Das Problem mit Ihrem Code ist die Art und Weise, wie Sie die AsyncTask verwenden, denn wenn Sie den Bildschirm während Ihres Sleep-Threads drehen:
Die AsyncTask funktioniert immer noch. Dies liegt daran, dass Sie die AsyncTask-Instanz in onDestroy () nicht ordnungsgemäß abgebrochen haben, bevor das Fragment neu erstellt wurde (wenn Sie es drehen) und wenn dieselbe AsyncTask-Instanz (nach dem Drehen) onPostExecute () ausgeführt wird, wird versucht, dies zu finden die Ressourcen mit getResources () mit der alten Fragmentinstanz (eine ungültige Instanz):
was äquivalent ist zu:
Die endgültige Lösung besteht also darin, die AsyncTask-Instanz zu verwalten (abzubrechen, wenn dies noch funktioniert), bevor das Fragment beim Drehen des Bildschirms neu erstellt wird. Wenn die Fragmentierung während des Übergangs abgebrochen wird, starten Sie die AsyncTask nach der Rekonstruktion mithilfe eines booleschen Flags neu:
quelle
getResources().***
HilfeFragments.this.getResource().***
geholfenSie sind ziemlich trickreiche Lösung dafür und Leck von Fragmenten aus der Aktivität.
Im Fall von getResource oder etwas anderem, das vom Aktivitätskontext abhängt, auf den über Fragment zugegriffen wird, wird der Aktivitätsstatus und der Fragmentstatus immer wie folgt überprüft
quelle
isAdded
ist genug. Ich habe nie eine Situation gesehen, in dergetString()
es abgestürzt warisAdded == true
. Sind Sie sicher, dass eine Aktivität angezeigt und ein Fragment angehängt wurde?funktioniert auch in einigen Fällen. Unterbricht einfach die Codeausführung und stellt sicher, dass die App nicht abstürzt
quelle
Ich hatte das gleiche Problem, ich habe nur die Singletone-Instanz hinzugefügt, um die von Erick angegebene Ressource zu erhalten
Sie können auch verwenden
Ich hoffe das wird helfen.
quelle
Ich hatte ähnliche Probleme, als die Aktivität der Anwendungseinstellungen mit den geladenen Einstellungen sichtbar war. Wenn ich eine der Einstellungen ändern und dann den Anzeigeinhalt drehen und die Einstellung erneut ändern würde, stürzt die Meldung ab, dass das Fragment (meine Voreinstellungsklasse) nicht an eine Aktivität angehängt war.
Beim Debuggen sah es so aus, als würde die onCreate () -Methode des PreferencesFragment zweimal aufgerufen, wenn der Anzeigeinhalt gedreht wurde. Das war schon seltsam genug. Dann habe ich den isAdded () - Check außerhalb des Blocks hinzugefügt, der auf den Absturz hinweist, und das Problem behoben.
Hier ist der Code des Listeners, der die Zusammenfassung der Einstellungen aktualisiert, um den neuen Eintrag anzuzeigen. Es befindet sich in der onCreate () -Methode meiner Preferences-Klasse, die die PreferenceFragment-Klasse erweitert:
Ich hoffe das wird anderen helfen!
quelle
Wenn Sie die
Application
Klasse wie folgt erweitern und ein statisches 'globales' Kontextobjekt verwalten, können Sie dieses anstelle der Aktivität zum Laden einer String-Ressource verwenden.Wenn Sie dies verwenden, können Sie mit dem
Toast
Laden von Ressourcen durchkommen, ohne sich um Lebenszyklen sorgen zu müssen.quelle
In meinem Fall wurden Fragmentmethoden aufgerufen
quelle
Ein alter Beitrag, aber ich war überrascht über die am besten bewertete Antwort.
Die richtige Lösung hierfür sollte darin bestehen, die Asynctask in onStop (oder wo immer dies in Ihrem Fragment angemessen ist) abzubrechen. Auf diese Weise führen Sie keinen Speicherverlust ein (eine Asynctask, die einen Verweis auf Ihr zerstörtes Fragment enthält) und haben eine bessere Kontrolle darüber, was in Ihrem Fragment vor sich geht.
quelle
cancel
verhindern kann nichtonPostExecute
aus wird aufgerufen.