Thứ tự chính xác của việc gọi các phương thức siêu lớp trong các phương thức onPause, onStop và onDestroy là gì? và tại sao?


89

Tôi vừa duyệt qua Trang web dành cho nhà phát triển Android, làm mới trên Vòng đời hoạt động và trong mỗi ví dụ mã, có một nhận xét bên cạnh các phương thức siêu lớp cho biết "Luôn gọi phương thức siêu lớp trước".

Mặc dù điều này có ý nghĩa trong nửa chu kỳ tạo: onCreate, onStart và onResume, tôi hơi bối rối không biết đâu là quy trình chính xác trong nửa chu kỳ hủy: onPause, onStop, onDestroy.

Việc phá hủy các tài nguyên cụ thể của cá thể trước, trước khi phá hủy các tài nguyên lớp cha mà các tài nguyên cụ thể của cá thể đó có thể phụ thuộc vào có ý nghĩa, chứ không phải ngược lại. Tôi đang thiếu gì?

Chỉnh sửa : Vì mọi người dường như đang bối rối với ý định trong câu hỏi, điều tôi muốn biết là điều nào sau đây là đúng? VÀ TẠI SAO ?

1. Google gợi ý

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

      //my implementation here
    }

2. cách khác

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

       super.onStop();  
    }

1
Tôi đang ở trại hai cho các phương pháp tắt máy. Tôi đang ở trại một về các phương pháp khởi động.
danny117

1
Đó là khá nhiều điểm. Chỉ không thể hiểu cách sử dụng phương pháp 1 cho các phương pháp tắt máy có ý nghĩa như thế nào.
Anudeep Bulla

Câu trả lời:


107

Việc phá hủy các tài nguyên cụ thể của cá thể trước, trước khi phá hủy các tài nguyên lớp cha mà các tài nguyên cụ thể của cá thể đó có thể phụ thuộc vào có ý nghĩa, chứ không phải ngược lại. Nhưng các ý kiến ​​đề xuất khác. Tôi đang thiếu gì?

Theo tôi: không phải là một điều duy nhất.

Câu trả lời này từ Mark (hay còn gọi là CommonsWare on SO) làm sáng tỏ vấn đề: Liên kết - Lời gọi phương thức siêu lớp có nên là câu lệnh đầu tiên không? . Nhưng sau đó, bạn có thể thấy bình luận sau để lại câu trả lời của anh ấy:

Nhưng tại sao tài liệu chính thức lại nói: "Luôn gọi phương thức siêu lớp trước" trong onPause ()?

Làm lại từ đầu. Được rồi, hãy nhìn điều này từ một góc độ khác. Chúng tôi biết rằng Đặc tả ngôn ngữ Java không chỉ định thứ tự mà lệnh gọi super.overridenMethod()phải được đặt (hoặc nếu lệnh gọi phải được thực hiện).

Trong trường hợp Hoạt động của lớp, super.overridenMethod()các lệnh gọi là bắt buộc và được thực thi :

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

mCalledđược đặt thành true trong Activity.onStop().

Bây giờ, chi tiết duy nhất còn lại để tranh luận là thứ tự.

I also know that both work

Chắc chắn rồi. Nhìn vào phần thân phương thức cho 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;
}

Dù bạn gọi theo cách nào super.onPause(), bạn sẽ ổn. Activity.onStop () có một phần thân phương thức tương tự. Nhưng hãy xem 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);
}

Ở đây, việc đặt hàng có thể vấn đề tùy thuộc vào cách hoạt động của bạn được thiết lập và việc gọi điện super.onDestroy()có ảnh hưởng đến mã sau đó hay không.

Cuối cùng, tuyên bố Always call the superclass method firstnày dường như không có nhiều bằng chứng để chứng minh. Điều tồi tệ hơn (đối với câu lệnh) là đoạn mã sau đã được lấy từ android.app.ListActivity:

public class ListActivity extends Activity {

    ....

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

Và, từ ứng dụng mẫu LunarLander có trong sdk android:

public class LunarLander extends Activity {

    ....

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

Tóm tắt và đề cập xứng đáng:

Người dùng Philip Sheard : Cung cấp một kịch bản trong đó cuộc gọi đến super.onPause()phải bị trì hoãn trong trường hợp Hoạt động bắt đầu được sử dụng startActivityForResult(Intent). Đặt kết quả bằng setResult(...) after super.onPause() sẽ không hoạt động. Sau đó, anh ấy làm rõ điều này trong các bình luận cho câu trả lời của mình.

Người dùng Sherif elKhatib : Giải thích lý do tại sao để lớp siêu khởi tạo tài nguyên của nó trước và hủy tài nguyên của nó sau cùng dựa trên logic:

Hãy để chúng tôi xem xét thư viện bạn đã tải xuống có LocationActivity chứa hàm getLocation () cung cấp vị trí. Rất có thể, Do đó, bạn sẽ gọi super.onDestroy ở cuối sau khi bạn hoàn thành với onDestroy của riêng mình. hoạt động này sẽ cần khởi tạo nội dung của nó trong onCreate (), điều này sẽ buộc bạn phải gọi super.onCreate trước . Bạn đã làm điều đó bởi vì bạn cảm thấy nó có ý nghĩa. Bây giờ, trong onDestroy của bạn, bạn quyết định muốn lưu Vị trí ở đâu đó trong SharedPreferences. Nếu bạn gọi super.onDestroy đầu tiên, có thể ở một mức độ nhất định getLocation sẽ trả về giá trị null sau lệnh gọi này vì việc triển khai LocationActivity sẽ vô hiệu hóa giá trị vị trí trong onDestroy. Ý tưởng là bạn sẽ không đổ lỗi nếu điều này xảy ra.

Ông tiếp tục chỉ ra rằng: nếu một lớp con được cách ly phù hợp (về mặt phụ thuộc tài nguyên) với lớp cha, thì các super.X()lệnh gọi không cần tuân theo bất kỳ đặc tả thứ tự nào.

Xem câu trả lời của ông trên trang này để đọc qua một kịch bản mà vị trí của super.onDestroy()cuộc gọi không ảnh hưởng đến các chương trình logic.

Từ một câu trả lời của Mark :

Các phương thức bạn ghi đè là một phần của quá trình tạo thành phần (onCreate (), onStart (), onResume (), v.v.), bạn nên chuỗi vào lớp cha như câu lệnh đầu tiên , để đảm bảo rằng Android có cơ hội thực hiện công việc của mình trước bạn cố gắng làm điều gì đó dựa trên công việc đã được hoàn thành.

Các phương thức bạn ghi đè là một phần của việc phá hủy thành phần (onPause (), onStop (), onDestroy (), v.v.), bạn nên thực hiện công việc của mình trước tiên và chuỗi đến lớp cha là điều cuối cùng . Bằng cách đó, trong trường hợp Android dọn dẹp thứ gì đó mà công việc của bạn phụ thuộc vào, bạn sẽ hoàn thành công việc của mình trước.

Các phương thức trả về thứ gì đó không phải void (onCreateOptionsMenu (), v.v.), đôi khi bạn xâu chuỗi đến lớp cha trong câu lệnh trả về, giả sử rằng bạn không cụ thể làm điều gì đó cần buộc một giá trị trả về cụ thể.

Mọi thứ khác - chẳng hạn như onActivityResult () - nói chung là tùy thuộc vào bạn. Tôi có xu hướng xâu chuỗi với lớp cha là điều đầu tiên, nhưng trừ khi bạn đang gặp sự cố, việc xâu chuỗi sau sẽ ổn.

Bob Kerns từ chủ đề này :

Đó là một mô hình tốt [(mô hình mà Mark gợi ý ở trên)], nhưng tôi đã tìm thấy một số ngoại lệ. Ví dụ, chủ đề tôi muốn áp dụng cho PreferenceActivity của mình sẽ không có hiệu lực trừ khi tôi đặt nó trước onCreate () của lớp cha.

Người dùng Steve Benett cũng chú ý đến điều này:

Tôi chỉ biết một tình huống, thời điểm của cuộc gọi siêu cấp là cần thiết. Nếu bạn muốn thay đổi hành vi tiêu chuẩn của chủ đề hoặc màn hình và tương tự như vậy trong onCreate, bạn phải làm điều đó trước khi gọi super để xem hiệu ứng . Nếu không thì AFAIK không có sự khác biệt tại thời điểm bạn gọi nó.

Người dùng Sunil Mishra xác nhận rằng thứ tự (rất có thể) không đóng vai trò gì khi gọi các phương thức của lớp Activity. Anh ấy cũng tuyên bố rằng gọi các phương thức siêu lớp trước được coi là một phương pháp hay nhất . Tuy nhiên, tôi không thể chứng thực điều này.

Tài LOG_TAG : Giải thích lý do tại sao một cuộc gọi đến lớp cha constructor cần phải được trước mọi thứ khác. Theo tôi, lời giải thích này không thêm vào câu hỏi được đặt ra.

Lưu ý cuối : Tin tưởng, nhưng xác minh. Hầu hết các câu trả lời trên trang này đều tuân theo cách tiếp cận này để xem liệu câu lệnh Always call the superclass method firstcó sự hỗ trợ logic hay không. Hóa ra, nó không; ít nhất, không phải trong trường hợp Hoạt động lớp. Nói chung, người ta nên đọc qua mã nguồn của lớp cha để xác định xem việc sắp xếp các lệnh gọi đến các phương thức của lớp siêu có phải là một yêu cầu hay không.


2
Chà. Cảm ơn vì những gợi ý. Cả câu trả lời này và câu trả lời của @ Sherif đều cung cấp bối cảnh quan trọng. Nếu một trong hai người có thể tóm tắt các câu trả lời trên trang này, tôi sẽ đánh dấu nó là được chấp nhận. Vui lòng bao gồm: 1. câu trả lời trên trang này. 2. Câu trả lời của @ Philip trên trang này 3. Câu trả lời của @ CommonsWare trên trang này 4. Tôi sẽ thảo luận này, nhưng tôi không muốn ghi công cho những câu trả lời tuyệt vời của bạn. Chúc mừng và cảm ơn
Anudeep Bulla

Chào. Bạn có thể vui lòng tóm tắt lại, nếu @Sherif không muốn?
Anudeep Bulla

@AnudeepBulla Chào Anudeep, cho tôi đến ngày mai. Tôi sẽ thêm tài liệu có liên quan vào câu trả lời của mình và để lại cho bạn nhận xét ở đây.
Vikram

@AnudeepBulla Tôi đã thêm một bản tóm tắt ở trên. Vui lòng cho tôi biết trong trường hợp tôi bỏ lỡ bất cứ điều gì.
Vikram

@Vikram Là TL; DR. rằng gọi onDestroyonStopcuối cùng là một mặc định an toàn hơn và đối với onPausemọi thứ có thể phức tạp hơn trong một vài trường hợp? Bạn có thể thêm nó đầu tiên? Tôi đã muốn tự mình chỉnh sửa câu trả lời nhưng tôi không chắc bản tóm tắt này là chính xác.
Blaisorblade

12

Vì (bạn nói) nên gọi super onCreate trước: Hãy nghĩ về nó.

Khi tôi muốn tạo, Cấp cao của tôi tạo tài nguyên của nó> Tôi tạo tài nguyên của tôi.

Ngược lại: (loại một ngăn xếp)

Khi tôi muốn phá hủy, tôi phá hủy tài nguyên của tôi> Siêu của tôi phá hủy tài nguyên của anh ta.


Theo nghĩa này, nó áp dụng cho bất kỳ hàm nào (onCreate / onDestroy, onResume / onPause, onStart / onStop). Đương nhiên, onCreate sẽ tạo ra các tài nguyên và onDestroy sẽ giải phóng các tài nguyên này. Nhân tiện, chứng minh tương tự cũng áp dụng cho các cặp đôi khác.

Hãy để chúng tôi xem xét thư viện bạn đã tải xuống có LocationActivity chứa hàm getLocation () cung cấp vị trí. Rất có thể, hoạt động này sẽ cần khởi tạo nội dung của nó trong onCreate (), điều này sẽ buộc bạn phải gọi super.onCreate trước. Bạn đã làm điều đó bởi vì bạn cảm thấy nó có ý nghĩa. Bây giờ, trong onDestroy của bạn, bạn quyết định muốn lưu Vị trí ở đâu đó trong SharedPreferences. Nếu bạn gọi super.onDestroy đầu tiên, thì ở một mức độ nhất định có thể getLocation sẽ trả về giá trị null sau lệnh gọi này vì việc triển khai LocationActivity sẽ vô hiệu hóa giá trị vị trí trong onDestroy. Ý tưởng là bạn sẽ không đổ lỗi nếu điều này xảy ra. Do đó, bạn sẽ gọi super.onDestroy ở cuối sau khi hoàn tất với onDestroy của riêng mình. Tôi hy vọng điều này có ý nghĩa một chút.

Nếu điều trên hợp lý, hãy xem xét rằng tại bất kỳ thời điểm nào chúng ta có một hoạt động tuân theo khái niệm trên. Nếu tôi muốn mở rộng hoạt động này, tôi có thể sẽ cảm thấy như vậy và tuân theo cùng một thứ tự vì cùng một đối số chính xác.

Bằng cách cảm ứng, bất kỳ hoạt động nào cũng nên làm điều tương tự. Đây là một lớp trừu tượng tốt cho một hoạt động buộc phải tuân theo các quy tắc sau:

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

Cuối cùng, điều gì sẽ xảy ra nếu hoạt động của bạn được gọi là AnudeepBullaActivitymở rộng BaseActivity và sau này, tôi muốn tạo SherifElKhatibActivityhoạt động đó mở rộng hoạt động của bạn? Tôi nên gọi các super.dohàm theo thứ tự nào? Nó cuối cùng là một điều giống nhau.


Đối với câu hỏi của bạn:

Tôi nghĩ rằng ý định của Google là muốn nói với chúng tôi rằng: Hãy gọi cho siêu cấp dù ở đâu. Tất nhiên, như một thông lệ chung, hãy gọi nó vào đầu. Tất nhiên, Google có những kỹ sư và nhà phát triển sáng giá nhất nên họ có thể đã làm rất tốt việc cô lập các cuộc gọi cấp cao của mình và không can thiệp vào các cuộc gọi con.

Tôi đã thử một chút và có lẽ không dễ dàng (vì Google chúng tôi đang cố gắng chứng minh là sai) để tạo một hoạt động có thể gặp sự cố đơn giản vì When is super được gọi.

Tại sao?

Mọi thứ được thực hiện trong các hàm này thực sự là riêng tư đối với lớp Activity và sẽ không bao giờ gây ra bất kỳ xung đột nào với lớp con của bạn. Ví dụ (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 và mManagedDialogs và mSearchManager đều là các trường riêng tư. Và không có api công khai / được bảo vệ nào sẽ bị ảnh hưởng bởi những gì được thực hiện ở đây.

Tuy nhiên, trong API 14, sendActivityDestroyed đã được thêm vào để gửi một onActivityDestroyed đến ActivityLifecycleCallbacks đã đăng ký với Ứng dụng của bạn. Do đó, bất kỳ mã nào phụ thuộc vào một số logic trong ActivityLifecycleCallbacks của bạn sẽ có một kết quả khác dựa trên thời điểm bạn đang gọi super. Ví dụ:

Tạo một Lớp ứng dụng đếm số lượng hoạt động hiện đang chạy:

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

Những điều sau đây có thể không có ý nghĩa hoặc không phải là một thông lệ tốt nhưng nó chỉ để chứng minh một quan điểm (Người ta có thể tìm thấy một tình huống thực tế hơn). Tạo MainActivity được cho là chuyển đến hoạt động GoodBye khi nó kết thúc và khi nó là hoạt động cuối cùng:

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

Nếu bạn gọi super.onDestroy ở đầu onDestroy, hoạt động GoodBye sẽ được khởi chạy. Nếu bạn gọi super.onDestroy ở cuối onDestroy, hoạt động GoodBye sẽ không được khởi chạy.

Tất nhiên, một lần nữa, đây không phải là ví dụ tối ưu. Tuy nhiên, điều này cho thấy Google đã làm sai một chút ở đây. Bất kỳ biến nào khác sẽ không ảnh hưởng đến hành vi của ứng dụng của bạn. Tuy nhiên, việc thêm các công văn này vào onDestroy đã khiến cấp trên bằng cách nào đó can thiệp vào lớp con của bạn.

Tôi nói rằng họ đã nhầm lẫn vì một lý do khác. Họ không chỉ (trước api 14) chỉ chạm vào các lệnh gọi siêu cấp là cuối cùng và / hoặc riêng tư, mà họ còn gọi các chức năng nội bộ khác nhau (riêng tư) thực sự sau đó gửi các chức năng onPause ...

Ví dụ, performStophàm là hàm được gọi mà lần lượt gọi hàm 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;
}

Lưu ý rằng họ gọi OnStop của Activity ở đâu đó trong hàm này. Do đó, họ cũng có thể đặt tất cả mã (bao gồm trong super.onStop) trước hoặc sau lệnh gọi đến onStop và sau đó chỉ cần thông báo cho các lớp con về onStop bằng cách sử dụng các siêu chức năng onStop trống và thậm chí không cần thêm SuperNotCalledException hoặc kiểm tra điều này được gọi.

Đối với điều này, nếu họ gọi công văn này đến ActivityLifeCycle trong performanceDestroy thay vì gọi nó ở cuối super.onDestroy, thì hoạt động của chúng ta sẽ giống nhau bất kể khi nào chúng ta gọi super.

Dù sao đây là điều đầu tiên họ làm (hơi sai) và nó chỉ có trong API 14.


Câu hỏi chưa bao giờ là tại sao gọi super.onDestroy () cuối cùng lại có ý nghĩa. Tôi thích ví dụ thư viện của bạn. Chính xác những gì tôi cũng muốn truyền tải. Tôi không thể đồng ý hơn về việc gọi các phương thức siêu cuối cùng vào nửa chu kỳ hủy cuối cùng, giống hệt như trong một ngăn xếp; để ngăn chặn việc mất dữ liệu ngẫu nhiên. Vấn đề là tại sao Google lại nhấn mạnh vào việc gọi các phương thức siêu trước, với tiền đề trên? Tôi đặt câu hỏi vì tôi nghĩ rằng có lẽ tôi và dường như bạn cũng vậy, có thể tiếp cận nó hoàn toàn khác. Cheers
Anudeep Bulla

Ồ, tôi không thấy đề xuất của Google và cách khác: p! Nghe này, tôi sẽ cố gắng tạo một hoạt động sẽ bị lỗi nếu bạn gọi onDestroy trước. Bạn cũng nên thử điều đó. Cheers
Sherif elKhatib

@AnudeepBulla bạn có thể kiểm tra các chỉnh sửa của tôi. Và btw bạn có thể ngừng cố gắng. super.oncó thể sẽ không bao giờ làm hỏng hoạt động của bạn.
Sherif elKhatib,

Chà. Cảm ơn vì những gợi ý. Cả câu trả lời này và câu trả lời của Người dùng @ đều cung cấp ngữ cảnh quan trọng. Nếu một trong hai người có thể tóm tắt các câu trả lời trên trang này, tôi sẽ đánh dấu nó là được chấp nhận. Vui lòng bao gồm: 1. câu trả lời trên trang này. 2. Câu trả lời của @ Philip trên trang này 3. Câu trả lời của @ CommonsWare trên trang này 4. Tôi sẽ thảo luận này, nhưng tôi không muốn ghi công cho những câu trả lời tuyệt vời của bạn. Cheers & Thanks
Anudeep Bulla

@AnudeepBulla Không có chi. Tôi không chắc chúng ta sẽ quyết định ai là người đăng bài viết cuối cùng.
Vikram

1

Từ quan điểm java, đây là một số giải pháp cho sự nhầm lẫn này:

Tại sao this () và super () phải là câu lệnh đầu tiên trong một hàm tạo?

Phương thức khởi tạo 'của lớp cha cần được gọi trước phương thức khởi tạo của lớp con'. Điều này sẽ đảm bảo rằng nếu bạn gọi bất kỳ phương thức nào trên lớp cha trong hàm tạo của mình, thì lớp cha đã được thiết lập chính xác.

Những gì bạn đang cố gắng làm, chuyển args cho hàm tạo siêu là hoàn toàn hợp pháp, bạn chỉ cần tạo nội tuyến các args đó như bạn đang làm hoặc chuyển chúng vào hàm tạo của bạn và sau đó chuyển chúng cho super:

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

Nếu trình biên dịch không thực thi điều này, bạn có thể thực hiện điều này:

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

Nó cho thấy rằng trên thực tế, các trường con phải được hủy bỏ trước lớp tối ưu! Trong khi đó, yêu cầu java "bảo vệ" chúng ta khỏi việc chuyên biệt hóa lớp bằng cách chuyên biệt hóa đối số của hàm tạo siêu

Trong trường hợp lớp cha có phương thức khởi tạo mặc định, trình biên dịch sẽ tự động chèn vào cho bạn lệnh gọi tới super. Vì mọi lớp trong Java đều kế thừa từ Object, nên phương thức khởi tạo đối tượng phải được gọi bằng cách nào đó và nó phải được thực thi trước. Việc tự động chèn super () bởi trình biên dịch cho phép điều này. Việc ép buộc super xuất hiện trước, thực thi các phần tử khởi tạo được thực thi theo đúng thứ tự sẽ là: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

(1) Kiểm tra rằng super là câu lệnh đầu tiên là không đủ để ngăn chặn vấn đề đó. Ví dụ: bạn có thể đặt "super (someMethodInSuper ());" trong hàm tạo của bạn. Điều này cố gắng truy cập một phương thức trong lớp cha trước khi nó được xây dựng, mặc dù super là câu lệnh đầu tiên.

(2) Trình biên dịch dường như thực hiện một kiểm tra khác, tự nó, đủ để ngăn chặn sự cố này. Thông báo là "không thể tham chiếu xxx trước khi phương thức tạo siêu kiểu được gọi". Do đó, việc kiểm tra xem super có phải là câu lệnh đầu tiên không là không cần thiết

Vui lòng xem qua http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html này


Tôi hoàn toàn hiểu những gì bạn đang đặt ra ở đó. Bạn gọi các hàm tạo siêu lớp trước, vì chúng có thể khởi tạo các tài nguyên mà trẻ có thể cần; và các trình hủy cuối cùng, 'coz bạn không muốn xóa tất cả cha mẹ cho các tài nguyên cục bộ, làm cho chúng trở nên vô nghĩa. Đó là quan điểm của tôi chính xác. Và vì onPause, onStop và onDestroy có công việc lưu thông tin trạng thái và ít nhiều cung cấp tài nguyên cho GC (vì vậy, theo một nghĩa nào đó, tiêu diệt chúng), tôi thấy chúng tương tự như trình hủy và do đó nghĩ rằng việc gọi chúng là cuối cùng có ý nghĩa. Không?
Anudeep Bulla

Tất cả những gì bạn nói ở trên là chính xác những gì tôi muốn nói khi tôi nói "việc gọi các phương pháp siêu có ý nghĩa đầu tiên trong nửa chu kỳ tạo". Tôi lo lắng và bối rối trong trường hợp của các phương pháp nửa chu kỳ phá hủy, có vẻ giống với phương pháp hủy hơn. Cheers
Anudeep Bulla

1

Điều quan trọng nhất cần ghi nhớ là super.onPause()các cuộc gọi ngầm setResult(Activity.RESULT_CANCELED). Nhưng setResultchỉ có thể được gọi một lần và tất cả các cuộc gọi tiếp theo đều bị bỏ qua. Vì vậy, nếu bạn muốn đẩy bất kỳ loại kết quả nào trở lại hoạt động phụ huynh, bạn phải gọi setResultcho chính mình, trước khi bạn gọi super.onPause(). Đó là gotcha lớn nhất, theo như tôi biết.


Wow, điều này có vẻ quan trọng. Vì vậy, có một tình huống, khi bạn chắc chắn phải trì hoãn cuộc gọi đến các phương thức siêu. Cảm ơn bạn.
Anudeep Bulla

super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED). Bạn có thể nói bạn lấy cái này từ đâu không?
Vikram

Tôi đã nhầm lẫn. Nó thực sự là finish () gọi setResult và super.onBackPressed () gọi finish (). Vì vậy, setResult chắc chắn phải được gọi trước super.onBackPressed (). Tôi không chắc liệu có bất kỳ trường hợp nào mà super.onPause () có thể khiến setResult được gọi hay không, nhưng tôi không muốn mạo hiểm.
Philip Sheard

1

CẢ HAI đều đúng IMO

Theo tài liệu

Các lớp dẫn xuất phải gọi đến việc thực thi phương thức này của lớp siêu. Nếu không, một ngoại lệ sẽ được ném ra.

Super phương thức phải luôn được gọi khi tài liệu nói rõ ràng như vậy.

Tuy nhiên, bạn có thể chọn thời điểm gọi phương thức siêu.

Nhìn vào nguồn của onPause

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

Do đó bất kể trước hay sau khi nó được gọi. Bạn nên tốt.

Nhưng để thực hành tốt nhất, bạn nên gọi nó trước.

Tôi đề xuất nó chủ yếu như một cơ chế bảo vệ: nếu có một ngoại lệ thì superphương thức thể hiện sẽ được gọi.

Ngoài ra việc đặt những lời gọi này lên dòng đầu tiên sẽ giúp bạn tránh mắc phải những sai lầm trong tương lai chẳng hạn như xóa mã trong phương thức và vô tình xóa cuộc gọi đến siêu lớp.


Tôi xin lỗi nếu lần đầu tiên câu hỏi không hoàn toàn rõ ràng, nhưng vui lòng xem nó ngay bây giờ.
Anudeep Bulla

@AnudeepBulla Đó là những gì tôi đã giải thích cho bạn. Bạn có thể sử dụng một trong hai. Cả hai đều hợp lệ.
Sunil Mishra,

Tôi hiểu rằng việc triển khai tùy chỉnh các phương thức onPause, onStop và onDestroy là không cần thiết. Tôi đã tạo ra nhiều ứng dụng mà không có những thứ đó. Vì vậy, những gì bạn có nghĩa là bởi Super phương pháp luôn luôn được gọi là? Chúng được gọi ngầm, ngay cả khi không ghi đè. Tôi cũng biết rằng cả hai đều có tác dụng. Tôi muốn biết lý do tại sao các tài liệu nói rằng nên gọi super trước. Và trong trường hợp câu hỏi vẫn chưa rõ ràng, bạn có vui lòng giải thích TẠI SAO không, khi bạn nói "Nhưng để thực hành tốt nhất, bạn nên gọi nó trước"?
Anudeep Bulla

Nếu bạn không ghi đè lên, các phương pháp này được gọi là từ Base Classnhưng nếu bạn ghi đè lên, nó là cần thiết mà bạn gọi supernếu không bạn sẽ cóandroid.app.SuperNotCalledException
Sunil Mishra

1
Bạn có vẻ hiểu lầm. Câu hỏi không phải là có nên gọi hay không. Giống như bạn đã chỉ ra, bạn PHẢI, nếu bạn ghi đè chúng. Câu hỏi là, khi nào?
Anudeep Bulla

1

Bạn nói rằng Google đề xuất phương pháp 1, tuy nhiên Dianne Hackborn, một kỹ sư khung Android nổi tiếng đề xuất cách khác, hãy xem Liên kết diễn đàn của Google .

Nó làm cho cảm giác trực quan để gọi lớp siêu cuối cùng khi phá hủy một thể hiện trong onPause, onStoponDestroy phương pháp và đầu tiên khi tạo một thể hiện với các phương pháp onCreate, onResumeonStart .


Liên kết đến bài đăng của Dianne Hackborn rất quan trọng và xác nhận mô hình đó.
Nick Westgate

0

Siêu lệnh gọi lại là cần thiết để đặt Hoạt động ở trạng thái phù hợp bên trong hệ thống.

Giả sử bạn bắt đầu Hoạt động của mình và onCreate được gọi bởi hệ thống. Bây giờ bạn có thể ghi đè nó và ví dụ như tải bố cục của bạn. Nhưng vì lợi ích của luồng hệ thống, bạn phải gọi là siêu, hệ thống có thể tiếp tục với quy trình chuẩn. Đó là lý do tại sao một ngoại lệ sẽ được ném ra nếu bạn không gọi nó.

Điều này xảy ra độc lập với việc triển khai của bạn trong onCreate. Nó chỉ đóng vai trò quan trọng cho hệ thống. Nếu không có ANR, bạn có thể có một vòng lặp vô tận trong bất kỳ cuộc gọi lại nào và Hoạt động sẽ bị bắt vào vòng lặp đó. Vì vậy, hệ thống biết khi nào cuộc gọi lại đã được kết thúc và hơn là các cuộc gọi tiếp theo.

Tôi chỉ biết một tình huống, thời điểm của cuộc gọi siêu cấp là cần thiết. Nếu bạn muốn thay đổi hành vi tiêu chuẩn của chủ đề hoặc màn hình và tương tự như vậy trong onCreate, bạn phải làm điều đó trước khi gọi super để xem hiệu ứng. Nếu không thì AFAIK không có sự khác biệt tại thời điểm bạn gọi nó.

Nhưng để cho hệ thống làm những gì tốt nhất có thể, hãy đặt siêu phẩm vào dòng đầu tiên của lệnh gọi lại, sau đó là mã của bạn, nếu bạn không có lý do chính đáng để phá vỡ nó.


Trình tự trong onCreate có vẻ khá dễ hiểu. Điều gì xảy ra trong các phương pháp phá hủy? Nói onStop. Giả sử việc triển khai onStop của tôi sử dụng một số tài nguyên mà phương thức siêu giải phóng nếu được gọi. Khi đó, sẽ có lý khi gọi phương thức siêu sau khi thực hiện.
Anudeep Bulla

Tốt nhất, đây là trường hợp, đúng. Hoạt động của chúng ta sẽ luôn có các tài nguyên mà siêu lớp có và một số tài nguyên khác. Và những tài nguyên độc lập với hoạt động của tôi, trong hầu hết các trường hợp, có thể phụ thuộc vào các tài nguyên của lớp siêu, đó là điều phổ biến. Sẽ có ý nghĩa hơn khi xử lý các tài nguyên của tôi trước, và sau đó gọi lớp cha để xử lý các tài nguyên chung. Tại sao Google sau đó nói rằng chúng tôi "NÊN" gọi các phương thức siêu lớp trước?
Anudeep Bulla

Bạn đang nói về tài nguyên nào, mà bạn có thể truy cập trong onCreate nhưng không có trong onDestroy?
Steve Benett

Tôi không có trường hợp sử dụng. Tôi chỉ tự hỏi, nó thay đổi như thế nào với phong cách OOP. Các hàm tạo siêu lớp được gọi đầu tiên, trước khi bạn thực hiện và các hàm hủy siêu lớp được gọi sau cùng, sau khi bạn thực hiện đúng không? onPause, onStop và onDestroy không phải là những trình hủy hoàn toàn, nhưng chúng có xu hướng làm những việc tương tự, phải không? Ít nhất onDestroy và chủ yếu là onStop nữa .. không?
Anudeep Bulla

Xem các lệnh gọi lại ở trạng thái thay đổi theo cách không phải là hàm tạo / hủy. Mọi cuộc gọi lại đều có nhu cầu, ví dụ như tạo (sẵn sàng sử dụng) / hủy (cơ hội cuối cùng của bạn để tương tác) một Hoạt động hoặc đặt nó ở nền trước / nền. Các lệnh gọi lại ở đó mà bạn có thể kiểm soát tài nguyên của mình trong luồng của hệ thống. Hệ thống chỉ việc kiểm tra xem nó đang ở trạng thái nào và xử lý phù hợp. Tài nguyên bạn sử dụng và tài nguyên mà hệ thống kiểm soát độc lập với nhau và sẽ không có sự giao nhau.
Steve Benett
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.