1. 程式人生 > >Activity onDestroy方法未能及時執行原因

Activity onDestroy方法未能及時執行原因

網上出現的情況也不少,大致看了下主要有以下幾類:

  • AndroidManifest 針對某個activity 設定的theme為 @android:style/Theme.NoTitleBar

在目前專案中(android2.0),發現一個嚴重BUG。當打開了A,B兩個頁面的時候,此時當前頁面為B,如果在B中呼叫finish()後,雖然已經回到了A,但B卻並沒有執行onDestroy()方法,只有在手機上按動了某一個按鍵後,這時才會執行B.onDestroy()。B才會完全銷燬。另:如果按手機自帶的返回按鈕,卻不會出現這種情況。


因此,如果此時在onDestroy()方法有一些操作的話,那麼在回到A不進行按鍵操作的話,B並沒有及時銷燬,所以不會做方法中的這些操作。
 

經定位修改後發現,此BUG是由於在AndroidManifest.xml中對A頁面設定了android:theme="@android:style/Theme.NoTitleBar"。而B頁面並沒有設定此引數造成的。把B增加此設定後,問題解決。

雖然問題已解決,但一直沒有明白未設定android:theme="@android:style/Theme.NoTitleBar"的頁面,為什麼會在finish()後不能馬上呼叫onDestroy()。按理說兩者不應該有什麼關聯才是。

我寫了一個Activity,反覆進去和退出,這樣重複20次,TV的記憶體居然從53M升到了驚人的 170M,

  • Activity 的 onDestroy() 是系統回撥函式, 呼叫時機是不確定的

據張明雲(程式設計,Andoid開發話題的優秀回答者)所說,在finish()方法之後沒有立即執行onDestroy()方法,只是上述這個命題的一種情況,不僅如此,Activity的其他生民週期方法何時會呼叫也是不確定的,onDestroy沒有及時執行暫未找到有效的處理方法,但可以通過isFinishing()方法判斷 Activity 是否處於銷燬狀態。

  • 按下home,再在recent中強行刪除

這種情況據網上回答,位於棧頂的Activity是會執行onDestroy(),棧內其他Activity不會執行。據我測試,一個都沒執行。因為又有回答說 Activity被手機記憶體強制回收是不會呼叫destory方法的。

  • onDestroy() 和 finish()

finish()方法用於結束一個Activity的生命週期,而onDestory()方法則是Activity的一個生命週期方法,其作用是在一個Activity物件被銷燬之前,Android系統會呼叫該方法,用於釋放此Activity之前所佔用的資源。

finish會呼叫到onDestroy方法,
可以在onDestroy裡列印一句話,就會發現在finish方法那也會列印這句話。。。

Activity.finish()
Call this when your activity is done and should be closed.
在你的activity動作完成的時候,或者Activity需要關閉的時候,呼叫此方法。
當你呼叫此方法的時候,系統只是將最上面的Activity移出了棧,並沒有及時的呼叫onDestory()方法,其佔用的資源也沒有被及時釋放。因為移出了棧,所以當你點選手機上面的“back”按鍵的時候,也不會再找到這個Activity。
Activity.onDestory()
the system is temporarily destroying this instance of the activity to save space.
系統銷燬了這個Activity的例項在記憶體中佔據的空間。
在Activity的生命週期中,onDestory()方法是他生命的最後一步,資源空間等就被回收了。當重新進入此Activity的時候,必須重新建立,執行onCreate()方法。

  • 我遇到的情況

在發現問題原因之前進行了多方面排查,主要有

  • activity呼叫finish卻不立即執行onDestroy
  • SignFragment中實現定位?
  • intent.setClass()?
  • xml中 tools:context?
  • Androidmanifest中具體的activity的屬性 configuration?
  • 專案activity達到峰值了?
  • v4 Fragment 還是 Fragment?

經過2/3的工作日時間找到了問題出處,雖然找到了問題,可是處理起來還是比較費勁。

我的情況是在Fragment中使用了下面程式碼,然後跳轉到Activity,退出Activity時,Activity 的 onDestroy()方法延遲了幾秒執行。

animation = AnimationUtils.loadAnimation(mContext, R.anim.anim_sign_in);

 @Override
 public void onResume() {
    super.onResume();
    EventBus.getDefault().register(this);
    mAnim.startAnimation(animation);
 }

 @Override
 public void onPause() {
    super.onPause();
    EventBus.getDefault().unregister(this);
    mAnim.setAnimation(null);
  }

 移除這句話的時候,Activity 的onDestroy()方法會立即執行。

mAnim.startAnimation(animation);

首先將startAnimation()改為 setAnimation(),在onPause()中在置null,發現還是一樣。其次研究了一下AnimationUtils的基本使用情況,設定以下程式碼,效果還是一樣

    @Override
    public void onResume() {
        super.onResume();
        EventBus.getDefault().register(this);
        mAnim.setAnimation(animation);
        animation.startNow();
    }

    @Override
    public void onPause() {
        super.onPause();
        EventBus.getDefault().unregister(this);
        animation.cancel();
        mAnim.setAnimation(null);
    }

將Animation 改用Animator 試試看,以我的理解是 Animation應該要被棄用了,它能實現的,Animator都能實現,並且更遵循面向物件的原則。所以改為以下方式後在執行,發現 Activity的onDestroy() 方法會隨著Activity 介面消失立即執行。

    private void test() {
        ObjectAnimator anim1 = ObjectAnimator.ofFloat(mAnim,"scaleX",1.2f,0.8f);
        anim1.setRepeatCount(-1);
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(mAnim,"scaleY",1.2f,0.8f);
        anim2.setRepeatCount(-1);
        AnimatorSet set = new AnimatorSet();
        set.play(anim1).with(anim2);
        set.setDuration(1000);
        set.start();
    }

    private void startAnimator() {
        Animator anim = AnimatorInflater.loadAnimator(mContext,R.animator.anim_signnal);
        anim.setTarget(mAnim);
        anim.start();
    }

問題總算是解決了,不過伴隨著的知識還是有很多地方需要去了解和加深的。