Android相机启动加速详解

所属分类: 软件编程 / Android 阅读数: 37
收藏 0 赞 0 分享

在Android上实现一个简单能用的相机其实挺容易。谷歌随便搜一搜就有很多能用的Sample。当然就像谷歌能搜到的其他代码一样,这些Sample虽然能用但离好用还很远。

这篇文章就只说说从用户点击启动按钮到用户能看到实时预览的这一小段时间内,我们所做的优化。

Android手机上良莠不齐的硬件,导致相机启动时间有长有短,很难预期。用户在使用app过程中,过长的等待会产生焦虑。我们要做的就是让用户尽量感知不到相机启动的耗时。

按照网上能搜到的一般相机Sample的说法,从启动相机到实时预览,我们需要做三件事:1.构建一个GlSurfaceView并获取它的SurfaceHolder;2.获取一个Camera device,启动它;3.将Camera device的预览设置为我们准备好的SurfaceHolder。

我们把GlSurfaceView写到xml里如下:

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

我们可以在CameraActivity的onCreate里获取到这个GlSurfaceView。可是并不是GlSurfaceView创建好了SurfaceHolder就也准备好了。我们还需要给它设置一个HolderListener来等待它生成出来的SurfaceHolder。

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

然后我们来Open一个Camera。

  //代码省略掉了检测Camera个数,获取CameraId还有设置CameraPreviewSize的逻辑。那是其他部分的内容了。
  mCamera = Camera.open(mCameraId);

最后把SurfaceHolder设置给Camera就可以开启预览了。

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

一般网上搜到的Sample Code会把这三步放到Activity的onCreate里顺序执行。也就是等SurfaceHolderListener获取到了SurfaceHolder再启动Camera。Camera启动完成再把它俩关联上并启动预览。我们来看一下再小米1上这个流程的耗时。

获取SurfaceHolderListener 0.3秒

启动Camera 1秒

如果把Activity创建的时间和其它代码执行的时间都忽略的话,我们一共耗费了1.3秒。而用户对大于1秒的等待都是不耐烦的。更不用说在有的手机上Camera启动时间能够达到反人类的1.5秒以上。

很容易想到的一个优化方案就是让获取SurfaceHolder和启动Camera在两个线程里异步进行。这样应该可以使耗时在小米1上缩短到1秒左右,勉强能接受。

SurfaceHolder的获取本身就是异步的。我们只需要在Activity的onCreate里再启动一个异步线程去启动Camera。在这两个异步线程执行完成后都分别去检测另一个线程是否完成。简化的代码如下。

  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();
    }
  }

这样就算优化完了吗?让我们想想苹果是怎么做的吧。苹果很喜欢用一些过渡动画来掩饰后台加载的耗时。毕竟相机启动的这1秒时间是由硬件限制的,我们在app层面上没办法把它缩短,所以我们不如加一个动画,并在动画过程中提前启动相机,来一个苹果式的小trick。我给进入相机Activity的按钮加了一个0.5秒的反馈动画,又给相机Activity加了一个0.3秒的Pending动画,在两个动画完成后,只需再有0.2秒的时间小米1的相机就完成启动了,这对用户来说已经是完全可以接受的了。

上面的逻辑实现起来有两个问题。一个是在我们获取到CameraActivity的实例之前就要开始启动相机了,另一个是Camera启动完成后没办法调用Activity实例的checkCamera方法。所以我们只能把Camera和Activity实例分别存放到一个static变量里。写起来不复杂,只是需要注意变量的回收。在Activity的onDestroy里先把Camera release再设为null,Activity实例的引用直接设为null,这样就可以了。

  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();
    }
  }

更多精彩内容其他人还在看

Android实现信号强度监听的方法

这篇文章主要介绍了Android实现信号强度监听的方法,是Android手机中很常见的一个实用功能,需要的朋友可以参考下
收藏 0 赞 0 分享

Android实现Activity界面切换添加动画特效的方法

这篇文章主要介绍了Android实现Activity界面切换添加动画特效的方法,非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

Android中Dialog去黑边的方法

这篇文章主要介绍了Android中Dialog去黑边的方法,需要的朋友可以参考下
收藏 0 赞 0 分享

Qt for Android开发实例教程

这篇文章主要介绍了Qt for Android开发的方法,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Android开发之时间日期操作实例

这篇文章主要介绍了Android开发之时间日期操作,是Android程序开发中常见的一个功能,需要的朋友可以参考下
收藏 0 赞 0 分享

Android开发之时间日期组件用法实例

这篇文章主要介绍了Android开发之时间日期组件用法,主要介绍了TimePicker和DatePicker组件,对于Android程序开发有不错的借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Android开发之获取网络链接状态

这篇文章主要介绍了Android获取网络链接状态的方法,主要是通过ConnectivityManager类来完成的,需要的朋友可以参考下
收藏 0 赞 0 分享

Android开发之广播机制浅析

这篇文章主要介绍了Android开发之广播机制浅析,主要包括了发布、接收及配置广播的实例,需要的朋友可以参考下
收藏 0 赞 0 分享

Android开发之登录验证实例教程

这篇文章主要介绍了Android开发之登录验证实现方法,包括发送数据、服务器端验证、配置文件等,需要的朋友可以参考下
收藏 0 赞 0 分享

Android开发之注册登录方法示例

这篇文章主要介绍了Android开发的注册登录方法,是针对Android程序设计中版本兼容性的进一步完善,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多