SurfaceHolder 콜백은 활동 수명주기와 어떤 관련이 있나요?
표면에서 카메라 미리보기가 필요한 응용 프로그램을 구현하려고했습니다. 내가 본 것처럼 활동 및 표면 수명주기는 모두 다음 상태로 구성됩니다.
- 내 활동을 처음 시작할 때 :
onResume()->onSurfaceCreated()->onSurfaceChanged()
- 내 활동을 떠날 때 :
onPause()->onSurfaceDestroyed()
이 방식에서는 카메라 열기 / 해제, onPause/onResume
및 에서 미리보기 시작 / 중지와 같은 해당 호출을 수행 할 수 있습니다 onSurfaceCreated()/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");
}
}
활동이 일시 중지 된 후에도 표면이 파괴되지 않은 이유에 대한 아이디어가 있습니까? 또한 이러한 경우 카메라 수명주기를 어떻게 처리합니까?
편집 : targetSDK가 10보다 크면 앱을 절전 모드로 전환 onPause
하고 onStop
. 출처
진저 브레드 폰의 작은 카메라 앱에서 Activity와 SurfaceView의 수명주기를 살펴 보았습니다. 당신은 전적으로 정확합니다. 전원 버튼을 눌러 휴대폰을 절전 모드로 전환해도 표면이 파괴되지 않습니다. 전화기가 잠자기 상태가되면 활동은 onPause
. (그리고하지 않습니다 onStop
.) onResume
전화기가 깨어날 때 작동 하며, 지적했듯이 잠금 화면이 여전히 표시되고 입력을 수락하는 동안이 작업을 수행합니다. 약간 이상합니다. 홈 버튼을 눌러 활동을 보이지 않게하면 활동이 onPause
및 onStop
. surfaceDestroyed
이 경우의 끝 onPause
과 시작 사이에 콜백이 발생합니다 onStop
. 그다지 분명하지는 않지만 매우 일관된 것 같습니다.
전원 버튼을 눌러 전화기를 절전 모드로 전환하면 명시 적으로 중지하지 않는 한 카메라가 계속 실행됩니다! 카메라가 각 미리보기 프레임에 대해 이미지 당 콜백을 수행하고 거기에 Log.d ()가 있으면 전화가 잠자는 척하는 동안 로그 문이 계속 나옵니다. 나는 그것이 Very Sneaky 라고 생각합니다 .
또 다른 혼란으로,에 콜백 surfaceCreated
및 surfaceChanged
발생 후 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도 파괴되고 다시 생성됩니다.
잘 작동하는 또 다른 간단한 솔루션은 미리보기 화면의 가시성을 변경하는 것입니다.
private SurfaceView preview;
미리보기는 init in onCreate
method입니다. 에서 onResume
방법 세트 View.VISIBLE
미리보기 표면 :
@Override
public void onResume() {
preview.setVisibility(View.VISIBLE);
super.onResume();
}
그리고 각각 onPause
세트 가시성 View.GONE
:
@Override
public void onPause() {
super.onPause();
preview.setVisibility(View.GONE);
stopPreviewAndFreeCamera(); //stop and release camera
}
이전의 모든 답변 덕분에 배경 또는 잠금 화면으로 돌아 가면서 카메라 미리보기가 명확하게 작동하도록 관리했습니다.
@ e7fendy가 언급했듯이 SurfaceView의 콜백은 시스템에서 표면보기가 계속 표시되므로 화면 잠금 상태에서 호출되지 않습니다.
@validcat가 의뢰 같이 따라서, 호출 preview.setVisibility(View.VISIBLE);
및 preview.setVisibility(View.GONE);
자체 레이아웃 다시 사면 뷰 강제 각각 onPause ()와 onResume ()을하고 콜백을 호출한다.
그때까지 @ emrys57의 솔루션과 위의 두 가지 가시성 메서드 호출을 통해 카메라 미리보기가 명확하게 작동합니다. :)
그래서 나는 당신 모두가 그것을받을 자격이 있기 때문에 당신 각자에게 +1을 줄 수 있습니다.)
SurfaceHolder.Callback은 Surface와 관련이 있습니다.
화면에 활동이 있습니까? 그렇다면 Surface가 여전히 화면에 있기 때문에 SurfaceHolder.Callback이 없습니다.
SurfaceView를 제어하려면 onPause / onResume에서만 처리 할 수 있습니다. SurfaceHolder.Callback의 경우, SurfaceCreated시 openGL 초기화, surfaceDestroyed시 openGL 삭제 등과 같이 Surface가 변경 (생성, 크기 변경 및 소멸) 된 경우 사용할 수 있습니다.
다음은 모든 콜백 메서드에 대한 대체 솔루션으로, 모두 활동주기와 함께 정의되지 않은 동일한 이벤트 순서 동작의 영향을받을 수 있습니다. 원점 트리거를 결정하고 구현을 제어하는 사람을 결정하는 데 사용하는 각 콜백에 대한 모든 Android 코드를 검사하지 않는 한, 나중에 코드 기반이 변경되지 않기를 바라지 않는 한, 콜백 사이의 이벤트 순서를 말할 수 있습니다. 활동 라이프 사이클 이벤트가 보장 될 수 있습니다.
현재 이러한 순서의 상호 작용은 일반적으로 개발 목적으로 정의되지 않은 동작이라고 할 수 있습니다.
따라서 가장 좋은 방법은이 정의되지 않은 동작을 항상 올바르게 처리하여 처음부터 문제가되지 않도록 주문이 정의 된 동작인지 확인하는 것입니다.
예를 들어 내 Sony Xperia는 절전 모드에서 앱을 삭제 한 다음 다시 시작하여 현재 앱을 순환하고 믿거 나 말거나 일시 중지 상태로 전환합니다.
Google이 호스트 환경을위한 특수 테스트 빌드로 SDK에서 제공하는 이벤트 순서 지정 동작 테스트의 양은 모르겠지만 확실히 노력해야합니다. 이벤트 순서 동작은 모두 엄격하게 잠겨 있습니다. 문제.
import android.util.Log; import android.util.SparseArray;
/ ** * 2016/06/24에 woliver가 작성했습니다. * * Android 호스트 환경은 OnCreate, onStart, onResume, onPause, onStop, onDestory에 대한 활동 수명주기를 지시합니다. * 다른 애플리케이션에서 사용할 메모리 및 핸들을 해제해야하는 위치. * 재개 할 때이 항목을 다른 개체와 함께 리 바인딩하고 활성화해야하는 경우가 있습니다. * 일반적으로 이러한 다른 객체는 onCreated 및 onDestroy를 제공하는 호스트 환경에서 콜백 메서드를 제공합니다.이 메서드는 * OnCreated에서이 객체에 바인딩하고 * 바인딩 onDestory를 해제 할 수 있습니다. * 이러한 유형의 콜백 메소드, 실행 시간은 호스트 환경에 의해 제어되며 * 활동 라이프 사이클 및 이러한 콜백 메소드의 동작 / 실행 순서가 일관되게 유지된다는 보장은 없습니다. * 개발 목적을 위해 상호 작용 및 실행 순서는 호스트 구현 구현자인 samsung, sony, htc에 따라 기술적으로 정의되지 않음 *이라고 할 수 있습니다. * * 다음 개발자 문서 참조 :https://developer.android.com/reference/android/app/Activity.html * 인용구 : * 활동이 다른 활동에 의해 완전히 가려지면 중지됩니다. 여전히 모든 상태 * 및 멤버 정보를 유지하지만 더 이상 사용자에게 표시되지 않으므로 해당 창은 * 숨겨지며 메모리가 다른 곳에서 필요할 때 시스템에 의해 종종 종료됩니다. * 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();
}
Example of Implementation and use of class, which deals with or the undefined behaviour of android host enviroment implementers.
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);
}
}
'Development Tip' 카테고리의 다른 글
Does anything like number exist? (0) | 2020.11.05 |
---|---|
Java의 자연 정렬 순서 문자열 비교-내장되어 있습니까? (0) | 2020.11.05 |
연결 문자열에서 초기 카탈로그와 데이터베이스 키워드의 차이점 (0) | 2020.11.05 |
ReactJS : 자식 구성 요소 내부 부모의 setState (0) | 2020.11.05 |
테이블 스캔과 클러스터형 인덱스 스캔의 차이점은 무엇입니까? (0) | 2020.11.05 |