私は、サーフェス上でカメラ プレビューを必要とするアプリケーションを実装しようとしています。私の見るところ、アクティビティとサーフェスのライフサイクルはどちらも次の状態で構成されています。
- アクティビティを初めて起動すると:
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");
}
}
アクティビティが一時停止された後もサーフェスが破壊されない理由について何かご存知ですか? また、このような場合、カメラのライフサイクルをどのように処理しますか?
ベストアンサー1
編集:targetSDKが10より大きい場合、アプリをスリープ状態にするとonPause
そして onStop
。ソース
私はジンジャーブレッド フォンの小さなカメラ アプリで Activity と SurfaceView の両方のライフサイクルを調べました。あなたのおっしゃる通り、電源ボタンを押して電話をスリープ状態にしても、サーフェスは破壊されません。電話がスリープ状態になると、Activity は を実行しますonPause
。( は実行しませんonStop
。)onResume
電話が起動すると を実行します。そして、あなたが指摘したように、ロック画面が表示されていて入力を受け付けている間にこれを実行するので、少し奇妙です。ホーム ボタンを押して Activity を非表示にすると、Activity は と の両方を実行しますonPause
。この場合、 の終了と の開始の間にonStop
、何かが へのコールバックを引き起こします。あまり明白ではありませんが、非常に一貫しているように見えます。surfaceDestroyed
onPause
onStop
電源ボタンを押して電話をスリープ状態にすると、明示的に停止しない限り、カメラは動き続けます。カメラに各プレビューフレームごとに画像ごとのコールバックを実行させ、そこにLog.d()を含めると、電話がスリープ状態を装っている間もログステートメントが出力され続けます。これが原因だと思います。とてもこっそり。
もう一つの混乱として、コールバックとコールバック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 も破棄され再作成されます。