Saya baru saja membuka Situs Pengembang Android, menyegarkan kembali siklus Hidup Aktivitas, dan di setiap contoh kode, ada komentar di samping metode kelas super yang mengatakan "Selalu panggil metode kelas super dulu".
Meskipun ini masuk akal dalam pembuatan setengah siklus: onCreate, onStart dan onResume, saya agak bingung tentang prosedur apa yang benar pada setengah siklus penghancuran: onPause, onStop, onDestroy.
Menghancurkan sumber daya spesifik instance terlebih dahulu, sebelum menghancurkan sumber daya kelas super yang mungkin bergantung pada sumber daya spesifik, bukan sebaliknya. Tetapi komentar menyarankan sebaliknya. Apa yang saya lewatkan?
Sunting : Karena orang tampaknya menjadi bingung tentang maksud dalam pertanyaan, yang ingin saya ketahui adalah manakah dari berikut ini yang benar? DAN MENGAPA ?
1. Google menyarankan
@Override
protected void onStop() {
super.onStop(); // Always call the superclass method first
//my implementation here
}
2. Cara lain
@Override
protected void onStop() {
//my implementation here
super.onStop();
}
sumber
Jawaban:
Menurut saya: tidak ada satu hal pun.
Jawaban dari Mark (alias CommonsWare on SO) ini menjelaskan masalah ini: Tautan - Haruskah panggilan ke metode superclass menjadi pernyataan pertama? . Tapi kemudian, Anda dapat melihat komentar berikut yang ditinggalkan pada jawabannya:
Kembali ke titik awal. Oke, mari kita lihat ini dari sudut lain. Kita tahu bahwa Spesifikasi Bahasa Java tidak menentukan urutan panggilan ke mana
super.overridenMethod()
harus dilakukan (atau jika panggilan harus dilakukan sama sekali).Dalam kasus Aktivitas kelas,
super.overridenMethod()
panggilan diperlukan dan diberlakukan :if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onStop()"); }
mCalled
disetel ke true inActivity.onStop()
.Sekarang, satu-satunya detail yang tersisa untuk diperdebatkan adalah urutannya.
I also know that both work
Tentu. Lihat badan metode untuk Activity.onPause ():
protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); // This is to invoke // Application.ActivityLifecyleCallbacks.onActivityPaused(Activity) getApplication().dispatchActivityPaused(this); // The flag to enforce calling of this method mCalled = true; }
Apa pun cara Anda menjawab panggilan
super.onPause()
, Anda akan baik-baik saja. Activity.onStop () memiliki badan metode serupa. Tapi lihat Activity.onDestroy ():protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; // dismiss any dialogs we are managing. if (mManagedDialogs != null) { final int numDialogs = mManagedDialogs.size(); for (int i = 0; i < numDialogs; i++) { final ManagedDialog md = mManagedDialogs.valueAt(i); if (md.mDialog.isShowing()) { md.mDialog.dismiss(); } } mManagedDialogs = null; } // close any cursors we are managing. synchronized (mManagedCursors) { int numCursors = mManagedCursors.size(); for (int i = 0; i < numCursors; i++) { ManagedCursor c = mManagedCursors.get(i); if (c != null) { c.mCursor.close(); } } mManagedCursors.clear(); } // Close any open search dialog if (mSearchManager != null) { mSearchManager.stopSearch(); } getApplication().dispatchActivityDestroyed(this); }
Di sini, pengurutan mungkin penting bergantung pada bagaimana aktivitas Anda disiapkan, dan apakah pemanggilan
super.onDestroy()
akan mengganggu kode berikutnya.Sebagai kata terakhir, pernyataan
Always call the superclass method first
tersebut tampaknya tidak memiliki banyak bukti untuk mendukungnya. Yang lebih buruk (untuk pernyataan) adalah bahwa kode berikut telah diambil dariandroid.app.ListActivity
:public class ListActivity extends Activity { .... @Override protected void onDestroy() { mHandler.removeCallbacks(mRequestFocus); super.onDestroy(); } .... }
Dan, dari contoh aplikasi LunarLander yang disertakan dalam android sdk:
public class LunarLander extends Activity { .... @Override protected void onPause() { mLunarView.getThread().pause(); // pause game when Activity pauses super.onPause(); } .... }
Ringkasan dan sebutan yang layak:
Pengguna Philip Sheard : Menyediakan skenario di mana panggilan ke
super.onPause()
harus ditunda jika suatu Aktivitas mulai digunakanstartActivityForResult(Intent)
. Mengatur hasil menggunakansetResult(...)
setelahsuper.onPause()
tidak akan berfungsi. Dia kemudian mengklarifikasi hal ini di komentar untuk jawabannya.Pengguna Sherif elKhatib : Menjelaskan mengapa membiarkan superclass menginisialisasi sumber dayanya terlebih dahulu dan menghancurkan sumber dayanya terakhir mengikuti dari logika:
Dia melanjutkan dengan menunjukkan: jika kelas anak diisolasi dengan sesuai (dalam hal ketergantungan sumber daya) dari kelas induk,
super.X()
panggilan tidak perlu mematuhi spesifikasi pesanan apa pun.Lihat jawabannya di halaman ini untuk membaca sebuah skenario di mana penempatan
super.onDestroy()
panggilan tidak mempengaruhi logika program.Dari jawaban Mark :
Bob Kerns dari utas ini :
Pengguna Steve Benett juga memperhatikan hal ini:
Pengguna Sunil Mishra mengonfirmasi bahwa order (kemungkinan besar) tidak berperan saat memanggil metode kelas Activity. Dia juga mengklaim bahwa memanggil metode superclass terlebih dahulu dianggap sebagai praktik terbaik . Namun, saya tidak bisa membenarkan hal ini.
LOG_TAG Pengguna : Menjelaskan mengapa panggilan ke konstruktor superclass harus dilakukan sebelum yang lainnya. Menurut saya, penjelasan ini tidak menambah pertanyaan yang diajukan.
Catatan akhir : Percayai, tetapi verifikasi. Sebagian besar jawaban di halaman ini mengikuti pendekatan ini untuk melihat apakah pernyataan tersebut
Always call the superclass method first
memiliki dukungan logis. Ternyata tidak; setidaknya, tidak dalam kasus Aktivitas kelas. Umumnya, seseorang harus membaca kode sumber superclass untuk menentukan apakah memesan panggilan ke metode super adalah suatu persyaratan.sumber
onDestroy
dan yangonStop
terakhir adalah default yang lebih aman, dan untuk beberapaonPause
hal bisa lebih rumit dalam beberapa kasus? Bisakah Anda menambahkan itu dulu? Saya tergoda untuk mengedit sendiri jawabannya, tetapi saya tidak yakin ringkasan ini benar.Karena (Anda mengatakan) masuk akal untuk memanggil super onCreate terlebih dahulu: Pikirkanlah.
Ketika saya ingin membuat, Super saya menciptakan sumber dayanya> Saya membuat sumber daya saya.
Terbalik: (semacam tumpukan)
Ketika saya ingin menghancurkan, saya menghancurkan sumber daya saya> Super saya menghancurkan sumber dayanya.
Dalam pengertian ini, ini berlaku untuk beberapa fungsi (onCreate / onDestroy, onResume / onPause, onStart / onStop). Secara alami, onCreate akan membuat sumber daya dan onDestroy akan membebaskan sumber daya ini. Ngomong-ngomong, bukti yang sama berlaku untuk pasangan lain.
Mari kita pertimbangkan pustaka yang Anda unduh yang memiliki LocationActivity yang berisi fungsi getLocation () yang menyediakan lokasi. Kemungkinan besar, aktivitas ini perlu menginisialisasi barangnya di onCreate () yang akan memaksa Anda memanggil super.onCreate terlebih dahulu. Anda sudah melakukannya karena Anda merasa itu masuk akal. Sekarang, di onDestroy Anda, Anda memutuskan ingin menyimpan Lokasi di suatu tempat di SharedPreferences. Jika Anda memanggil super.onDestroy terlebih dahulu, sampai batas tertentu mungkin getLocation akan mengembalikan nilai null setelah panggilan ini karena implementasi LocationActivity membatalkan nilai lokasi di onDestroy. Idenya adalah Anda tidak akan menyalahkannya jika ini terjadi. Oleh karena itu, Anda akan memanggil super.onDestroy di bagian akhir setelah Anda selesai dengan onDestroy Anda sendiri. Saya harap ini masuk akal.
Jika hal di atas masuk akal, pertimbangkan bahwa setiap saat kita memiliki aktivitas yang sesuai dengan konsep di atas. Jika saya ingin melanjutkan kegiatan ini, saya mungkin akan merasakan hal yang sama dan mengikuti urutan yang sama karena argumen yang sama persis.
Dengan induksi, aktivitas apapun harus melakukan hal yang sama. Berikut adalah kelas abstrak yang bagus untuk aktivitas yang dipaksa mengikuti aturan ini:
package mobi.sherif.base; import android.app.Activity; import android.os.Bundle; public abstract class BaseActivity extends Activity { protected abstract void doCreate(Bundle savedInstanceState); protected abstract void doDestroy(); protected abstract void doResume(); protected abstract void doPause(); protected abstract void doStart(); protected abstract void doStop(); protected abstract void doSaveInstanceState(Bundle outState); @Override protected final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); doCreate(savedInstanceState); } @Override protected final void onDestroy() { doDestroy(); super.onDestroy(); } @Override protected final void onResume() { super.onResume(); doResume(); } @Override protected final void onPause() { doPause(); super.onPause(); } @Override protected final void onStop() { doStop(); super.onStop(); } @Override protected final void onStart() { super.onStart(); doStart(); } @Override protected final void onSaveInstanceState(Bundle outState) { doSaveInstanceState(outState); super.onSaveInstanceState(outState); } }
Terakhir, bagaimana jika aktivitas Anda yang disebut
AnudeepBullaActivity
extends BaseActivity dan kemudian, saya ingin membuatSherifElKhatibActivity
yang memperluas aktivitas Anda? Dalam urutan apa saya harus memanggilsuper.do
fungsi? Itu pada akhirnya adalah hal yang sama.Adapun pertanyaan Anda:
Saya pikir niat Google adalah memberi tahu kami: Harap hubungi super di mana pun. Sebagai praktik umum tentunya, sebut saja di awal. Google tentu saja memiliki insinyur dan pengembang paling cerdas sehingga mereka mungkin melakukan pekerjaan yang baik dengan mengisolasi panggilan super mereka dan tidak mengganggu panggilan anak.
Saya mencoba sedikit dan mungkin tidak mudah (karena ini adalah Google, kami mencoba untuk membuktikan kesalahan) untuk membuat aktivitas yang akan crash sederhana karena When is super sedang dipanggil.
Mengapa?
Apa pun yang dilakukan dalam fungsi ini benar-benar pribadi untuk kelas Aktivitas dan tidak akan pernah menimbulkan konflik dengan subkelas Anda. Misalnya (onDestroy)
protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; // dismiss any dialogs we are managing. if (mManagedDialogs != null) { final int numDialogs = mManagedDialogs.size(); for (int i = 0; i < numDialogs; i++) { final ManagedDialog md = mManagedDialogs.valueAt(i); if (md.mDialog.isShowing()) { md.mDialog.dismiss(); } } mManagedDialogs = null; } // close any cursors we are managing. synchronized (mManagedCursors) { int numCursors = mManagedCursors.size(); for (int i = 0; i < numCursors; i++) { ManagedCursor c = mManagedCursors.get(i); if (c != null) { c.mCursor.close(); } } mManagedCursors.clear(); } // Close any open search dialog if (mSearchManager != null) { mSearchManager.stopSearch(); } getApplication().dispatchActivityDestroyed(this); }
mManagedCursors dan mManagedDialogs serta mSearchManager semuanya adalah bidang pribadi. Dan tidak ada api publik / dilindungi yang akan terpengaruh oleh apa yang dilakukan di sini.
Namun, dalam API 14, dispatchActivityDestroyed telah ditambahkan untuk mengirimkan onActivityDestroyed ke ActivityLifecycleCallbacks yang terdaftar ke Aplikasi Anda. Oleh karena itu, kode apa pun yang bergantung pada beberapa logika dalam ActivityLifecycleCallbacks Anda akan memiliki hasil yang berbeda berdasarkan pada saat Anda memanggil super. Sebagai contoh:
Buat Kelas Aplikasi yang menghitung jumlah aktivitas yang sedang berjalan:
package mobi.shush; import android.app.Activity; import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.os.Bundle; public class SherifApplication extends Application implements ActivityLifecycleCallbacks { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this); } public int getCount() { return count; } int count = 0; @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { count++; } @Override public void onActivityDestroyed(Activity activity) { count--; } @Override public void onActivityPaused(Activity activity) {} @Override public void onActivityResumed(Activity activity) {} @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} @Override public void onActivityStarted(Activity activity) {} @Override public void onActivityStopped(Activity activity) {} }
Berikut ini mungkin tidak masuk akal atau bukan praktik yang baik tetapi ini hanya untuk membuktikan suatu hal (Orang mungkin menemukan situasi yang lebih nyata). Buat MainActivity yang seharusnya masuk ke aktivitas GoodBye saat selesai dan saat itu adalah aktivitas terakhir:
@Override protected void onDestroy() { super.onDestroy(); if(((SherifApplication) getApplication()).getCount() == 0) { //i want to go to a certain activity when there are no other activities startActivity(new Intent(this, GoodBye.class)); } }
Jika Anda memanggil super.onDestroy di awal onDestroy, aktivitas GoodBye akan diluncurkan. Jika Anda memanggil super.onDestroy di akhir onDestroy Anda, aktivitas GoodBye tidak akan diluncurkan.
Tentu saja, sekali lagi, ini bukan contoh yang optimal. Namun ini menunjukkan bahwa Google sedikit kacau di sini. Variabel lain mana pun tidak akan memengaruhi perilaku aplikasi Anda. Namun menambahkan pengiriman ini ke onDestroy menyebabkan super mengganggu subkelas Anda.
Saya mengatakan mereka mengacaukan karena alasan yang berbeda juga. Mereka tidak hanya (sebelum api 14) hanya menyentuh panggilan super apa yang final dan / atau pribadi, tetapi mereka juga memanggil fungsi internal yang berbeda (pribadi) yang kemudian mengirimkan fungsi onPause ....
Misalnya,
performStop
fungsi adalah fungsi yang dipanggil yang pada gilirannya memanggil fungsi onStop:final void performStop() { if (mLoadersStarted) { mLoadersStarted = false; if (mLoaderManager != null) { if (!mChangingConfigurations) { mLoaderManager.doStop(); } else { mLoaderManager.doRetain(); } } } if (!mStopped) { if (mWindow != null) { mWindow.closeAllPanels(); } if (mToken != null && mParent == null) { WindowManagerGlobal.getInstance().setStoppedState(mToken, true); } mFragments.dispatchStop(); mCalled = false; mInstrumentation.callActivityOnStop(this); if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onStop()"); } synchronized (mManagedCursors) { final int N = mManagedCursors.size(); for (int i=0; i<N; i++) { ManagedCursor mc = mManagedCursors.get(i); if (!mc.mReleased) { mc.mCursor.deactivate(); mc.mReleased = true; } } } mStopped = true; } mResumed = false; }
Perhatikan bahwa mereka memanggil onStop Aktivitas di suatu tempat dalam fungsi ini. Oleh karena itu, mereka mungkin sebaiknya meletakkan semua kode (termasuk dalam super.onStop) sebelum atau setelah panggilan ke onStop dan kemudian memberi tahu subclass tentang onStop menggunakan fungsi super onStop kosong dan bahkan tanpa menambahkan SuperNotCalledException atau memeriksa panggilan ini.
Untuk ini, jika mereka memanggil pengiriman ini ke ActivityLifeCycle di performDestroy alih-alih memanggilnya di akhir super.onDestroy, perilaku aktivitas kita akan sama terlepas dari kapan kita memanggil super.
Bagaimanapun ini adalah hal pertama yang mereka lakukan (agak salah) dan hanya di API 14.
sumber
super.on
mungkin tidak akan pernah merusak aktivitas Anda.Anda mengatakan bahwa Google menyarankan metode 1, tetapi Dianne Hackborn, seorang insinyur sistem Android terkenal menyarankan sebaliknya, lihat Tautan Forum Google .
Masuk akal secara intuitif untuk memanggil super class terakhir saat menghancurkan sebuah instance dalam metode onPause, onStop, dan onDestroy, dan pertama kali saat membuat instance dengan metode onCreate, onResume, dan onStart .
sumber
Dari perspektif java, berikut adalah beberapa solusi untuk kebingungan ini:
Mengapa this () dan super () harus menjadi pernyataan pertama dalam konstruktor?
Konstruktor kelas induk harus dipanggil sebelum konstruktor subkelas. Ini akan memastikan bahwa jika Anda memanggil metode apa pun pada kelas induk di konstruktor Anda, kelas induk telah disiapkan dengan benar.
Apa yang Anda coba lakukan, meneruskan argumen ke konstruktor super benar-benar legal, Anda hanya perlu membuat argumen tersebut sebaris seperti yang Anda lakukan, atau meneruskannya ke konstruktor Anda dan kemudian meneruskannya ke super:
public MySubClassB extends MyClass { public MySubClassB(Object[] myArray) { super(myArray); } }
Jika kompilator tidak memberlakukan ini, Anda dapat melakukan ini:
public MySubClassB extends MyClass { public MySubClassB(Object[] myArray) { someMethodOnSuper(); //ERROR super not yet constructed super(myArray); } }
Ini menunjukkan bahwa sebenarnya, subbidang harus di-inilialisasi sebelum supreclass! Sementara itu, persyaratan java "melindungi" kita dari spesialisasi kelas dengan mengkhususkan apa yang argumen konstruktor super
Dalam kasus di mana kelas induk memiliki konstruktor default, panggilan ke super disisipkan untuk Anda secara otomatis oleh kompilator. Karena setiap kelas di Java mewarisi dari Object, konstruktor objek harus dipanggil entah bagaimana dan harus dijalankan terlebih dahulu. Penyisipan otomatis super () oleh kompilator memungkinkan ini. Menerapkan super untuk muncul pertama kali, memaksa badan konstruktor dieksekusi dalam urutan yang benar yaitu: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth
(1) Memeriksa bahwa super adalah pernyataan pertama tidak cukup untuk mencegah masalah itu. Misalnya, Anda bisa meletakkan "super (someMethodInSuper ());" di konstruktor Anda. Ini mencoba untuk mengakses metode di superclass sebelum dibangun, meskipun super adalah pernyataan pertama.
(2) Kompilator tampaknya mengimplementasikan pemeriksaan berbeda yang dengan sendirinya cukup untuk mencegah masalah ini. Pesannya adalah "tidak dapat mereferensikan xxx sebelum konstruktor supertipe dipanggil". Oleh karena itu, tidak perlu memeriksa bahwa super adalah pernyataan pertama
Silakan melalui http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html
sumber
Hal yang paling penting untuk diingat adalah
super.onPause()
panggilan secara implisitsetResult(Activity.RESULT_CANCELED)
. NamunsetResult
hanya dapat dipanggil sekali, dan semua panggilan berikutnya akan diabaikan. Jadi, jika Anda ingin mendorong hasil apa pun kembali ke aktivitas induk, Anda harus meneleponsetResult
diri sendiri, sebelum meneleponsuper.onPause()
. Itu adalah gotcha terbesar, sejauh yang saya tahu.sumber
super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED)
. Bisakah Anda mengatakan dari mana Anda mendapatkan ini?KEDUA adalah IMO yang benar
Menurut dokumen
Super
metode harus selalu dipanggil ketika dokumentasi secara eksplisit mengatakannya.Namun Anda dapat memilih kapan harus memanggil metode super.
Melihat sumber
onPause
protected void onPause() { getApplication().dispatchActivityPaused(this); mCalled = true; }
Karenanya tidak masalah sebelum atau sesudah itu disebut. Kamu harusnya baik.
Tetapi untuk praktik terbaik, Anda harus memanggilnya terlebih dahulu.
Saya merekomendasikannya sebagian besar sebagai mekanisme perlindungan: jika ada pengecualian maka
super
metode instance sudah akan dipanggil.Juga menempatkan panggilan ini di baris pertama akan membantu Anda menghindari kesalahan di masa mendatang seperti menghapus kode dalam metode dan secara tidak sengaja menghapus panggilan ke super class.
sumber
Base Class
tetapi jika Anda menimpa, Anda harus memanggil metodesuper
lain yang akan Anda milikiandroid.app.SuperNotCalledException
Super callback diperlukan untuk menempatkan Aktivitas dalam keadaan yang benar secara internal untuk sistem.
Misalkan Anda memulai Aktivitas dan onCreate dipanggil oleh sistem. Sekarang Anda dapat menggantinya dan misalnya memuat tata letak Anda. Tetapi demi aliran sistem Anda harus memanggil super, agar sistem dapat melanjutkan dengan prosedur standar. Itulah mengapa pengecualian akan dilemparkan jika Anda tidak menyebutnya.
Ini terjadi terlepas dari penerapan Anda di onCreate. Ini hanya penting untuk sistem. Jika tidak akan ada ANR, Anda dapat memiliki loop tanpa akhir di callback mana pun dan Aktivitas akan terperangkap di callback tersebut. Jadi, sistem mengetahui kapan callback telah diakhiri dan kemudian memanggil panggilan berikutnya.
Saya hanya tahu satu situasi, di mana waktu panggilan super diperlukan. Jika Anda ingin mengubah perilaku standar tema atau tampilan dan semacamnya di onCreate, Anda harus melakukannya sebelum memanggil super untuk melihat efeknya. Jika tidak AFAIK tidak ada perbedaan saat Anda menyebutnya.
Tetapi untuk membiarkan sistem melakukan apa yang terbaik, letakkan super di baris pertama callback diikuti dengan kode Anda, jika Anda tidak memiliki alasan yang tepat untuk memutusnya.
sumber