2012-04-23 48 views
1

我有一個activity的onCreate方法調用Init函數(該函數調用一些涉及很多東西的本地代碼並調用openSLES音頻api)。問題的關鍵是,這個Init函數在再次調用時會導致應用程序崩潰,這種情況發生在屏幕旋轉上,或者當我使用Back按鈕關閉該活動並重新啓動它時(但是如果在此期間該進程被終止,我沒有麻煩)。我無法改變Init函數的承載。如何檢查onCreate以前的android進程是否一樣被殺死?

我發現當活動被破壞時我沒有殺死這個進程,我在閱讀文檔後期待這樣做,這是一件好事,因爲 - 如果有一些音頻信號正在播放 - 在活動結束後繼續播放破壞了,這對我的目的有好處。

我試圖執行使用了的onSaveInstanceState初始化狀態的檢查,但效果很好只能在屏幕旋轉,當是的onSaveInstanceState叫那。當我按下後退按鈕時,不會調用回調。

,所以我嘗試使用共享偏好,執行狀態的onPause節約。但是在這一點上,我有相反的問題:如果進程被終止,共享首選項值將保留,但在這種情況下,我需要再次執行Init以使應用程序正常工作。

我想我需要一種方法來知道,如果一個進程終止或不後創建我的活動,但此刻我看不出。我想在onPause方法中使用bundle實例,但我無法確定這是否可行。任何類型的提示都會非常感激。

+0

你不能在onPause/init onResume中釋放那個東西嗎?應該只有一個實例處於這種狀態。或者把整個東西變成一個服務,因爲這些東西總是單個實例 – zapl 2012-04-23 16:53:32

+0

我不能釋放「那個東西」(這是一個很好的方式來命名:)),既不在onPause也不在其他任何地方,我只能依靠它在進程終止後安全地調用。服務選項聽起來像是對我的需求矯枉過正,但我​​開始考慮它。 – athos 2012-04-23 17:04:10

+0

一個簡單的本地(意圖)服務只是一些代碼行,你可以在一個活動中啓動它,在下一個活動中停止它,因爲它的生命週期不與活動相關聯。它們非常適合背景音樂等不會中斷的事物。嘗試解決Activity生命週期當然是可能的,但我不認爲這是一個乾淨的解決方案。 – zapl 2012-04-23 17:09:31

回答

0

有一個簡單的解決這個問題。您無需在SharedPreferences中保存任何內容即可完成此操作。只需使用一個靜態(類)變量。像這樣:

public class Globals { 
    public static boolean initialized = false; 
} 

當類加載時變量initialized將被設置爲false。只有一次。在您的代碼,您再檢查並設定這樣的變量:

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    // Initialize (but only once per process) 
    if (!Globals.initialized) { 
     init(); // Call init function that does things one time per process 
     Globals.initialized = true; // Remember we are initialized so we don't 
            // do it again 
    } 
    ... 
} 

即使所有的活動完成後,如果操作系統不殺你的過程變量initialized仍將是「真」,如果應用程序再次啓動。一旦操作系統殺死進程,下次啓動應用程序並創建新進程時,該變量將設置爲「false」。

+0

如果您在另一個Activity中,此應用程序的進程被終止並稍後重新啓動,這將不起作用。 – 2013-04-30 11:33:39

+0

@MaciejGórski你是什麼意思「這不行」? OP詢問了一個具體的案例。他需要確保他只在進程中調用一次'init()'方法。我的答案並不意味着這是一個普通用途「這是如何恢復一切,以防Android殺死你的進程」 – 2013-04-30 11:58:27

+0

爲了確保本地lib在使用之前被初始化,它不能被初始化在(第一次)活動正常運行當用戶點擊應用程序圖標時。當進程在任何其他活動(思想Spash,菜單,遊戲)上被終止並重新啓動時,您將無法使用音頻API,因此將不會有聲音或應用程序崩潰。 – 2013-04-30 12:07:57

0

對於每個進程你有PID或進程ID。在您的init函數中,您可以輕鬆獲取線程ID並可以將其保存爲任何整數值。 。

Thread.currentThread()的getId()));

,只要您的活動將重新啓動只是檢查線程ID是相同或不同。如果線程ID不同,那麼調用你的函數init函數。否則,你已經完成了。

+0

嘗試使用進程ID。請參閱@ darkmist的回答。 – athos 2012-04-23 18:00:55

+0

你不必專注於不同的過程可以有相同的PID。有一次,一個線程ID將是唯一的。所以如果你的線程有id 1,那麼當時其他子線程不能有相同的id。如果你使用Thread.currentThread()。getId()));在你的oncreate,那麼你將有UIthread ID,但你需要該線程的init函數正在運行的ID,所以要麼根據你的條件使用上面的語句到你的init函數的init函數中。將它保存在變量中並使用它。它應該工作。 – 2012-04-23 18:57:16

1
  1. 您可以將您的pid在共享偏好的過程。如果您在YourActivity.on中比較當前的pid和存儲的pid,則可以確定何時必須初始化OpenSLES。
  2. 您可以在ApplicationApplication.onCreate中初始化應用程序派生類中的OpenSLES - 僅調用一次。

編輯:

即聲明以下類:

public class YourApplication extends Application { 
    static private native synchronized void InitOpenSLES(); 

    public YourApplication() {} 

    // see http://developer.android.com/reference/android/app/Application.html#onCreate() for details 
    @Override 
    public void onCreate() { 
    super.onCreate(); 
    InitOpenSLES(); 
    } 
} 
+0

剛剛嘗試了1.它似乎工作,但我會確定不同過程的PID將永遠不同嗎?我的意思是,在舊的死亡之後創建的新進程不可能獲得相同的pid? 我不明白2,你能詳細說明嗎? – athos 2012-04-23 18:00:35

+0

1.你說得對,理論上操作系統可能會殺死你的進程,而不是你啓動大量的進程來包裝pid數,然後用相同的pid創建一個新的進程 - 在這種情況下,你不會執行初始化過程需要。但是這種情況的概率非常低=)關於用於爲新創建的進程分配pid的算法,請參見例如這裏:http://stackoverflow.com/questions/3446727/how-does-linux-determine-the-next- PID。 2.查看答案的更新。 – 2012-04-23 20:04:25

+0

接受了第2點;) – athos 2012-05-02 08:13:53