As pointed out in the Android documentation on handling the camera, it is recommended to use a separate thread to open the camera.
Well, I am doing just that but do have some difficulties:
For my camera object I am using a global instance variable. Now, when I start my app, I create a separate thread in onResume()
and do all initializations for that camera object in that thread.
Later when I leave the app, I release the camera in onPause()
. This works all just fine.
But the problem is: When I did some stress tests and switched really fast between onResume()
and onPause()
(by hitting the multitask button excessively fast) my app crashed. The reason was that there was a Method called after release()
.
This makes sense since it is possible that the camera may be released in onPause()
but at the same time the thread hasn't finished with its initialization. Thus, the thread tries to make a call on a camera object that was already released.
Now, what can I do to fix this? Maybe not use a global camera object? Or how can I make this thread safe?
By design, Android View objects are not thread-safe. An app is expected to create, use, and destroy UI objects, all on the main thread. If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.
When an application is launched in Android, it creates the first thread of execution, known as the “main” thread. The main thread is responsible for dispatching events to the appropriate user interface widgets as well as communicating with components from the Android UI toolkit.
Technically there is no limit, but at some point having more threads is going to be less efficient than having less. If you want to see a pretty much optimal implementation of a ThreadPool look at the source code of the AsyncTask .
The thread, sometimes called the UI thread, is responsible for updating the screen in an Android app.
You have a couple of options.
Option #1: only touch the Camera
from the UI thread.
This is pretty straightforward, since you can manage the camera in onPause()
and onResume()
. If you want to do work with the Camera
object from another thread, you can just use runOnUiThread()
. The problem with this approach is that you don't want to do significant amounts of work on the UI thread, so you have to send the preview data elsewhere.
This approach was used in the "Show + capture camera" activity in Grafika. There's a long-winded comment about the thread management here. Note that the camera preview gets handled by the GL rendering thread.
Option #2: only touch the Camera
from a dedicated camera thread.
For this to work, you need to send messages to the thread every time you want it to do something with the Camera
. This is most easily done with the usual Handler
and Looper
arrangement. The trick is that, when you send a message, the call returns immediately. If you send a message to tell the thread to shut the camera down from onPause()
, the actual camera shutdown may not happen for a while -- and in the mean time, the activity is shutting other things down, and (if events are happening quickly) might be back in onResume()
before the shutdown has completed. (I suspect this is what's happening to you.)
For startup and shutdown, you need to wait for completion. You can see an example of this in a different (non-Camera) Grafika activity, where it waits for the render thread to finish initializing before sending it messages. The shutdown is synchronized by join()
ing the thread as it stops.
The key thing to remember is that you can only call Camera
methods from a single thread -- whatever thread you use to open the camera is the only thread that gets to touch that instance. After that, it's a "simple" matter of concurrency management. (Strictly speaking, the doc says "this class's methods must never be called from multiple threads at once", so you can use multiple threads if you serialize the access carefully. The easiest way to serialize access is to only do the calls from one thread.)
You can use a boolean flag for camera thread to indicate that the camera is no longer necessary (for example in the case of pause).
so your code is going to be something like this:
private boolean isNeeded;
private boolean isInitialized;
@Override
protected void onResume(){
super.onResume();
Thread thread = new Thread(){
@Override
public void run(){
// getCamera();
// initializeCamera();
if (isNeeded){
inInitialized = true;
// do stuff with Camera
} else {
//release camera here
}
}
}
thread.start();
}
protected void onPause(){
super.onPause();
isNeeded = false;
if (isInitialized){
//release camera here
}
}
so your initializing thread checks if the camera isn't needed and release it if so, or initialize the camera successfully and you release the camera from the onPause.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With