Ich habe versucht, eine Anwendung zu implementieren, die eine Kameravorschau auf einer Oberfläche erfordert. Aus meiner Sicht bestehen sowohl Aktivität als auch Oberflächenlebenszyklus aus den folgenden Zuständen:
- Wenn ich meine Aktivität zum ersten Mal starte:
onResume()->onSurfaceCreated()->onSurfaceChanged()
- Wenn ich meine Aktivität verlasse:
onPause()->onSurfaceDestroyed()
In diesem Schema kann ich entsprechende Anrufe wie Öffnen / Freigeben der Kamera und Starten / Stoppen der Vorschau in onPause/onResume
und ausführen onSurfaceCreated()/onSurfaceDestroyed()
.
Es funktioniert gut, es sei denn, ich sperre den Bildschirm. Wenn ich die App starte, sperre ich den Bildschirm und entsperre ihn später. Ich sehe:
onPause()
- und nichts anderes, nachdem der Bildschirm gesperrt wurde - dann onResume()
nach dem Entsperren - und danach keine Oberflächenrückrufe. Wird tatsächlich onResume()
aufgerufen, nachdem der Netzschalter gedrückt und der Bildschirm eingeschaltet wurde, der Sperrbildschirm jedoch noch aktiv ist. Dies ist also der Fall, bevor die Aktivität überhaupt sichtbar wird.
Mit diesem Schema wird nach dem Entsperren ein schwarzer Bildschirm angezeigt, und es werden keine Oberflächenrückrufe aufgerufen.
Hier ist ein Codefragment, das nicht die eigentliche Arbeit mit der Kamera beinhaltet, sondern die SurfaceHolder
Rückrufe. Das obige Problem wird auch mit diesem Code auf meinem Telefon reproduziert (Rückrufe werden in normaler Reihenfolge aufgerufen, wenn Sie die Taste "Zurück" drücken, fehlen jedoch, wenn Sie den Bildschirm sperren):
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");
}
}
Irgendwelche Ideen, warum die Oberfläche nach dem Anhalten der Aktivität unzerstört bleibt? Wie gehen Sie in solchen Fällen mit dem Kamera-Lebenszyklus um?
quelle
Antworten:
Bearbeiten: Wenn das targetSDK größer als 10 ist, wird die App in den Ruhezustand versetzt
onPause
undonStop
. QuelleIch habe den Lebenszyklus von Activity und SurfaceView in einer winzigen Kamera-App auf meinem Lebkuchen-Handy betrachtet. Sie sind völlig richtig; Die Oberfläche wird nicht zerstört, wenn der Netzschalter gedrückt wird, um das Telefon in den Ruhezustand zu versetzen. Wenn das Telefon in den Ruhezustand wechselt, wird die Aktivität ausgeführt
onPause
. (Und nichtonStop
.)onResume
Dies geschieht, wenn das Telefon aufwacht, und wie Sie darauf hinweisen, geschieht dies, während der Sperrbildschirm noch sichtbar ist und Eingaben akzeptiert, was etwas seltsam ist. Wenn ich die Aktivität durch Drücken der Home-Taste unsichtbar mache, führt die Aktivität sowohlonPause
als auch ausonStop
. Etwas verursachtsurfaceDestroyed
in diesem Fall einen Rückruf zwischen dem EndeonPause
und dem Anfang vononStop
. Es ist nicht sehr offensichtlich, aber es scheint sehr konsequent zu sein.Wenn der Netzschalter gedrückt wird, um das Telefon in den Ruhezustand zu versetzen, läuft die Kamera weiter, es sei denn, es wird ausdrücklich etwas unternommen, um es zu stoppen! Wenn die Kamera für jedes Vorschaubild einen Rückruf pro Bild mit einem Log.d () ausführt, werden die Protokollanweisungen immer wieder angezeigt, während das Telefon vorgibt, zu schlafen. Ich finde das sehr hinterhältig .
Als eine weitere Verwirrung, die Rückrufe
surfaceCreated
undsurfaceChanged
passieren nachonResume
der Tätigkeit, wenn die Oberfläche erstellt wird.In der Regel verwalte ich die Kamera in der Klasse, die die SurfaceHolder-Rückrufe implementiert.
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; } } }
und dann in der Aktivität:
@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(); }
und das scheint für mich in Ordnung zu sein. Rotationsereignisse führen dazu, dass die Aktivität zerstört und neu erstellt wird, wodurch auch die SurfaceView zerstört und neu erstellt wird.
quelle
super.onResume
irgendwo in deronResume
Routine aufgerufen wird . Ich denke.Eine weitere einfache Lösung, die gut funktioniert - um die Sichtbarkeit der Vorschauoberfläche zu ändern.
private SurfaceView preview;
Vorschau ist in der
onCreate
Methode init . InonResume
derView.VISIBLE
für die Vorschau eingestellten Methode :@Override public void onResume() { preview.setVisibility(View.VISIBLE); super.onResume(); }
und jeweils in
onPause
festgelegter SichtbarkeitView.GONE
:@Override public void onPause() { super.onPause(); preview.setVisibility(View.GONE); stopPreviewAndFreeCamera(); //stop and release camera }
quelle
Dank der beiden vorherigen Antworten gelang es mir, die Vorschau meiner Kamera einfach wiederzugeben, während ich entweder vom Hintergrund oder vom Sperrbildschirm zurückging.
Wie bei @ e7fendy erwähnt, wird der Rückruf von SurfaceView während des Screenlocks nicht aufgerufen, da die Oberflächenansicht für das System weiterhin sichtbar ist.
Wie @validcat empfohlen hat, wird durch Aufrufen von
preview.setVisibility(View.VISIBLE);
bzw.preview.setVisibility(View.GONE);
in onPause () und onResume () die Oberflächenansicht gezwungen, sich selbst weiterzuleiten, und es werden Rückrufe aufgerufen.Bis dahin sorgt die Lösung von @ emrys57 und diese beiden oben genannten Aufrufe für Sichtbarkeitsmethoden dafür, dass Ihre Kameravorschau problemlos funktioniert :)
Also kann ich jedem von euch nur +1 geben, wie ihr es alle verdient habt;)
quelle
SurfaceHolder.Callback hängt mit seiner Oberfläche zusammen.
Ist die Aktivität auf dem Bildschirm? In diesem Fall wird SurfaceHolder.Callback nicht angezeigt, da die Oberfläche noch auf dem Bildschirm angezeigt wird.
Um SurfaceView zu steuern, können Sie es nur in onPause / onResume verarbeiten. Für SurfaceHolder.Callback können Sie es verwenden, wenn die Oberfläche geändert (erstellt, in der Größe geändert und zerstört) wird, z. B. openGL initialisieren, wenn Oberfläche erstellt wird, und openGL zerstören, wenn Oberfläche zerstört wird usw.
quelle
Hier ist eine alternative Lösung für alle Rückrufmethoden, die möglicherweise alle dem gleichen undefinierten Ereignisreihenfolgeverhalten mit Aktivitätszyklus unterliegen. Wenn Sie nicht den gesamten Android-Code für jeden Rückruf überprüfen, den Sie verwenden, um den Ursprungsauslöser zu bestimmen und wer die Implementierungen steuert, und hoffen, dass sich die Codebasis in Zukunft nicht ändert, kann man wirklich sagen, dass die Ereignisreihenfolge zwischen Rückrufen und Aktivitätslebenszyklusereignisse könnten garantiert werden.
Derzeit können diese Ordnungswechselwirkungen für Entwicklungszwecke normalerweise als undefiniertes Verhalten bezeichnet werden.
Am besten behandeln Sie dieses undefinierte Verhalten immer korrekt, sodass es überhaupt kein Problem darstellt, indem Sie sicherstellen, dass die Befehle ein definiertes Verhalten sind.
Mein Sony Xperia zum Beispiel schaltet meine aktuelle App im Ruhezustand durch, indem es die App zerstört und dann neu startet und in den Pausenzustand versetzt, ob Sie es glauben oder nicht.
Wie viel Testverhalten von Ereignisbestellungen Google in seinem SDK als spezielles Test-Build für Host-Umgebungsimplemente bereitstellt, weiß ich nicht, aber sie müssen sich definitiv bemühen, um sicherzustellen, dass das Verhalten von Ereignisbestellungen durch eine strenge Einhaltung der Regeln begrenzt wird Angelegenheit.
https://code.google.com/p/android/issues/detail?id=214171&sort=-opened&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened
import android.util.Log; import android.util.SparseArray;
/ ** * Erstellt von woliver am 24.06.2016. * * Die Android-Hostumgebung bestimmt einen Aktivitätslebenszyklus für OnCreate, onStart, onResume, onPause, onStop, onDestory *, in dem Speicher und Handles für andere Anwendungen freigegeben werden müssen. * Bei der Wiederaufnahme müssen wir diese Elemente manchmal neu binden und mit anderen Objekten aktivieren. * In der Regel stellen diese anderen Objekte Rückrufmethoden aus der Host-Umgebung bereit, die * ein onCreated und ein onDestroy bereitstellen, in denen wir nur von OnCreated aus an dieses Objekt binden und die Bindung onDestory verlieren können. * Diese Art von Rückrufmethoden, deren Ausführungszeit von unserer Host-Umgebung gesteuert wird * und es gibt keine Garantie dafür, dass das Verhalten / die Reihenfolge der Ausführung des Aktivitätslebenszyklus und dieser Rückrufmethoden * konsistent bleibt. * Zum Zwecke der Entwicklung können die Interaktionen und die Reihenfolge der Ausführung technisch als undefiniert bezeichnet werden *, da dies dem Implementierer der Host-Implementierung, Samsung, Sony, HTC, überlassen bleibt. * * Siehe folgendes Entwicklerdokument:https://developer.android.com/reference/android/app/Activity.html * Zitat: * Wenn eine Aktivität durch eine andere Aktivität vollständig verdeckt wird, wird sie gestoppt. Es behält weiterhin alle Status- * und Mitgliedsinformationen bei, ist jedoch für den Benutzer nicht mehr sichtbar, sodass sein Fenster * ausgeblendet ist und häufig vom System beendet wird, wenn an anderer Stelle Speicher benötigt wird. * EndQuato: * * Wenn die Aktivität nicht ausgeblendet ist, wurden keine Rückrufe aufgerufen, von denen man erwartet hätte, dass sie vom Hostsystem * aufgerufen wurden, wie z. B. der SurfaceView-Rückruf der OnCreate- und OnDestory-Methodenschnittstelle. * Dies bedeutet, dass Sie das an SurfaceView gebundene Objekt wie eine Kamera * in einer Pause stoppen müssen und das Objekt niemals erneut binden müssen, da der OnCreate-Rückruf niemals aufgerufen wird. * * /
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(); }
Beispiel für die Implementierung und Verwendung einer Klasse, die sich mit oder dem undefinierten Verhalten von Implementierern der Android-Host-Umgebung befasst.
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); } }
quelle