Apa urutan yang benar untuk memanggil metode superclass dalam metode onPause, onStop, dan onDestroy? dan mengapa?

89

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();  
    }
Anudeep Bulla
sumber
1
Saya di kamp dua untuk metode penutupan. Saya berada di kamp pertama untuk metode memulai.
danny117
1
Itulah intinya. Hanya tidak bisa mengerti bagaimana menggunakan metode 1 untuk metode shutdown masuk akal.
Anudeep Bulla

Jawaban:

107

Menghancurkan sumber daya spesifik instance terlebih dahulu, sebelum menghancurkan sumber daya kelas super yang mungkin bergantung pada sumber daya spesifik, bukan sebaliknya. Tetapi komentar tersebut menyarankan sebaliknya. Apa yang saya lewatkan?

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:

Tetapi mengapa dokter resmi mengatakan: "Selalu panggil metode superclass dulu" di onPause ()?

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()");
}

mCalleddisetel ke true in Activity.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 firsttersebut tampaknya tidak memiliki banyak bukti untuk mendukungnya. Yang lebih buruk (untuk pernyataan) adalah bahwa kode berikut telah diambil dari android.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 digunakan startActivityForResult(Intent). Mengatur hasil menggunakan setResult(...) setelah super.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:

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.

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 :

Metode yang Anda timpa yang merupakan bagian dari pembuatan komponen (onCreate (), onStart (), onResume (), dll.), Anda harus menghubungkan ke superclass sebagai pernyataan pertama , untuk memastikan bahwa Android memiliki kesempatan untuk melakukan pekerjaannya sebelum Anda mencoba melakukan sesuatu yang bergantung pada pekerjaan yang telah dilakukan.

Metode yang Anda timpa yang merupakan bagian dari penghancuran komponen (onPause (), onStop (), onDestroy (), dll.), Anda harus melakukan pekerjaan Anda terlebih dahulu dan menghubungkan ke superclass sebagai hal terakhir . Dengan begitu, jika Android membersihkan sesuatu yang menjadi tempat bergantung pekerjaan Anda, Anda harus menyelesaikan pekerjaan Anda terlebih dahulu.

Metode yang mengembalikan sesuatu selain void (onCreateOptionsMenu (), dll.), Terkadang Anda menghubungkan ke superclass dalam pernyataan return, dengan asumsi bahwa Anda tidak secara khusus melakukan sesuatu yang perlu memaksakan nilai pengembalian tertentu.

Segala sesuatu yang lain - seperti onActivityResult () - terserah Anda, secara keseluruhan. Saya cenderung merantai superclass sebagai hal pertama, tetapi kecuali Anda mengalami masalah, mengikat nanti seharusnya baik-baik saja.

Bob Kerns dari utas ini :

Ini adalah pola yang bagus [(pola yang disarankan Mark di atas)], tetapi saya telah menemukan beberapa pengecualian. Misalnya, tema yang ingin saya terapkan ke PreferenceActivity saya tidak akan berpengaruh kecuali saya meletakkannya di depan superclass's onCreate ().

Pengguna Steve Benett juga memperhatikan hal ini:

Saya hanya tahu satu situasi, di mana waktu panggilan super diperlukan. Jika Anda ingin mengubah perilaku standar dari 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.

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 firstmemiliki 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.

Vikram
sumber
2
Wow. Terima kasih atas petunjuknya. Baik ini dan jawaban @ Sherif memberikan konteks penting. Jika salah satu dari Anda dapat meringkas jawaban di halaman ini, saya akan menandainya sebagai diterima. Mohon sertakan: 1. Jawaban di halaman ini. 2. @ Jawaban Philip di halaman ini 3. Jawaban @ CommonsWare di halaman ini 4. Diskusi ini saya mau, tetapi saya tidak ingin pujian atas jawaban Anda yang luar biasa. Cheers & Thanks
Anudeep Bulla
Hai. Bisakah Anda meringkas, mengingat @Sherif tidak mau?
Anudeep Bulla
@AnudeepBulla Hai Anudeep, beri aku waktu sampai besok. Saya akan menambahkan materi yang relevan ke jawaban saya dan memberi Anda komentar di sini.
Vikram
@AnudeepBulla Saya telah menambahkan ringkasan di atas. Tolong beri tahu saya jika saya melewatkan sesuatu.
Vikram
@Vikram Apakah TL; DR. bahwa menelepon onDestroydan yang onStopterakhir adalah default yang lebih aman, dan untuk beberapa onPausehal bisa lebih rumit dalam beberapa kasus? Bisakah Anda menambahkan itu dulu? Saya tergoda untuk mengedit sendiri jawabannya, tetapi saya tidak yakin ringkasan ini benar.
Blaisorblade
13

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 AnudeepBullaActivityextends BaseActivity dan kemudian, saya ingin membuat SherifElKhatibActivityyang memperluas aktivitas Anda? Dalam urutan apa saya harus memanggil super.dofungsi? 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, performStopfungsi 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.

Sherif elKhatib
sumber
Pertanyaannya bukanlah mengapa memanggil super.onDestroy () terakhir masuk akal. Saya suka contoh perpustakaan Anda. Persis apa yang ingin saya sampaikan juga. Saya sangat setuju untuk memanggil metode super terakhir pada penghancuran setengah siklus terakhir, persis seperti dalam tumpukan; untuk mencegah kehilangan data yang tidak disengaja. Masalahnya adalah mengapa Google bersikeras memanggil metode super terlebih dahulu, mengingat premis di atas? Saya mengajukan pertanyaan itu karena saya pikir mungkin saya, dan sepertinya Anda juga, mungkin melakukan pendekatan yang sama sekali berbeda. Cheers
Anudeep Bulla
Oh, saya tidak melihat saran Google dan cara lain: p! Dengar, saya akan mencoba membuat aktivitas yang akan macet jika Anda memanggil onDestroy terlebih dahulu. Anda harus mencobanya juga. Cheers
Sherif elKhatib
@AnudeepBulla Anda dapat memeriksa suntingan saya. Dan btw Anda bisa berhenti mencoba. super.onmungkin tidak akan pernah merusak aktivitas Anda.
Sherif elKhatib
Wow. Terima kasih atas petunjuknya. Baik ini dan jawaban @ Pengguna memberikan konteks penting. Jika salah satu dari Anda dapat meringkas jawaban di halaman ini, saya akan menandainya sebagai diterima. Mohon sertakan: 1. Jawaban di halaman ini. 2. @ Jawaban Philip di halaman ini 3. Jawaban @ CommonsWare di halaman ini 4. Diskusi ini saya mau, tetapi saya tidak ingin pujian atas jawaban Anda yang luar biasa. Cheers & Thanks
Anudeep Bulla
@AnudeBulla Sama-sama. Saya tidak yakin bagaimana kami akan memutuskan siapa yang memposting artikel terakhir.
Vikram
2

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 .

NickT
sumber
Tautan ke posting Dianne Hackborn penting dan menegaskan polanya.
Nick Westgate
1

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

LOG_TAG
sumber
Saya sangat mengerti apa yang Anda taruh di sana. Anda memanggil konstruktor kelas super dulu, karena mereka mungkin menginisialisasi sumber daya yang mungkin dibutuhkan anak; dan penghancur terakhir, 'karena Anda tidak ingin mungkin menghapus semua orang tua ke sumber daya lokal, membuat mereka tidak berguna. Tepatnya itulah maksud saya. Dan karena onPause, onStop, dan onDestroy memiliki tugas untuk menyimpan info status dan kurang lebih membuat sumber daya tersedia untuk GC (jadi dalam artian menghancurkannya), saya melihat mereka analog dengan destruktor dan karenanya berpikir memanggil mereka terakhir masuk akal. Tidak?
Anudeep Bulla
Semua yang Anda katakan di atas persis seperti yang saya maksud ketika saya mengatakan "memanggil metode super terlebih dahulu masuk akal pada setengah siklus pembuatan". Saya prihatin, dan bingung dalam kasus metode penghancuran setengah siklus, yang tampaknya lebih analog dengan perusak. Cheers
Anudeep Bulla
1

Hal yang paling penting untuk diingat adalah super.onPause()panggilan secara implisit setResult(Activity.RESULT_CANCELED). Namun setResulthanya dapat dipanggil sekali, dan semua panggilan berikutnya akan diabaikan. Jadi, jika Anda ingin mendorong hasil apa pun kembali ke aktivitas induk, Anda harus menelepon setResultdiri sendiri, sebelum menelepon super.onPause(). Itu adalah gotcha terbesar, sejauh yang saya tahu.

Philip Sheard
sumber
Wow, sepertinya ini penting. Jadi ada situasi, ketika Anda pasti harus menunda panggilan ke metode super. Terima kasih.
Anudeep Bulla
super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED). Bisakah Anda mengatakan dari mana Anda mendapatkan ini?
Vikram
Saya bingung. Sebenarnya finish () yang memanggil setResult, dan super.onBackPressed () memanggil finish (). Jadi setResult pasti harus dipanggil sebelum super.onBackPressed (). Saya tidak yakin apakah ada keadaan di mana super.onPause () dapat menyebabkan setResult dipanggil, tetapi saya memilih untuk tidak mengambil risiko.
Philip Sheard
1

KEDUA adalah IMO yang benar

Menurut dokumen

Kelas turunan harus memanggil implementasi kelas super untuk metode ini. Jika tidak, pengecualian akan dilemparkan.

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 supermetode 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.

Sunil Mishra
sumber
Saya minta maaf jika pertanyaan itu tidak sepenuhnya jelas pada saat pertama kali, tetapi mohon lihat sekarang.
Anudeep Bulla
@AnudeepBulla Itulah yang telah saya jelaskan kepada Anda. Anda dapat menggunakan salah satunya. Keduanya valid.
Sunil Mishra
Menurut pemahaman saya, penerapan kustom metode onPause, onStop, dan onDestroy tidak sepenuhnya diperlukan. Saya telah membuat banyak aplikasi tanpa itu. Jadi apa yang Anda maksud dengan metode Super harus selalu dipanggil? Mereka dipanggil secara implisit, bahkan tanpa menimpa. Saya juga tahu bahwa keduanya bekerja. Saya ingin tahu mengapa dokumen mengatakan super harus dipanggil dulu. Dan jika pertanyaannya masih belum jelas, bisakah Anda menjelaskan MENGAPA, ketika Anda mengatakan "Tapi untuk praktik terbaik, Anda harus menelepon dulu"?
Anudeep Bulla
Jika Anda tidak menimpa, metode akan dipanggil dari Base Classtetapi jika Anda menimpa, Anda harus memanggil metode superlain yang akan Anda milikiandroid.app.SuperNotCalledException
Sunil Mishra
1
Anda tampaknya salah paham. Pertanyaannya bukanlah apakah akan menelepon atau tidak. Seperti yang Anda tunjukkan, Anda HARUS, jika Anda menimpanya. Pertanyaannya adalah, kapan?
Anudeep Bulla
0

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.

Steve Benett
sumber
Urutan di onCreate tampaknya cukup bisa dimengerti. Apa yang terjadi dalam metode penghancuran? Ucapkan onStop. Misalkan implementasi onStop saya menggunakan beberapa sumber daya yang dirilis metode super jika dipanggil. Maka masuk akal, untuk memanggil metode super setelah implementasi saya.
Anudeep Bulla
Idealnya, harus demikian, benar. Aktivitas kita akan selalu memiliki sumber daya yang dimiliki kelas super, dan lainnya. Dan yang tidak bergantung pada aktivitas saya mungkin, dalam banyak kasus bergantung pada sumber daya superkelas, yang umum. Lebih masuk akal jika berurusan dengan sumber daya saya terlebih dahulu, dan kemudian memanggil superclass untuk menangani yang umum. Lalu mengapa Google mengatakan bahwa kita "HARUS" memanggil metode kelas super terlebih dahulu?
Anudeep Bulla
Sumber daya apa yang Anda bicarakan, yang dapat Anda akses di onCreate tetapi tidak di onDestroy?
Steve Benett
Saya tidak memiliki kasus penggunaan. Saya hanya ingin tahu, bagaimana ini bervariasi dengan gaya OOP. Konstruktor kelas super dipanggil terlebih dahulu, sebelum implementasi Anda, dan penghancur kelas super dipanggil terakhir, setelah implementasi Anda kan? onPause, onStop, dan onDestroy tidak sepenuhnya destruktor, tetapi mereka cenderung melakukan hal yang sama, bukan? Atleast onDestroy dan kebanyakan onStop juga .. bukan?
Anudeep Bulla
Lihat callback dalam keadaan berubah bukan sebagai konstruktor / destruktor. Setiap callback memiliki kebutuhannya sendiri, misalnya membuat (siap digunakan) / menghancurkan (kesempatan terakhir Anda untuk berinteraksi) suatu Aktivitas atau meletakkannya di latar depan / latar belakang. Callback ada di sana sehingga Anda dapat mengontrol sumber daya Anda dalam aliran sistem. Sistem hanya memeriksa di negara bagian mana dan menanganinya. Sumber daya yang Anda gunakan dan yang satu kontrol sistem tidak bergantung satu sama lain dan tidak akan ada persimpangan.
Steve Benett