ลำดับที่ถูกต้องของการเรียกเมธอด superclass ในเมธอด onPause, onStop และ onDestroy คืออะไร และทำไม?


92

ฉันเพิ่งอ่านไซต์นักพัฒนาซอฟต์แวร์ Android โดยรีเฟรชวงจรชีวิตของกิจกรรมและในแต่ละตัวอย่างโค้ดมีความคิดเห็นข้างเมธอดระดับสูงที่ระบุว่า "เรียกเมธอด superclass ก่อนเสมอ"

แม้ว่าสิ่งนี้จะสมเหตุสมผลในครึ่งรอบการสร้าง: onCreate, onStart และ onResume แต่ฉันก็สับสนเล็กน้อยว่าขั้นตอนที่ถูกต้องในครึ่งรอบการทำลายคืออะไร: onPause, onStop, onDestroy

ทำลายทรัพยากรเฉพาะของอินสแตนซ์ก่อนก่อนที่จะทำลายทรัพยากรระดับสูงที่ทรัพยากรเฉพาะของอินสแตนซ์อาจขึ้นอยู่กับความสมเหตุสมผลไม่ใช่ในทางกลับกัน แต่ความคิดเห็นแนะนำเป็นอย่างอื่น ฉันขาดอะไรไป?

แก้ไข : เนื่องจากผู้คนดูเหมือนจะสับสนเกี่ยวกับเจตนาในคำถามสิ่งที่ฉันต้องการทราบคือข้อใดต่อไปนี้ถูกต้อง? และทำไม ?

1. Google แนะนำ

    @Override
    protected void onStop() {
      super.onStop();  // Always call the superclass method first

      //my implementation here
    }

2. วิธีอื่น ๆ

    @Override
    protected void onStop() {
       //my implementation here

       super.onStop();  
    }

1
ฉันอยู่ในค่ายสองสำหรับวิธีการปิดระบบ ฉันอยู่ในค่ายหนึ่งสำหรับวิธีการเริ่มต้น
danny117

1
นั่นเป็นประเด็นที่น่าสนใจมาก ไม่เข้าใจว่าการใช้วิธีที่ 1 สำหรับวิธีการปิดระบบนั้นสมเหตุสมผลอย่างไร
Anudeep Bulla

คำตอบ:


109

ทำลายทรัพยากรเฉพาะของอินสแตนซ์ก่อนก่อนที่จะทำลายทรัพยากรระดับสูงที่ทรัพยากรเฉพาะของอินสแตนซ์อาจขึ้นอยู่กับความสมเหตุสมผลไม่ใช่ในทางกลับกัน แต่ความคิดเห็นแนะนำเป็นอย่างอื่น ฉันขาดอะไรไป?

ในความคิดของฉัน: ไม่ใช่สิ่งเดียว

คำตอบนี้จาก Mark (aka CommonsWare on SO) ให้ความกระจ่างเกี่ยวกับปัญหา: Link - การเรียกใช้วิธี superclass ควรเป็นคำสั่งแรกหรือไม่? . แต่จากนั้นคุณจะเห็นความคิดเห็นต่อไปนี้ทิ้งไว้ในคำตอบของเขา:

แต่ทำไมเอกสารทางการถึงบอกว่า: "เรียกเมธอด superclass ก่อนเสมอ" ใน onPause ()?

กลับไปที่กำลังสอง เอาล่ะมาดูมุมอื่นกันดีกว่า เราทราบดีว่าข้อกำหนดภาษา Java ไม่ได้ระบุลำดับที่จะsuper.overridenMethod()ต้องทำการเรียก(หรือหากต้องทำการโทรเลย)

ในกรณีของกิจกรรมชั้นเรียนsuper.overridenMethod()จำเป็นต้องมีการโทรและบังคับใช้ :

if (!mCalled) {
    throw new SuperNotCalledException(
        "Activity " + mComponent.toShortString() +
            " did not call through to super.onStop()");
}

mCalledActivity.onStop()มีการตั้งค่าในความจริง

ตอนนี้รายละเอียดเดียวที่เหลือในการอภิปรายคือการสั่งซื้อ

I also know that both work

แน่นอน ดูที่เนื้อหาวิธีการสำหรับ 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;
}

ไม่ว่าคุณจะโทรไปทางไหนsuper.onPause()คุณก็โอเค Activity.onStop () มีเนื้อหาวิธีการที่คล้ายกัน แต่ลองดูที่ 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);
}

ที่นี่การสั่งซื้ออาจมีความสำคัญขึ้นอยู่กับวิธีการตั้งค่ากิจกรรมของคุณและการโทรsuper.onDestroy()จะรบกวนโค้ดที่ตามมาหรือไม่

คำพูดสุดท้ายAlways call the superclass method firstดูเหมือนจะไม่มีหลักฐานยืนยันมากนัก สิ่งที่แย่กว่านั้น (สำหรับคำสั่ง) คือรหัสต่อไปนี้ถูกนำมาจากandroid.app.ListActivity:

public class ListActivity extends Activity {

    ....

    @Override
    protected void onDestroy() {
        mHandler.removeCallbacks(mRequestFocus);
        super.onDestroy();
    }
    ....    
}

และจากแอปพลิเคชันตัวอย่าง LunarLander ที่รวมอยู่ใน android sdk:

public class LunarLander extends Activity {

    ....

    @Override
    protected void onPause() {
        mLunarView.getThread().pause(); // pause game when Activity pauses
        super.onPause();
    }
    ....
}

บทสรุปและการกล่าวถึงที่คุ้มค่า:

ผู้ใช้งานฟิลิป Sheard : ให้สถานการณ์ที่มีการโทรไปยังต้องมีความล่าช้าในกรณีของกิจกรรมเริ่มใช้super.onPause() startActivityForResult(Intent)การตั้งค่าผลลัพธ์โดยใช้setResult(...) after super.onPause()จะไม่ทำงาน ภายหลังเขาชี้แจงเรื่องนี้ในความคิดเห็นต่อคำตอบของเขา

ผู้ใช้ Sherif elKhatib : อธิบายว่าเหตุใดการปล่อยให้ superclass เริ่มต้นทรัพยากรก่อนและทำลายทรัพยากรเป็นครั้งสุดท้ายจากตรรกะ:

ให้เราพิจารณาไลบรารีที่คุณดาวน์โหลดซึ่งมี LocationActivity ที่มีฟังก์ชัน getLocation () ที่ระบุตำแหน่ง ส่วนใหญ่อาจกิจกรรมนี้จะต้องเริ่มต้นสิ่งใน onCreate () ซึ่งจะบังคับให้คุณโทร super.onCreate แรก คุณทำอย่างนั้นอยู่แล้วเพราะคุณรู้สึกว่ามันสมเหตุสมผล ตอนนี้ใน onDestroy คุณตัดสินใจว่าต้องการบันทึกตำแหน่งไว้ที่ใดที่หนึ่งใน SharedPreferences ถ้าคุณเรียก super.onDestroy ก่อนในระดับหนึ่งอาจเป็นไปได้ที่ getLocation จะส่งคืนค่าว่างหลังจากการเรียกนี้เนื่องจากการใช้งาน LocationActivity ทำให้ค่าตำแหน่งใน onDestroy เป็นโมฆะ แนวคิดก็คือคุณจะไม่ตำหนิหากสิ่งนี้เกิดขึ้นดังนั้นคุณจะโทรหา super.onDestroy ในตอนท้ายหลังจากที่คุณทำ onDestroy ของคุณเสร็จแล้ว

เขากล่าวต่อไปว่า: หากคลาสย่อยถูกแยกอย่างเหมาะสม (ในแง่ของการพึ่งพาทรัพยากร) จากคลาสพาเรนต์การsuper.X()เรียกไม่จำเป็นต้องเป็นไปตามข้อกำหนดคำสั่งใด ๆ

ดูคำตอบของเขาในหน้านี้เพื่ออ่านผ่านสถานการณ์ที่ตำแหน่งของsuper.onDestroy()การโทรไม่ส่งผลกระทบต่อตรรกะโปรแกรม

จากคำตอบของ Mark :

วิธีการที่คุณลบล้างซึ่งเป็นส่วนหนึ่งของการสร้างคอมโพเนนต์ (onCreate (), onStart (), onResume () ฯลฯ ) คุณควรเชื่อมโยงกับ superclass เป็นคำสั่งแรกเพื่อให้แน่ใจว่า Android มีโอกาสทำงานก่อนคุณ พยายามทำบางสิ่งบางอย่างที่อาศัยงานที่ทำไปแล้ว

วิธีการคุณแทนที่ที่เป็นส่วนหนึ่งของการทำลายส่วนประกอบ (onPause () onStop () OnDestroy () เป็นต้น) คุณควรจะทำอย่างไรการทำงานของคุณเป็นครั้งแรกและห่วงโซ่การ superclass เป็นสิ่งสุดท้าย ด้วยวิธีนี้ในกรณีที่ Android ล้างบางสิ่งที่ขึ้นอยู่กับงานของคุณคุณจะต้องทำงานของคุณก่อน

เมธอดที่ส่งคืนสิ่งอื่นที่ไม่ใช่โมฆะ (onCreateOptionsMenu () ฯลฯ ) บางครั้งคุณเชื่อมโยงกับ superclass ในคำสั่ง return โดยสมมติว่าคุณไม่ได้ทำบางอย่างที่ต้องการบังคับให้ส่งคืนค่าเฉพาะ

อย่างอื่นเช่น onActivityResult () - ขึ้นอยู่กับคุณโดยรวม ฉันมักจะเชื่อมโยงกับซูเปอร์คลาสเป็นสิ่งแรก แต่ถ้าคุณไม่ประสบปัญหาการผูกมัดในภายหลังก็น่าจะดี

Bob Kernsจากหัวข้อนี้ :

เป็นรูปแบบที่ดี [(รูปแบบที่ Mark แนะนำด้านบน)] แต่ฉันพบข้อยกเว้นบางประการ ตัวอย่างเช่น ธีมที่ฉันต้องการนำไปใช้กับ PreferenceActivity ของฉันจะไม่มีผลเว้นแต่ฉันจะใส่ไว้ก่อนหน้า onCreate () ของ superclass

ผู้ใช้ Steve Benettยังให้ความสำคัญกับสิ่งนี้:

ฉันรู้เพียงสถานการณ์เดียวที่จำเป็นต้องใช้เวลาในการโทรพิเศษ หากคุณต้องการปรับเปลี่ยนลักษณะการทำงานมาตรฐานของธีมหรือการแสดงผลใน onCreate คุณต้องทำก่อนที่จะเรียก super เพื่อดูเอฟเฟกต์ ไม่เช่นนั้น AFAIK จะไม่มีความแตกต่างในเวลาที่คุณเรียกมัน

ผู้ใช้ Sunil Mishraยืนยันว่าคำสั่งซื้อ (เป็นไปได้มากที่สุด) ไม่มีบทบาทเมื่อเรียกใช้เมธอดของคลาสกิจกรรม นอกจากนี้เขายังอ้างว่าเรียก superclass วิธีแรกถือว่าเป็นวิธีที่ดีที่สุด อย่างไรก็ตามฉันไม่สามารถยืนยันสิ่งนี้ได้

ผู้ใช้ LOG_TAG : อธิบายว่าเหตุใดการเรียกตัวสร้างคลาสระดับสูงจึงต้องมาก่อนสิ่งอื่นใด ในความคิดของฉันคำอธิบายนี้ไม่ได้เพิ่มคำถามที่ถูกถาม

หมายเหตุตอนท้าย : เชื่อถือได้แต่ยืนยันได้ คำตอบส่วนใหญ่ในหน้านี้เป็นไปตามแนวทางนี้เพื่อดูว่าคำสั่งAlways call the superclass method firstนั้นมีเหตุผลสนับสนุนหรือไม่ ปรากฎว่ามันไม่; อย่างน้อยไม่ใช่ในกรณีของกิจกรรมในชั้นเรียน โดยทั่วไปเราควรอ่านซอร์สโค้ดของ superclass เพื่อพิจารณาว่าการสั่งซื้อการโทรไปยังวิธีการของ super เป็นข้อกำหนดหรือไม่


2
ว้าว. ขอบคุณสำหรับคำแนะนำ ทั้งคำตอบนี้และคำตอบของ @ Sherif ให้บริบทที่สำคัญ หากคุณคนใดคนหนึ่งสามารถสรุปคำตอบในหน้านี้ได้ฉันจะทำเครื่องหมายว่ายอมรับ กรุณาระบุ: 1. คำตอบในหน้านี้ 2. คำตอบของ @ Philip ในหน้านี้ 3. คำตอบของ @ CommonsWare ในหน้านี้ 4. การสนทนานี้ ฉันต้องการ แต่ฉันไม่ต้องการเครดิตสำหรับคำตอบที่ยอดเยี่ยมของคุณ Cheers & Thanks
Anudeep Bulla

สวัสดี. คุณช่วยสรุปได้ไหมว่า @Sherif ไม่ต้องการ?
Anudeep Bulla

@AnudeepBulla สวัสดี Anudeep ให้ฉันจนถึงพรุ่งนี้ ฉันจะเพิ่มเนื้อหาที่เกี่ยวข้องในคำตอบของฉันและแสดงความคิดเห็นที่นี่
Vikram

@AnudeepBulla ฉันได้เพิ่มบทสรุปด้านบนแล้ว โปรดแจ้งให้เราทราบในกรณีที่ฉันพลาดอะไรไป
Vikram

@Vikram เป็น TL; DR. การโทรonDestroyและonStopครั้งสุดท้ายนั้นเป็นค่าเริ่มต้นที่ปลอดภัยกว่าและสำหรับonPauseสิ่งต่าง ๆ อาจยุ่งยากกว่าในบางกรณี? ช่วยเพิ่มก่อนได้ไหม ฉันอยากจะแก้ไขคำตอบด้วยตัวเอง แต่ฉันไม่แน่ใจว่าสรุปนี้ถูกต้อง
Blaisorblade

13

เนื่องจาก (คุณพูด) มันสมเหตุสมผลแล้วที่จะเรียก super onCreate ก่อน: ลองคิดดูสิ

เมื่อฉันต้องการสร้าง super ของฉันจะสร้างทรัพยากร> ฉันสร้างทรัพยากรของฉัน

ผกผัน: (เรียงซ้อน)

เมื่อฉันต้องการทำลายฉันทำลายทรัพยากรของฉัน> ซุปเปอร์ของฉันทำลายทรัพยากรของเขา


ในแง่นี้จะใช้กับสองฟังก์ชัน (onCreate / onDestroy, onResume / onPause, onStart / onStop) ตามธรรมชาติแล้ว onCreate จะสร้างทรัพยากรและ onDestroy จะปลดปล่อยทรัพยากรเหล่านี้ อย่างไรก็ตามหลักฐานเดียวกันนี้ใช้กับคู่อื่น ๆ

ให้เราพิจารณาไลบรารีที่คุณดาวน์โหลดซึ่งมี LocationActivity ที่มีฟังก์ชัน getLocation () ที่ระบุตำแหน่ง ส่วนใหญ่แล้วกิจกรรมนี้จะต้องเริ่มต้นสิ่งต่างๆใน onCreate () ซึ่งจะบังคับให้คุณเรียกใช้ super.onCreate ก่อน คุณทำอย่างนั้นอยู่แล้วเพราะคุณรู้สึกว่ามันสมเหตุสมผล ตอนนี้ใน onDestroy คุณตัดสินใจว่าต้องการบันทึกตำแหน่งไว้ที่ใดที่หนึ่งใน SharedPreferences หากคุณเรียก super.onDestroy ก่อนในระดับหนึ่งอาจเป็นไปได้ที่ getLocation จะส่งคืนค่าว่างหลังการเรียกนี้เนื่องจากการใช้งาน LocationActivity ทำให้ค่าตำแหน่งใน onDestroy เป็นโมฆะ แนวคิดก็คือคุณจะไม่ตำหนิหากสิ่งนี้เกิดขึ้น ดังนั้นคุณจะโทรหา super.onDestroy ในตอนท้ายหลังจากที่คุณทำ onDestroy เสร็จแล้ว ฉันหวังว่านี่จะสมเหตุสมผล

หากข้างต้นสมเหตุสมผลให้พิจารณาว่าในช่วงเวลาใดก็ตามที่เรามีกิจกรรมที่เป็นไปตามแนวคิดข้างต้น ถ้าฉันต้องการขยายกิจกรรมนี้ฉันอาจจะรู้สึกแบบเดียวกันและทำตามคำสั่งเดียวกันเพราะมีข้อโต้แย้งตรงกัน

โดยการเหนี่ยวนำกิจกรรมใด ๆ ควรทำในสิ่งเดียวกัน นี่คือคลาสนามธรรมที่ดีสำหรับกิจกรรมที่บังคับให้ปฏิบัติตามกฎเหล่านี้:

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

สุดท้ายจะเป็นอย่างไรหากกิจกรรมของคุณที่เรียกว่าAnudeepBullaActivityขยาย BaseActivity และในภายหลังฉันต้องการสร้างSherifElKhatibActivityสิ่งนี้เพื่อขยายกิจกรรมของคุณ ฉันควรเรียกใช้super.doฟังก์ชันในลำดับใด ในที่สุดมันก็เป็นสิ่งเดียวกัน


สำหรับคำถามของคุณ:

ฉันคิดว่าความตั้งใจของ Google คือต้องการบอกเรา: โปรดโทรหาซุปเปอร์ไม่ว่าจะอยู่ที่ไหน ในฐานะที่เป็นแนวทางปฏิบัติทั่วไปเรียกว่าในตอนต้น แน่นอนว่า Google มีวิศวกรและนักพัฒนาที่ฉลาดที่สุดดังนั้นพวกเขาจึงอาจทำงานได้ดีในการแยกการโทรที่ยอดเยี่ยมและไม่รบกวนการโทรของเด็ก

ฉันพยายามเล็กน้อยและอาจไม่ใช่เรื่องง่าย (เนื่องจากเป็น Google เราจึงพยายามพิสูจน์ว่าผิด) ที่จะสร้างกิจกรรมที่จะผิดพลาดง่ายๆเนื่องจากเมื่อมีการเรียกสุด

ทำไม?

ทุกสิ่งที่ทำในฟังก์ชั่นเหล่านี้มีความเป็นส่วนตัวสำหรับคลาสกิจกรรมและจะไม่ทำให้เกิดความขัดแย้งกับคลาสย่อยของคุณ ตัวอย่างเช่น (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 และ mManagedDialogs และ mSearchManager เป็นฟิลด์ส่วนตัวทั้งหมด และ API สาธารณะ / ที่ได้รับการป้องกันจะไม่ได้รับผลกระทบจากสิ่งที่ทำที่นี่

อย่างไรก็ตามใน API 14 มีการเพิ่ม dispatchActivityDestroyed เพื่อส่ง onActivityDestroyed ไปยัง ActivityLifecycleCallbacks ที่ลงทะเบียนกับแอปพลิเคชันของคุณ ดังนั้นรหัสใด ๆ ที่จะขึ้นอยู่กับตรรกะบางอย่างใน ActivityLifecycleCallbacks ของคุณจะมีผลลัพธ์ที่แตกต่างกันไปตามเวลาที่คุณเรียกใช้ super ตัวอย่างเช่น:

สร้างคลาสแอปพลิเคชันที่นับจำนวนกิจกรรมที่กำลังทำงานอยู่:

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) {}
}

สิ่งต่อไปนี้อาจไม่สมเหตุสมผลหรือไม่ใช่แนวทางปฏิบัติที่ดี แต่เป็นเพียงการพิสูจน์ประเด็นหนึ่ง (อาจพบสถานการณ์จริงมากกว่า) สร้าง MainActivity ที่คาดว่าจะเข้าสู่กิจกรรม GoodBye เมื่อเสร็จสิ้นและเมื่อเป็นกิจกรรมสุดท้าย:

@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));
    }
}

หากคุณโทรหา super.onDestroy ในช่วงเริ่มต้นของ onDestroy กิจกรรม GoodBye จะเปิดตัว หากคุณโทรหา super.onDestroy ในตอนท้ายของ onDestroy กิจกรรม GoodBye จะไม่เปิดตัว

แน่นอนอีกครั้งนี่ไม่ใช่ตัวอย่างที่ดีที่สุด อย่างไรก็ตามนี่แสดงให้เห็นว่า Google ยุ่งเล็กน้อยที่นี่ ตัวแปรอื่น ๆ จะไม่ส่งผลต่อพฤติกรรมของแอปของคุณ อย่างไรก็ตามการเพิ่มการจัดส่งเหล่านี้ไปยัง onDestroy ทำให้ super เข้ามารบกวนคลาสย่อยของคุณ

ฉันบอกว่าพวกเขายุ่งด้วยเหตุผลอื่นเช่นกัน พวกเขาไม่เพียง แต่ (ก่อน api 14) เท่านั้นที่สัมผัสใน super ที่เรียกว่าอะไรสุดท้ายและ / หรือส่วนตัว แต่ยังเรียกฟังก์ชันภายในที่แตกต่างกัน (ส่วนตัว) ซึ่งจะส่งฟังก์ชัน onPause ...

ตัวอย่างเช่นperformStopฟังก์ชันคือฟังก์ชันที่เรียกว่าฟังก์ชัน 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;
}

สังเกตว่าพวกเขาเรียกกิจกรรมว่า onStop ที่ไหนสักแห่งในฟังก์ชันนี้ ดังนั้นพวกเขาอาจใส่รหัสทั้งหมด (รวมอยู่ใน super.onStop) ก่อนหรือหลังการเรียกไปที่ onStop จากนั้นเพียงแจ้งคลาสย่อยเกี่ยวกับ onStop โดยใช้ฟังก์ชัน super onStop ที่ว่างเปล่าและไม่ต้องเพิ่ม SuperNotCalledException หรือตรวจสอบการเรียกนี้

สำหรับสิ่งนี้หากพวกเขาเรียกสิ่งนี้ว่าการจัดส่งไปยัง ActivityLifeCycle ใน performDestroy แทนที่จะเรียกมันในตอนท้ายของ super.onDestroy พฤติกรรมของกิจกรรมของเราจะเหมือนเดิมไม่ว่าเมื่อใดที่เราเรียก super

อย่างไรก็ตามนี่เป็นสิ่งแรกที่พวกเขาทำ (ผิดเล็กน้อย) และเป็นเพียงใน API 14


คำถามไม่เคยมีมาก่อนว่าทำไมถึงเรียก super.onDestroy () เป็นครั้งสุดท้าย ฉันชอบตัวอย่างห้องสมุดของคุณ สิ่งที่ฉันต้องการจะสื่อเกินไป ฉันไม่เห็นด้วยกับการเรียก super method ในช่วงครึ่งรอบสุดท้ายของการทำลายล้างเหมือนกับใน stack เพื่อป้องกันข้อมูลสูญหายโดยไม่ได้ตั้งใจ ปัญหาคือเหตุใด Google จึงยืนยันที่จะเรียกใช้ super method ก่อนตามหลักฐานข้างต้น ฉันถามคำถามนี้เพราะฉันคิดว่าบางทีฉันและดูเหมือนคุณด้วยอาจจะเข้าหามันต่างกันโดยสิ้นเชิง Cheers
Anudeep Bulla

โอ้ฉันไม่เห็นคำแนะนำของ Google และวิธีอื่น: p! ฟังนะฉันจะพยายามสร้างกิจกรรมที่จะพังถ้าคุณโทรหา onDestroy ก่อน คุณควรลองเช่นกัน ไชโย
Sherif elKhatib

@AnudeepBulla คุณสามารถตรวจสอบการแก้ไขของฉัน และคุณเลิกพยายามได้แล้ว super.onอาจจะไม่ทำให้กิจกรรมของคุณผิดพลาด
Sherif elKhatib

ว้าว. ขอบคุณสำหรับคำแนะนำ ทั้งคำตอบของผู้ใช้นี้และ @ ให้บริบทที่สำคัญ หากคุณคนใดคนหนึ่งสามารถสรุปคำตอบในหน้านี้ได้ฉันจะทำเครื่องหมายว่ายอมรับ กรุณาระบุ: 1. คำตอบในหน้านี้ 2. คำตอบของ @ Philip ในหน้านี้ 3. คำตอบของ @ CommonsWare ในหน้านี้ 4. การสนทนานี้ ฉันต้องการ แต่ฉันไม่ต้องการเครดิตสำหรับคำตอบที่ยอดเยี่ยมของคุณ Cheers & Thanks
Anudeep Bulla

@AnudeepBulla ยินดีต้อนรับค่ะ ฉันไม่แน่ใจว่าเราจะตัดสินใจอย่างไรว่าใครเป็นผู้โพสต์บทความสุดท้าย
Vikram

2

คุณบอกว่า Google ขอแนะนำวิธีที่ 1 แต่ Dianne Hackborn เป็นที่รู้จักกันดีวิศวกร Android กรอบขอแนะนำเป็นอย่างอื่นดูฟอรัม Google เชื่อมโยง

มันทำให้รู้สึกที่ใช้งานง่ายที่จะเรียกระดับซุปเปอร์สุดท้ายเมื่อทำลายเช่นในonPause, onStopและOnDestroyวิธีการและเป็นครั้งแรกเมื่อสร้างอินสแตนซ์ด้วยวิธีการของonCreate, onResumeและonStart


ลิงก์ไปยังโพสต์ของ Dianne Hackborn มีความสำคัญและยืนยันรูปแบบ
Nick Westgate

1

จากมุมมองของ java นี่คือทางออกสำหรับความสับสนนี้:

เหตุใดสิ่งนี้ () และ super () จึงต้องเป็นคำสั่งแรกในตัวสร้าง

ต้องเรียกตัวสร้าง 'คลาสแม่' ก่อนตัวสร้างคลาสย่อย เพื่อให้แน่ใจว่าหากคุณเรียกใช้เมธอดใด ๆ บนคลาสพาเรนต์ในคอนสตรัคเตอร์ของคุณคลาสพาเรนต์ได้รับการตั้งค่าอย่างถูกต้อง

สิ่งที่คุณกำลังพยายามทำส่ง args ไปยัง super constructor นั้นถูกกฎหมายอย่างสมบูรณ์คุณเพียงแค่ต้องสร้าง args เหล่านั้นแบบอินไลน์ในขณะที่คุณกำลังทำอยู่หรือส่งต่อไปยัง constructor ของคุณแล้วส่งต่อไปยัง super:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

หากคอมไพเลอร์ไม่ได้บังคับใช้สิ่งนี้คุณสามารถทำได้:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

มันแสดงให้เห็นว่าจริง ๆ แล้ว subfields จะต้องถูกฝังไว้ก่อน supreclass! ในขณะเดียวกันข้อกำหนดจาวาจะ "ปกป้อง" เราจากความเชี่ยวชาญในชั้นเรียนโดยเชี่ยวชาญสิ่งที่อาร์กิวเมนต์ super constructor

ในกรณีที่คลาสพาเรนต์มีคอนสตรัคเตอร์ดีฟอลต์การเรียกใช้ซูเปอร์จะถูกแทรกให้คุณโดยอัตโนมัติโดยคอมไพลเลอร์ เนื่องจากทุกคลาสใน Java สืบทอดมาจาก Object จึงต้องเรียกตัวสร้างอ็อบเจ็กต์อย่างใดอย่างหนึ่งและต้องดำเนินการก่อน การแทรกอัตโนมัติของ super () โดยคอมไพเลอร์ช่วยให้สิ่งนี้ บังคับให้ super ปรากฏขึ้นก่อนบังคับให้ตัวสร้างถูกดำเนินการตามลำดับที่ถูกต้องซึ่งจะเป็น: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

(1) การตรวจสอบว่า super เป็นคำสั่งแรกนั้นไม่เพียงพอที่จะป้องกันปัญหานั้น ตัวอย่างเช่นคุณสามารถใส่ "super (someMethodInSuper ());" ในตัวสร้างของคุณ สิ่งนี้พยายามเข้าถึงเมธอดใน superclass ก่อนที่จะสร้างแม้ว่า super จะเป็นคำสั่งแรกก็ตาม

(2) ดูเหมือนว่าคอมไพลเลอร์จะใช้การตรวจสอบที่แตกต่างกันซึ่งก็เพียงพอแล้วที่จะป้องกันปัญหานี้ ข้อความคือ "ไม่สามารถอ้างอิง xxx ก่อนที่จะมีการเรียกตัวสร้าง supertype" ดังนั้นการตรวจสอบว่า super เป็นคำสั่งแรกจึงไม่จำเป็น

โปรดอ่านhttp://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html


ฉันเข้าใจดีถึงสิ่งที่คุณนำเสนอ คุณเรียกผู้สร้างชั้นสูงก่อนว่า 'เพราะพวกเขาอาจเริ่มต้นทรัพยากรที่เด็กต้องการ และผู้ทำลายสุดท้าย 'เพราะคุณไม่ต้องการที่จะลบผู้ปกครองทั้งหมดไปที่ทรัพยากรในท้องถิ่นทำให้พวกเขาไม่มีจุดหมาย นั่นคือประเด็นของฉันอย่างแน่นอน และเนื่องจาก onPause, onStop และ onDestroy มีหน้าที่ในการบันทึกข้อมูลสถานะและทำให้ทรัพยากรพร้อมใช้งานมากขึ้นหรือน้อยลงสำหรับ GC (ดังนั้นในแง่หนึ่งก็คือการทำลายพวกเขา) ฉันเห็นว่าพวกเขาคล้ายคลึงกับผู้ทำลายและด้วยเหตุนี้จึงคิดว่าการเรียกพวกเขาครั้งสุดท้ายเหมาะสม ไม่?
Anudeep Bulla

ทุกสิ่งที่คุณกล่าวมาข้างต้นคือสิ่งที่ฉันหมายถึงเมื่อฉันพูดว่า "การเรียก super method ก่อนนั้นเหมาะสมกับครึ่งรอบการสร้าง" ฉันกังวลและสับสนในกรณีของวิธีการทำลายครึ่งรอบซึ่งดูเหมือนจะคล้ายคลึงกับผู้ทำลายล้างมากกว่า Cheers
Anudeep Bulla

1

สิ่งที่สำคัญที่สุดที่จะต้องจำไว้คือว่าสายโดยปริยายsuper.onPause() setResult(Activity.RESULT_CANCELED)แต่setResultสามารถเรียกได้เพียงครั้งเดียวและการโทรที่ตามมาทั้งหมดจะถูกละเว้น ดังนั้นหากคุณต้องการที่จะผลักดันชนิดของผลกลับไปยังกิจกรรมผู้ปกครองใด ๆ ที่คุณต้องเรียกsetResultตัวเองก่อนที่super.onPause()คุณเรียก นั่นคือ gotcha ที่ใหญ่ที่สุดเท่าที่ฉันรู้


ว้าวดูเหมือนจะสำคัญ ดังนั้นจึงมีสถานการณ์เมื่อคุณต้องชะลอการโทรไปยังวิธีการขั้นสูงอย่างแน่นอน ขอขอบคุณ.
Anudeep Bulla

super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED). คุณบอกได้ไหมว่าคุณได้สิ่งนี้มาจากไหน?
Vikram

ฉันสับสน. จริงๆแล้วมันเสร็จสิ้น () ที่เรียก setResult และ super.onBackPressed () เรียกเสร็จสิ้น () ดังนั้น setResult จึงต้องถูกเรียกก่อน super.onBackPressed () ฉันไม่แน่ใจว่ามีสถานการณ์ใดบ้างที่ super.onPause () อาจทำให้ถูกเรียกใช้ setResult ได้ แต่ฉันไม่ต้องการเสี่ยง
Philip Sheard

1

ทั้งสองเป็น IMO ที่ถูกต้อง

ตามเอกสาร

คลาสที่ได้รับจะต้องเรียกผ่านเพื่อใช้วิธีนี้ของซูเปอร์คลาส หากไม่ทำเช่นนั้นข้อยกเว้นจะถูกโยนทิ้ง

Super ควรเรียกวิธีการเสมอเมื่อเอกสารระบุอย่างชัดเจน

อย่างไรก็ตามคุณสามารถเลือกเวลาที่จะโทรหา super method ได้

กำลังดูที่มาของ onPause

protected void onPause() {
    getApplication().dispatchActivityPaused(this);
    mCalled = true;
}

ดังนั้นไม่ว่าก่อนหรือหลังจะถูกเรียก คุณน่าจะดี

แต่เพื่อแนวทางปฏิบัติที่ดีที่สุดคุณควรเรียกมันก่อน

ฉันแนะนำให้ใช้เป็นกลไกการป้องกันเป็นส่วนใหญ่: หากมีข้อยกเว้นsuperจะมีการเรียกวิธีการอินสแตนซ์ไปแล้ว

นอกจากนี้การวางสายเหล่านี้ในบรรทัดแรกจะช่วยให้คุณหลีกเลี่ยงการทำผิดพลาดในอนาคตเช่นการลบโค้ดในวิธีการและการลบการโทรไปยังซุปเปอร์คลาสโดยไม่ตั้งใจ


ขออภัยหากคำถามไม่ชัดเจนในครั้งแรก แต่โปรดดูตอนนี้
Anudeep Bulla

@AnudeepBulla นั่นคือสิ่งที่ฉันได้อธิบายคุณ คุณสามารถใช้อย่างใดอย่างหนึ่ง ใช้ได้ทั้งคู่
Sunil Mishra

ฉันเข้าใจว่าการนำเมธอด onPause, onStop และ onDestroy มาใช้แบบกำหนดเองนั้นไม่จำเป็นอย่างยิ่ง ฉันได้สร้างแอปที่เพียงพอโดยไม่มีแอปเหล่านั้น ดังนั้นคุณหมายถึงอะไรโดย Super method ควรเรียกเสมอ? พวกเขาถูกเรียกโดยปริยายแม้ว่าจะไม่มีการลบล้างก็ตาม ฉันยังรู้ว่าทั้งสองทำงาน ฉันอยากรู้ว่าทำไมเอกสารถึงบอกว่าควรเรียก super ก่อน และในกรณีที่คำถามยังไม่ชัดเจนคุณช่วยอธิบายได้ไหมว่าทำไมเมื่อคุณพูดว่า "แต่เพื่อแนวทางปฏิบัติที่ดีที่สุดคุณควรเรียกก่อน"
Anudeep Bulla

หากคุณไม่ลบล้างเมธอดจะถูกเรียกจากBase Classแต่ถ้าคุณลบล้างคุณจำเป็นต้องเรียกสิ่งsuperอื่นที่คุณจะมีandroid.app.SuperNotCalledException
Sunil Mishra

1
ดูเหมือนคุณจะเข้าใจผิด คำถามไม่ได้อยู่ที่ว่าจะโทรหรือไม่ เช่นเดียวกับที่คุณชี้ให้เห็นคุณต้องทำหากคุณลบล้างสิ่งเหล่านี้ คำถามคือเมื่อไหร่?
Anudeep Bulla

0

จำเป็นต้องใช้ super ของการเรียกกลับเพื่อทำให้กิจกรรมอยู่ในสถานะที่ถูกต้องภายในสำหรับระบบ

สมมติว่าคุณเริ่มกิจกรรมของคุณและระบบจะเรียก onCreate ตอนนี้คุณสามารถลบล้างและเช่นโหลดเค้าโครงของคุณ แต่เพื่อประโยชน์ของการไหลของระบบคุณต้องเรียกซูเปอร์เพื่อให้ระบบดำเนินการต่อตามขั้นตอนมาตรฐานได้ นั่นเป็นเหตุผลที่ข้อยกเว้นจะถูกโยนทิ้งหากคุณไม่เรียกมัน

สิ่งนี้เกิดขึ้นโดยไม่ขึ้นกับการใช้งานของคุณใน onCreate เป็นเพียงการนำเข้าสำหรับระบบเท่านั้น หากไม่มี ANR คุณอาจมีการวนซ้ำไม่รู้จบในการเรียกกลับใด ๆ และกิจกรรมจะติดอยู่ในรายการนั้น ดังนั้นระบบจะรู้ว่าเมื่อใดที่การโทรกลับถูกยกเลิกและโทรกลับมากกว่าการโทรครั้งถัดไป

ฉันรู้เพียงสถานการณ์เดียวที่จำเป็นต้องใช้เวลาในการโทรพิเศษ หากคุณต้องการปรับเปลี่ยนลักษณะการทำงานมาตรฐานของธีมหรือการแสดงผลใน onCreate คุณต้องทำก่อนที่จะเรียก super เพื่อดูเอฟเฟกต์ ไม่เช่นนั้น AFAIK จะไม่มีความแตกต่างในเวลาที่คุณเรียกมัน

แต่เพื่อให้ระบบทำในสิ่งที่ดีที่สุดที่จะทำให้ super อยู่ในบรรทัดแรกของการโทรกลับตามด้วยรหัสของคุณหากคุณไม่มีเหตุผลที่ดีที่จะทำลายมัน


ลำดับใน onCreate ดูเหมือนจะเข้าใจได้ดีทีเดียว เกิดอะไรขึ้นในวิธีการทำลายล้าง? พูด onStop สมมติว่าการใช้งาน onStop ของฉันใช้ทรัพยากรบางอย่างที่วิธีการขั้นสูงเผยแพร่หากเรียกมันสมเหตุสมผลแล้วที่จะเรียก super method หลังจากการใช้งาน
Anudeep Bulla

ตามหลักการแล้วควรเป็นกรณีนี้ กิจกรรมของเราจะมีแหล่งข้อมูลที่คลาสระดับสูงมีอยู่เสมอและอื่น ๆ อีกมากมายและสิ่งที่เป็นอิสระต่อกิจกรรมของฉันในกรณีส่วนใหญ่ขึ้นอยู่กับทรัพยากรระดับสูงซึ่งเป็นเรื่องธรรมดา มันสมเหตุสมผลกว่าที่จะจัดการกับทรัพยากรของฉันก่อนจากนั้นจึงเรียกซูเปอร์คลาสเพื่อจัดการกับคนทั่วไป เหตุใด Google จึงบอกว่าเรา "ควร" เรียกวิธีการระดับสูงก่อน
Anudeep Bulla

คุณพูดถึงแหล่งข้อมูลใดซึ่งคุณสามารถเข้าถึงได้ใน onCreate แต่ไม่สามารถเข้าถึงได้ใน onDestroy
Steve Benett

ฉันไม่มีกรณีการใช้งาน ฉันแค่สงสัยว่ามันแตกต่างกันอย่างไรตามสไตล์ OOP ตัวสร้างชั้นสูงจะถูกเรียกก่อนก่อนการใช้งานของคุณและตัวทำลายระดับซุปเปอร์จะถูกเรียกเป็นตัวสุดท้ายหลังจากการใช้งานของคุณใช่ไหม onPause, onStop และ onDestroy ไม่ใช่ตัวทำลายอย่างเคร่งครัด แต่พวกเขามักจะทำสิ่งเดียวกันใช่ไหม? Atleast onDestroy และส่วนใหญ่เป็น onStop ด้วย .. ไม่?
Anudeep Bulla

ดูการเรียกกลับในสถานะที่เปลี่ยนวิธีที่ไม่ใช่ตัวสร้าง / ตัวทำลาย ทุกการโทรกลับจำเป็นต้องมีเช่นสร้าง (พร้อมใช้งาน) / ทำลาย (โอกาสสุดท้ายในการโต้ตอบ) กิจกรรมหรือวางไว้ในเบื้องหน้า / พื้นหลัง การเรียกกลับอยู่ที่นั่นเพื่อให้คุณสามารถควบคุมทรัพยากรของคุณในการไหลเวียนของระบบ ระบบจะตรวจสอบว่าอยู่ในสถานะใดและจัดการตามนั้น ทรัพยากรที่คุณใช้และทรัพยากรที่ระบบควบคุมนั้นเป็นอิสระจากกันและจะไม่มีทางแยก
Steve Benett
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.