2012-07-15 63 views
63

我一直在試圖實現一個需要在表面上預覽相機的應用程序。 當我看到的東西,活性和表面的生命週期包括以下狀態:SurfaceHolder回調如何與Activity生命週期相關?

  1. 當我第一次啓動我的活動:onResume()->onSurfaceCreated()->onSurfaceChanged()
  2. 當我離開我的活動:onPause()->onSurfaceDestroyed()

在這方案,我可以在onPause/onResumeonSurfaceCreated()/onSurfaceDestroyed()中做相應的調用,如打開/釋放相機和開始/停止預覽。

它工作正常,除非我鎖定屏幕。當我啓動應用程序,然後鎖定屏幕,然後解鎖它,我看到:

onPause() - 屏幕鎖定後沒有別的 - 然後onResume()解鎖後 - 並沒有表面回調後。實際上,onResume()是在按下電源按鈕並且屏幕打開後調用的,但鎖定屏幕仍處於活動狀態,所以在活動變得可見之前。

有了這個方案,解鎖後我得到了黑屏,並且沒有調用表面回調。

下面是一個代碼片段,不涉及實際使用相機,但SurfaceHolder回調。這個問題上面有我的手機上這個代碼甚至複製(回調被稱爲在正常程序,當你按下「返回」按鈕,但是當你鎖定屏幕失蹤):爲什麼在

class Preview extends SurfaceView implements SurfaceHolder.Callback { 

    private static final String tag= "Preview"; 

    public Preview(Context context) { 
     super(context); 
     Log.d(tag, "Preview()"); 
     SurfaceHolder holder = getHolder(); 
     holder.addCallback(this); 
     holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
    } 

    public void surfaceCreated(SurfaceHolder holder) { 
     Log.d(tag, "surfaceCreated"); 
    } 

    public void surfaceDestroyed(SurfaceHolder holder) { 
     Log.d(tag, "surfaceDestroyed"); 
    } 

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
     Log.d(tag, "surfaceChanged"); 
    } 
} 

任何想法活動暫停後表面仍然未被破壞?另外,在這種情況下你如何處理相機生命週期?

+0

您正在開發哪種android Plaform/API級別? – FerranB 2012-08-26 16:58:24

回答

51

編輯:如果targetSDK較大比10,把應用程序睡覺呼籲 onStopSource

我在薑餅手機上的一個小照相機應用程序中查看了Activity和SurfaceView的生命週期。你完全正確;當按下電源按鈕使手機進入睡眠狀態時,表面不會被破壞。當手機進入睡眠狀態時,活動確實爲。 (並沒有做onStop。)當手機醒來時它確實有onResume,正如你指出的那樣,當鎖屏仍然可見並接受輸入時,它會這樣做,這有點奇怪。當我通過按主頁按鈕使活動不可見時,活動將同時執行和onStop。在這種情況下,在的結尾和onStop的開始之間的某個事件導致回撥到surfaceDestroyed。這不是很明顯,但看起來非常一致。

當按下電源按鈕讓手機進入睡眠狀態時,除非明確要停止手機,否則相機會繼續運行!如果我讓攝像機爲每個預覽幀執行每個圖像的回調,並在其中存在一個Log.d(),則當手機假裝睡眠時,日誌語句不斷出現。我認爲這是非常偷偷摸摸

作爲另一混亂,回調surfaceCreatedsurfaceChanged發生onResume在活動之後,如果正在產生的表面。

作爲一項規則,我管理實現SurfaceHolder回調的類中的相機。

class Preview extends SurfaceView implements SurfaceHolder.Callback { 
    private boolean previewIsRunning; 
    private Camera camera; 

    public void surfaceCreated(SurfaceHolder holder) { 
     camera = Camera.open(); 
     // ... 
     // but do not start the preview here! 
    } 

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
     // set preview size etc here ... then 
     myStartPreview(); 
    } 

    public void surfaceDestroyed(SurfaceHolder holder) { 
     myStopPreview(); 
     camera.release(); 
     camera = null; 
    } 

    // safe call to start the preview 
    // if this is called in onResume, the surface might not have been created yet 
    // so check that the camera has been set up too. 
    public void myStartPreview() { 
     if (!previewIsRunning && (camera != null)) { 
      camera.startPreview(); 
      previewIsRunning = true; 
     } 
    } 

    // same for stopping the preview 
    public void myStopPreview() { 
     if (previewIsRunning && (camera != null)) { 
      camera.stopPreview(); 
      previewIsRunning = false; 
     } 
    } 
} 

,然後在活動:

@Override public void onResume() { 
    preview.myStartPreview(); // restart preview after awake from phone sleeping 
    super.onResume(); 
} 
@Override public void onPause() { 
    preview.myStopPreview(); // stop preview in case phone is going to sleep 
    super.onPause(); 
} 

這似乎爲我工作確定。旋轉事件會導致Activity被銷燬並重新創建,導致SurfaceView被破壞並重新創建。

+0

爲什麼在超級通話之前? – 2015-05-11 23:18:37

+0

我不知道代碼是在超級調用之前還是之後執行都很重要。在'onResume'例程中調用'super.onResume'是非常重要的。我認爲。 – emrys57 2015-05-12 07:02:40

+0

實際上它取決於surfaceview和surfaceviewholder的初始化。如果你做一些同步任務,那麼你初始化視圖,然後它永遠不會在onResume之後調用。即使不是在同步任務後。但是當我移動到另一個活動並恢復時,它回調onSurfaceCreated或更改功能。順便謝謝你! @ emrys57。至少我已經整理出我的問題是與surfaceview。 :) – 2015-05-27 08:00:24

1

SurfaceHolder.Callback與其Surface相關。

是在屏幕上的活動?如果是這樣,將不會有SurfaceHolder.Callback,因爲Surface仍在屏幕上。

要控制任何SurfaceView,只能在onPause/onResume中處理它。對於SurfaceHolder.Callback,你可以使用它,如果表面被改變(創建,SizeChanged將和破壞),像openGL的初始化時surfaceCreated,並摧毀openGL的時候surfaceDestroyed等

18

工作正常的另一種簡單解決方案 - 更改預覽表面的可見性。

private SurfaceView preview; 

預覽是在onCreate方法init。在onResume方法設置View.VISIBLE預覽面:

@Override 
public void onResume() { 
    preview.setVisibility(View.VISIBLE); 
    super.onResume(); 
} 

,並分別在集可視性View.GONE

@Override 
public void onPause() { 
    super.onPause(); 
    preview.setVisibility(View.GONE); 
    stopPreviewAndFreeCamera(); //stop and release camera 
} 
+0

你是一個救星! – gtsouk 2017-02-03 23:24:05

+0

確認此功能也適用於Camera2 API。 – 2017-05-16 15:57:35

1

由於雙方以前所有的答案,我設法讓我的相機預覽的工作說白了,當去從後面無論是背景還是鎖屏。

正如@ e7fendy所說,SurfaceView的回調函數在屏幕鎖定時不會被調用,因爲表面視圖對於系統仍然可見。

因此,如@validcat建議,分別在onPause()和onResume()中調用preview.setVisibility(View.VISIBLE);preview.setVisibility(View.GONE);將強制表面視圖自行重新佈局,並將其稱爲回調。

屆時,從溶液中@ emrys57加上這兩個知名度的方法調用上面會讓你的相機預覽的工作說白了:)

所以我只能給+1對你們每一個人,你所有應得的;)

-2

這裏是所有回調方法的替代解決方案,它們可能都受到與活動週期相同的未定義事件順序行爲的影響。除非您要檢查每次回調的所有android代碼,您可以使用它們來確定原始觸發器和控制實現的人員,並希望將來代碼庫不會更改,但是真的可以說明,回調函數之間的事件順序並且活動生命週期事件可以得到保證。

現在,爲了開發目的,訂單的這些交互通常可以稱爲未定義的行爲。

所以最好是始終正確處理這個未定義的行爲,這樣 它將永遠不會成爲一個問題,通過確保訂單是定義的行爲。

我的索尼XPERIA例如,睡眠,週期我目前的應用程序,通過破壞應用程序,然後重新啓動它,並把它進入暫停狀態,信不信由你。

測試谷歌在SDK中提供多少事件排序行爲作爲特殊的測試構建主機環境實現我不知道,但他們肯定需要努力確保,事件訂單的行爲都被鎖定在這件事上相當嚴格。

https://code.google.com/p/android/issues/detail?id=214171&sort=-opened&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened

進口android.util.Log; import android.util.SparseArray;

/** * 通過對2016年6月24日woliver創建。 * * Android的主機環境,決定了在OnCreate,在onStart,的onResume,在onPause,的onStop,onDestory, 一個Activity生命週期*,其中由我們需要釋放內存並處理其他應用程序使用。 *當恢復時,我們有時需要重新綁定和激活這些項目與其他對象。 *典型地,這些其它的目的提供從主機環境,其提供 *一個onCreated和的onDestroy,我們可以在其中僅結合從OnCreated該對象的回調方法和和鬆散 *出綁定onDestory。 *這些類型的回調方法,shedual時間運行是控制器通過我們的主機環境 *和他們的無人保證了活動的生命週期的執行行爲/順序和這些回調方法 *保持一致。 *爲發展宗旨執行的相互作用和順序可以在技術上被稱爲未定義 *,因爲它是由主機執行實施者,三星,索尼,HTC。 * *請參閱以下開發人員文檔:https://developer.android.com/reference/android/app/Activity.html * Quote: *如果一項活動被其他活動完全遮擋,則會停止。它仍然保留所有的狀態 *和成員信息,但是,它不再對用戶可見的所以它的窗口是 *隱藏它通常會被系統的其他地方時,需要記憶被殺害。 * EndQuato: * *如果活動沒有隱藏,那麼系統將會調用主機 *系統調用的任何回調,例如OnCreate和OnDestory方法接口SurfaceView回調。 *這意味着,你將不得不停止已綁定到SurfaceView對象,如相機 *在暫停和永遠不會重新綁定對象爲在OnCreate回調將不會被調用。 * */

public abstract class WaitAllActiveExecuter<Size> 
{ 
    private SparseArray<Boolean> mReferancesState = null; 

// Use a dictionary and not just a counter, as hosted code 
// environment implementer may make a mistake and then may double executes things. 
private int mAllActiveCount = 0; 
private String mContextStr; 

public WaitAllActiveExecuter(String contextStr, int... identifiers) 
{ 
    mReferancesState = new SparseArray<Boolean>(identifiers.length); 

    mContextStr = contextStr; 

    for (int i = 0; i < identifiers.length; i++) 
     mReferancesState.put(identifiers[i], false); 
} 

public void ActiveState(int identifier) 
{ 
    Boolean state = mReferancesState.get(identifier); 

    if (state == null) 
    { 
     // Typically panic here referance was not registered here. 
     throw new IllegalStateException(mContextStr + "ActiveState: Identifier not found '" + identifier + "'"); 
    } 
    else if(state == false){ 

     mReferancesState.put(identifier, true); 
     mAllActiveCount++; 

     if (mAllActiveCount == mReferancesState.size()) 
      RunActive(); 
    } 
    else 
    { 
     Log.e(mContextStr, "ActivateState: called to many times for identifier '" + identifier + "'"); 
     // Typically panic here and output a log message. 
    } 
} 

public void DeactiveState(int identifier) 
{ 
    Boolean state = mReferancesState.get(identifier); 

    if (state == null) 
    { 
     // Typically panic here referance was not registered here. 
     throw new IllegalStateException(mContextStr + "DeActiveState: Identifier not found '" + identifier + "'"); 
    } 
    else if(state == true){ 

     if (mAllActiveCount == mReferancesState.size()) 
      RunDeActive(); 

     mReferancesState.put(identifier, false); 
     mAllActiveCount--; 
    } 
    else 
    { 
     Log.e(mContextStr,"DeActiveState: State called to many times for identifier'" + identifier + "'"); 
     // Typically panic here and output a log message. 
    } 
} 

private void RunActive() 
{ 
    Log.v(mContextStr, "Executing Activate"); 

    ExecuterActive(); 
} 

private void RunDeActive() 
{ 
    Log.v(mContextStr, "Executing DeActivate"); 

    ExecuterDeActive(); 
} 


abstract public void ExecuterActive(); 

abstract public void ExecuterDeActive(); 
} 

實施和使用類的,其與機器人或主機環境 實施者的未定義行爲涉及的實施例。

private final int mBCTSV_SurfaceViewIdentifier = 1; 
private final int mBCTSV_CameraIdentifier = 2; 

private WaitAllActiveExecuter mBindCameraToSurfaceView = 
     new WaitAllActiveExecuter("BindCameraToSurfaceViewe", new int[]{mBCTSV_SurfaceViewIdentifier, mBCTSV_CameraIdentifier}) 
{ 
    @Override 
    public void ExecuterActive() { 

     // Open a handle to the camera, if not open yet and the SurfaceView is already intialized. 
     if (mCamera == null) 
     { 
      mCamera = Camera.open(mCameraIDUsed); 

      if (mCamera == null) 
       throw new RuntimeException("Camera could not open"); 

      // Look at reducing the calls in the following two methods, some this is unessary. 
      setDefaultCameraParameters(mCamera); 
      setPreviewSizesForCameraFromSurfaceHolder(getSurfaceHolderForCameraPreview()); 
     } 

     // Bind the Camera to the SurfaceView. 
     try { 
      mCamera.startPreview(); 
      mCamera.setPreviewDisplay(getSurfaceHolderForCameraPreview()); 
     } catch (IOException e) { 

      e.printStackTrace(); 
      ExecuterDeActive(); 

      throw new RuntimeException("Camera preview could not be set"); 
     } 
    } 

    @Override 
    public void ExecuterDeActive() { 

     if (mCamera != null) 
     { 
      mCamera.stopPreview(); 

      mCamera.release(); 
      mCamera = null; 
     } 
    } 
}; 

@Override 
protected void onPause() { 


    mBindCameraToSurfaceView.DeactiveState(mBCTSV_CameraIdentifier); 

    Log.v(LOG_TAG, "Activity Paused - After Super"); 
} 

@Override 
public void onResume() { 

    mBindCameraToSurfaceView.ActiveState(mBCTSV_CameraIdentifier); 
} 

private class SurfaceHolderCallback implements SurfaceHolder.Callback 
{ 
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
    Log.v(LOG_TAG, "Surface Changed"); 

    } 

    public void surfaceCreated(SurfaceHolder surfaceHolder) { 

     Log.v(LOG_TAG, "Surface Created"); 
     mBindCameraToSurfaceView.ActiveState(mBCTSV_SurfaceViewIdentifier); 
    } 

    public void surfaceDestroyed(SurfaceHolder arg0) { 

     Log.v(LOG_TAG, "Surface Destoryed"); 
     mBindCameraToSurfaceView.DeactiveState(mBCTSV_SurfaceViewIdentifier); 
    } 
} 
相關問題