It's actually quite easy to implement a simple and usable camera on Android. Just search Google and you will find many useful Samples. Of course, just like other codes that Google can search for, these Samples are still far from being easy to use.
This article only talks about the optimizations we made in the short period of time from when the user clicks the start button to when the user can see the real-time preview.
The uneven hardware on Android phones has caused the camera startup time to be long and short, which is difficult to predict. When users use the app, excessive waiting will cause anxiety. What we need to do is to let users not perceive the time-consuming operation of the camera.
According to the general camera Sample that can be found online, from starting the camera to real-time preview, we need to do three things: 1. Build a GlSurfaceView and get its SurfaceHolder; 2. Get a Camera device and start it; 3. Set the preview of the Camera device to the SurfaceHolder we prepared.
We write GlSurfaceView into xml as follows:
<GlSurfaceView android: android:layout_width="match_parent" android:layout_height="match_parent" />
We can get this GlSurfaceView in the onCreate of CameraActivity. But it is not that GlSurfaceView is created and the SurfaceHolder is ready. We also need to set a HolderListener for it to wait for the SurfaceHolder it generates.
private class SurfaceObserver implements { public void onSurfaceHolderCreated(SurfaceHolder holder) { mSurfaceHolder = holder; } } (new SurfaceObserver());
Then let's open a Camera.
//The code omits the detection number of Cameras, obtaining CameraId and setting CameraPreviewSize logic. That's what the rest of the board is. mCamera = (mCameraId);
Finally, set the SurfaceHolder to Camera and you can turn on the preview.
(mSurfaceHolder); ();
Generally, Sample Code searched online will put these three steps into the Activity onCreate in sequence. That is, wait until the SurfaceHolderListener gets the SurfaceHolder and starts Camera. Camera is started and then associate them and start the preview. Let’s take a look at the time-consuming process on Xiaomi 1.
Get SurfaceHolderListener for 0.3 seconds
Start Camera for 1 second
If we ignore the time of activity creation and other code execution time, we will take a total of 1.3 seconds. Users are impatient with waiting more than 1 second. Not to mention that the Camera boot time can reach more than 1.5 seconds of anti-humanity on some mobile phones.
An optimization solution that is easy to think of is to get the SurfaceHolder and start the Camera asynchronously in two threads. This should reduce the time to about 1 second on Xiaomi 1, which is barely acceptable.
The acquisition of SurfaceHolder itself is asynchronous. We only need to start an asynchronous thread in the onCreate of the Activity to start Camera. After the execution of these two asynchronous threads is completed, they will respectively detect whether the other thread has completed. The simplified code is as follows.
public void onCreate(Bundle savedInstanceState){ (savedInstanceState); (new SurfaceObserver()); new Handler().post(new Runnable(){ public void run(){ mCamera = (mCameraId); checkCamera(); } }); } private class SurfaceObserver implements { public void onSurfaceHolderCreated(SurfaceHolder holder) { mSurfaceHolder = holder; checkCamera(); } } private void checkCamera(){ if(mSurfaceHolder != null && mCamera != null{ (mSurfaceHolder); (); } }
Is this the optimization completed? Let's think about how Apple is made. Apple likes to use some transition animations to conceal the time-consuming loading of the background. After all, the 1 second time of the camera startup is limited by the hardware, and we can't shorten it at the app level, so we might as well add an animation and start the camera in advance during the animation process to get an Apple-style trick. I added a 0.5-second feedback animation to the button entering the camera Activity, and a 0.3-second Pending animation to the camera Activity. After the two animations are completed, Xiaomi 1's camera will be started in just 0.2 seconds, which is completely acceptable to users.
There are two problems with the above logic implementation. One is to start the camera before we get the instance of CameraActivity, and the other is that after Camera starts, the checkCamera method of the Activity instance cannot be called. So we can only store Camera and Activity instances in a static variable respectively. It is not complicated to write, but you need to pay attention to the recycling of variables. In the onDestroy of Activity, first set the Camera release to null, and set the reference of the Activity instance to null, which is fine.
static Camera mCamera; static CameraActivity instance; public void onCreate(Bundle savedInstanceState){ (savedInstanceState); instance = this; (new SurfaceObserver()); } public static void openCamera{ new Handler().post(new Runnable(){ public void run(){ mCamera = (mCameraId); if(instance != null){ (); } } }); } private class SurfaceObserver implements { public void onSurfaceHolderCreated(SurfaceHolder holder) { mSurfaceHolder = holder; checkCamera(); } } private void checkCamera(){ if(mSurfaceHolder != null && mCamera != null{ (mSurfaceHolder); (); } }