Saya telah membuat aplikasi uji kecil yang mewakili masalah saya. Saya menggunakan ActionBarSherlock untuk mengimplementasikan tab dengan (Sherlock) Fragments.
Kode saya:
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();
}
}
Saya telah menambahkan Thread.sleep
bagian untuk mensimulasikan mengunduh data. Kode dalam onPostExecute
adalah untuk mensimulasikan penggunaan Fragment
.
Ketika saya memutar layar dengan sangat cepat antara landscape dan portrait, saya mendapatkan Exception pada onPostExecute
kode:
java.lang.IllegalStateException: Fragment MyFragment {410f6060} tidak dilampirkan ke Activity
Saya pikir itu karena yang baru MyFragment
telah dibuat sementara itu, dan dilampirkan pada Kegiatan sebelum AsyncTask
selesai. Kode dalam onPostExecute
panggilan pada yang tidak terikat MyFragment
.
Tetapi bagaimana saya bisa memperbaikinya?
sumber
mView = inflater.inflate(R.layout.my_layout, container, false)
Dan sekarang menggunakan tampilan ini bila Anda ingin mendapatkan sumber:mView.getResources().***
. Ini membantu saya untuk memperbaiki bug ini.Context
yang melekat pada `mView` Anda.mView
di onDestroy?Jawaban:
Saya telah menemukan jawaban yang sangat sederhana
isAdded()
::Untuk menghindari
onPostExecute
dipanggil saatFragment
tidak terpasang keActivity
adalah untuk membatalkanAsyncTask
ketika menjeda atau menghentikanFragment
. MakaisAdded()
tidak perlu lagi. Namun, disarankan untuk tetap memeriksa ini.sumber
isDetached()
, yang ditambahkan pada API level 13Masalahnya adalah Anda mencoba mengakses sumber daya (dalam hal ini, string) menggunakan getResources (). GetString (), yang akan mencoba untuk mendapatkan sumber daya dari Aktivitas. Lihat kode sumber kelas Fragmen ini:
mHost
adalah objek yang menyimpan Aktivitas Anda.Karena Aktivitas mungkin tidak dilampirkan, panggilan getResources () Anda akan mengeluarkan Pengecualian.
IMHO solusi yang diterima bukan cara untuk pergi karena Anda hanya menyembunyikan masalah. Cara yang benar adalah hanya untuk mendapatkan sumber daya dari tempat lain yang selalu dijamin ada, seperti konteks aplikasi:
sumber
getString()
ketika fragmen saya dijeda. Terima kasihSaya telah menghadapi dua skenario berbeda di sini:
1) Ketika saya ingin menyelesaikan tugas asinkron: bayangkan onPostExecute saya menyimpan data yang diterima dan kemudian memanggil pendengar untuk memperbarui tampilan, agar lebih efisien, saya tetap ingin menyelesaikan tugas jadi saya memiliki data yang siap ketika pengguna memanggil kembali. Dalam hal ini saya biasanya melakukan ini:
2) Ketika saya ingin tugas asinkron hanya selesai ketika tampilan dapat diperbarui: kasus yang Anda ajukan di sini, tugas hanya memperbarui tampilan, tidak perlu penyimpanan data, sehingga tidak memiliki petunjuk untuk menyelesaikan tugas jika tampilan tidak lagi ditampilkan. Saya melakukan ini:
Saya tidak menemukan masalah dengan ini, meskipun saya juga menggunakan cara (mungkin) yang lebih kompleks yang mencakup memulai tugas dari aktivitas alih-alih fragmen.
Semoga ini bisa membantu seseorang! :)
sumber
Masalah dengan kode Anda adalah cara Anda menggunakan AsyncTask, karena ketika Anda memutar layar selama utas tidur Anda:
AsyncTask masih berfungsi, itu karena Anda tidak membatalkan instance AsyncTask dengan benar di onDestroy () sebelum fragmen dibangun kembali (ketika Anda memutar) dan ketika instance AsyncTask yang sama (setelah diputar) berjalan diPostExecute (), ini mencoba mencari sumber daya dengan getResources () dengan instance fragmen lama (instance tidak valid):
yang setara dengan:
Jadi solusi terakhir adalah mengelola instance AsyncTask (untuk membatalkan jika ini masih berfungsi) sebelum fragmen dibangun kembali ketika Anda memutar layar, dan jika dibatalkan selama transisi, restart AsyncTask setelah rekonstruksi dengan bantuan bendera boolean:
sumber
getResources().***
menggunakanFragments.this.getResource().***
membantuMereka cukup solusi trik untuk ini dan kebocoran fragmen dari aktivitas.
Jadi dalam kasus getResource atau apapun yang tergantung pada konteks aktivitas mengakses dari Fragment selalu memeriksa status aktivitas dan status fragmen sebagai berikut
sumber
isAdded
sudah cukup. Saya tidak pernah melihat situasi ketikagetString()
jatuh jikaisAdded == true
. Apakah Anda yakin suatu kegiatan ditampilkan dan sebuah fragmen dilampirkan?berfungsi juga dalam beberapa kasus. Hancurkan saja eksekusi kode dan pastikan aplikasi tidak macet
sumber
Saya menghadapi masalah yang sama saya hanya menambahkan instance singletone untuk mendapatkan sumber daya sebagaimana dimaksud oleh Erick
Anda juga bisa menggunakan
Saya harap ini akan membantu.
sumber
Saya menghadapi masalah serupa ketika aktivitas pengaturan aplikasi dengan preferensi yang dimuat terlihat. Jika saya akan mengubah salah satu preferensi dan kemudian membuat konten tampilan diputar dan mengubah preferensi lagi, itu akan crash dengan pesan bahwa fragmen (kelas Preferensi saya) tidak dilampirkan ke suatu kegiatan.
Ketika debugging itu tampak seperti metode onCreate () dari PreferencesFragment dipanggil dua kali ketika konten tampilan diputar. Itu sudah cukup aneh. Kemudian saya menambahkan isAdded () periksa di luar blok di mana itu akan menunjukkan crash dan itu memecahkan masalah.
Berikut adalah kode pendengar yang memperbarui ringkasan preferensi untuk menampilkan entri baru. Itu terletak di metode onCreate () dari kelas Preferensi saya yang memperluas kelas PreferenceFragment:
Saya harap ini akan membantu orang lain!
sumber
Jika Anda memperluas
Application
kelas dan mempertahankan objek Konteks 'global' statis, seperti berikut, maka Anda dapat menggunakannya sebagai ganti aktivitas untuk memuat sumber daya String.Jika Anda menggunakan ini, Anda dapat pergi
Toast
dan memuat sumber daya tanpa khawatir tentang siklus hidup.sumber
Dalam kasus saya, metode fragmen telah dipanggil
sumber
Posting lama, tetapi saya terkejut dengan jawaban yang paling banyak dipilih.
Solusi yang tepat untuk ini harus membatalkan asynctask di onStop (atau di mana pun sesuai fragmen Anda). Dengan cara ini Anda tidak memperkenalkan kebocoran memori (asynctask menyimpan referensi untuk fragmen Anda yang hancur) dan Anda memiliki kontrol yang lebih baik dari apa yang terjadi di fragmen Anda.
sumber
cancel
mungkin tidak mencegahonPostExecute
dipanggil.