Detailed explanation of Android camera startup acceleration

  • 2021-09-12 02:26:27
  • OfStack

It is actually quite easy to implement a simple and usable camera on Android. Google has a lot of Sample that can be used if you search it casually. Of course, like other code-1 that Google can find, these Sample works but are far from easy to use.

This article will only talk about the optimization we made in the short time between the user clicking the start button and the user seeing the real-time preview.

The mixed hardware on Android mobile phone leads to the long and short start-up time of the camera, which is difficult to predict. In the process of using app, users will be anxious if they wait too long. What we have to do is to make the user try not to perceive the time taken to start the camera.

According to a camera Sample that can be found online, we need to do three things from starting the camera to previewing in real time: 1. Build an GlSurfaceView and get its SurfaceHolder;; 2. Get an Camera device and start it; 3. Set the preview of Camera device to our prepared SurfaceHolder.

We write GlSurfaceView into xml as follows:


<GlSurfaceView
  android:id="@+id/camera_preview"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

We can get this GlSurfaceView in onCreate of CameraActivity. However, GlSurfaceView is not ready when SurfaceHolder is created. We also need to set it up with an HolderListener to wait for the SurfaceHolder it generates.


  private class SurfaceObserver implements
      SupportCamSurfaceView.SurfaceHolderLisener {
 
    public void onSurfaceHolderCreated(SurfaceHolder holder) {
      mSurfaceHolder = holder;
    }
  }
  vCameraPreview.setHolderListener(new SurfaceObserver());

Then we come to Open1 and Camera.


  // Code omits detection Camera Number, get CameraId There are also settings CameraPreviewSize The logic of. That's the content of other parts. 
  mCamera = Camera.open(mCameraId);

Finally, set SurfaceHolder to Camera to start preview.


  mCamera.setPreviewTexture(mSurfaceHolder);
  mCamera.startPreview();

Sample Code found on the Internet will put these three steps into onCreate of Activity and execute them in sequence. That is, wait for SurfaceHolderListener to acquire SurfaceHolder before starting Camera. When Camera is started, associate them and start preview. Let's take a look at the time-consuming process on Xiaomi 1.

Get SurfaceHolderListener 0.3 seconds

Start Camera for 1 second

Ignoring the time it took to create Activity and the other code to execute, we spent 1.3 seconds. Users are impatient with waiting longer than 1 second. Not to mention that on some mobile phones, the startup time of Camera can reach more than 1.5 seconds of anti-human.

One optimization that is easy to think of is to get SurfaceHolder and start Camera asynchronously in two threads. This should shorten the time spent on Xiaomi 1 to about 1 second, which is barely acceptable.

The acquisition of the SurfaceHolder itself is asynchronous. We only need to start another asynchronous thread in onCreate of Activity to start Camera. After the execution of these two asynchronous threads is completed, check whether the other thread is completed. The simplified code is as follows.


  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    vCameraPreview.setHolderListener(new SurfaceObserver());
    new Handler().post(new Runnable(){
      public void run(){
        mCamera = Camera.open(mCameraId);
        checkCamera();
      }    
    }); 
  }
 
  private class SurfaceObserver implements
      SupportCamSurfaceView.SurfaceHolderLisener {
 
    public void onSurfaceHolderCreated(SurfaceHolder holder) {
      mSurfaceHolder = holder;
      checkCamera();
    }
  }
 
  private void checkCamera(){
    if(mSurfaceHolder != null && mCamera != null{
      mCamera.setPreviewTexture(mSurfaceHolder);
      mCamera.startPreview();
    }
  }

Is this the optimization finished? Let's think about how apples are made. Apple likes to use 1 transition animation to cover up the time-consuming background loading. After all, the one-second time for the camera to start is limited by hardware, so 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 small 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, it takes only 0.2 seconds for Xiaomi 1's camera to start, which is completely acceptable to users.

There are two problems with the implementation of the above logic. One is to start the camera before we get the instance of CameraActivity, and the other is that we can't call the checkCamera method of the instance of Activity after the startup of Camera is completed. So we can only put the Camera and Activity instances into one static variable, respectively. It's not complicated to write, just pay attention to the recycling of variables. In onDestroy of Activity, Camera release is set to null first, and the reference of Activity instance is set to null directly, so it is OK.


  static Camera mCamera; 
  static CameraActivity instance; 
 
  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    instance = this;
    vCameraPreview.setHolderListener(new SurfaceObserver());
  }
 
  public static void openCamera{
    new Handler().post(new Runnable(){
      public void run(){
        mCamera = Camera.open(mCameraId);
        if(instance != null){
          instance.checkCamera();
        }
      }    
    }); 
  }
 
  private class SurfaceObserver implements
      SupportCamSurfaceView.SurfaceHolderLisener {
 
    public void onSurfaceHolderCreated(SurfaceHolder holder) {
      mSurfaceHolder = holder;
      checkCamera();
    }
  }
 
  private void checkCamera(){
    if(mSurfaceHolder != null && mCamera != null{
      mCamera.setPreviewTexture(mSurfaceHolder);
      mCamera.startPreview();
    }
  }


Related articles: