Android相机启动加速详解

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

相关推荐

  • Android如何调用系统相机拍照

    本文实例为大家分享了Android调用系统相机拍照的具体代码,供大家参考,具体内容如下 /** * 调用系统相机 */ private void takePhoto() { Uri uri = null; if (which_image == FRONT_IMAGE) { frontFile = new File(getSDPath() +"/test/front_" + getDate() + ".jpg"); uri = Uri.fromFile(frontFi

  • android 7自定义相机预览及拍照功能

    本文实例为大家分享了Android实现摄像头切换,拍照及保存到相册,预览等功能,解决android7拍照之后不能连续预览的问题.参数设置相关问题以及前后摄像头语言颠倒等问题. import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; imp

  • Android自定义照相机的实例

    Android自定义照相机实现 近期小巫在学校有一个创新项目,也不是最近,是一个拖了很久的项目,之前一直没有去搞,最近因为要中期检查,搞得我跟小组成员一阵忙活,其实开发一款照相机软件并不太难,下面就是通过自定义的方式来实现手机照相的功能. 创建一个项目:FingerTakePicture 首先来搞一下界面: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&qu

  • Android调用系统照相机拍照与摄像的方法

    前言 在很多场景中,都需要用到摄像头去拍摄照片或视频,在照片或视频的基础之上进行处理.但是Android系统源码是开源的,很多设备厂商均可使用,并且定制比较混乱.一般而言,在需要用到摄像头拍照或摄像的时候,均会直接调用系统现有的相机应用,去进行拍照或摄像,我们只取它拍摄的结果进行处理,这样避免了不同设备的摄像头的一些细节问题.本篇博客将介绍在Android应用中,如何调用系统现有的相机应用去拍摄照片与短片,并对其进行处理,最后均会以一个简单的Demo来演示效果. 1.系统现有相机应用的调用 对于

  • Android 用 camera2 API 自定义相机

    前言 笔者因为项目需要自定义相机,所以了解了一下 Android 关于 camera 这块的 API.Android SDK 21(LOLLIPOP) 开始已经弃用了之前的 Camera 类,提供了 camera2 相关 API,目前网上关于 camera2 API 介绍的资料比较少,笔者搜集网上资料,结合自己的实践,在这里做一个总结. 流程 因为 camera2 提供的接口比较多,虽然很灵活,但是也增加了使用的复杂度.首先来大致了解一下调用 camera2 的流程,方便我们理清思路. 要显示相

  • Android 自定义相机及分析源码

    Android 自定义相机及分析源码 使用Android 系统相机的方法: 要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 <intent-filter> <action android:name="android.intent.action.IMAGE_CAPTURE" /> <category android:name="android.intent.category.DEFAULT" />

  • Android开源库自定义相机模块

    简介 相机模块库,自定义相机,通过简单的调用即可实现拍照.图片裁剪.录像及录像抓拍功能: 实现图片压缩,减少图片体积:自定义相机可避免使用系统相机导致的照片或视频体积过大问题: 内置相机及sd卡权限获取的处理: github链接如下,帮忙star支持下~ github链接 实现功能: - 拍照 - 图片裁剪 - 录像 - 录像抓拍 在项目中导入该库 在工程的 build.gradle中加入: allprojects { repositories { ... maven { url "https:

  • Android 照相机的实例应用

    Android 照相机的实例应用 关键技术: SurfaceHolder.Callback public class MyCameraDemo extends Activity { private SurfaceView surface = null ; private Button but = null ; private SurfaceHolder holder = null ; private Camera cam = null ; private boolean previewRunni

  • Android仿最新微信相机功能

    最近在开发即时通讯这个模块的时候使用到了自定义的相机,需求与微信一样,要求相机能长按和轻点,当时在网上找自定义相机的资源,很少,所以,我在这里把我的一些开发经验贴出来,供大家学习. 大致完成的功能如下: 长按拍摄视频,轻点拍照 前后摄像头的切换 闪光的的开启,关闭,自动 图片的压缩 自动聚焦,手动聚焦 效果图如下: 相关代码如下: package com.ses.im.app.chat.newcamera; import android.app.Activity; import android.

  • Android相机启动加速详解

    在Android上实现一个简单能用的相机其实挺容易.谷歌随便搜一搜就有很多能用的Sample.当然就像谷歌能搜到的其他代码一样,这些Sample虽然能用但离好用还很远. 这篇文章就只说说从用户点击启动按钮到用户能看到实时预览的这一小段时间内,我们所做的优化. Android手机上良莠不齐的硬件,导致相机启动时间有长有短,很难预期.用户在使用app过程中,过长的等待会产生焦虑.我们要做的就是让用户尽量感知不到相机启动的耗时. 按照网上能搜到的一般相机Sample的说法,从启动相机到实时预览,我们需

  • Android zygote启动流程详解

    对zygote的理解 在Android系统中,zygote是一个native进程,是所有应用进程的父进程.而zygote则是Linux系统用户空间的第一个进程--init进程,通过fork的方式创建并启动的. 作用 zygote进程在启动时,会创建一个Dalvik虚拟机实例,每次孵化新的应用进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面,从而使得每个应用程序进程都有一个独立的Dalvik虚拟机实例. zygote进程的主要作用有两个: 启动SystemServer. 孵化应用

  • Android ActivityManagerService启动流程详解

    目录 概述 AMS的启动流程 启动流程图 概述 AMS是系统的引导服务,应用进程的启动.切换和调度.四大组件的启动和管理都需要AMS的支持.从这里可以看出AMS的功能会十分的繁多,当然它并不是一个类承担这个重责,它有一些关联类. AMS的启动流程 1:SystemServer#main -> 2:SystemServer#run -> 3:SystemServiceManager#startBootstrapServices 1:首先SystemServer进程运行main函数, main函数

  • Android分包MultiDex策略详解

    1.分包背景 这里首先介绍下MultiDex的产生背景. 当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt.DexOpt的执行过程是在第一次加载Dex文件的时候执行的.这个过程会生成一个ODEX文件,即Optimised Dex.执行ODex的效率会比直接执行Dex文件的效率要高很多. 但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面.但是这个链表的长度是用一

  • Android init.rc文件详解及简单实例

    Android init.rc文件详解 本文主要来自$ANDROID_SOURCE/system/init/readme.txt的翻译. 1 简述 Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action,Commands,Services,Options.在init.rc文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的.如果需要在单词内使用空格,那么得使用转义字符"\",如果在一行的末尾有一个反斜杠,那么

  • Android中Service服务详解(二)

    本文详细分析了Android中Service服务.分享给大家供大家参考,具体如下: 在前面文章<Android中Service服务详解(一)>中,我们介绍了服务的启动和停止,是调用Context的startService和stopService方法.还有另外一种启动方式和停止方式,即绑定服务和解绑服务,这种方式使服务与启动服务的活动之间的关系更为紧密,可以在活动中告诉服务去做什么事情. 为了说明这种情况,做如下工作: 1.修改Service服务类MyService package com.ex

  • Crashlytics Android 异常报告统计管理(详解)

    简介 Crashlytic 成立于2011年,是专门为移动应用开者发提供的保存和分析应用崩溃信息的工具.Crashlytics的使用者包括:支付工具Paypal, 点评应用Yelp, 照片分享应用Path, 团购应用GroupOn等移动应用. 2013年1月,Crashlytics被Twitter收购,成为又一个成功的创业产品.被收购之后,由于没有了创业公司的不稳定因素,我们更有理由使用它来分析应用崩溃信息. 使用Crashlytics的好处有: 1.Crashlytics不会漏掉任何应用崩溃信

  • 史上最全Android build.gradle配置详解(小结)

    Android Studio是采用gradle来构建项目的,gradle是基于groovy语言的,如果只是用它构建普通Android项目的话,是可以不去学groovy的.当我们创建一个Android项目时会包含两个Android build.gradle配置详解文件,如下图: 一.Project的build.gradle文件: 对应的build.gradle代码如下: // Top-level build file where you can add configuration options

  • 基于Android FileProvider 属性配置详解及FileProvider多节点问题

    众所周知在android7.0,修改了对私有存储的限制,导致在获取资源的时候,不能通过Uri.fromFile来获取uri了我们需要适配7.0+的机型需要这样写: 1:代码适配 if (Build.VERSION.SDK_INT > 23) {// intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(context, SysInfo.packageN

  • Android LitePal的使用详解

    前言 数据库操作一直都是比较繁琐而且单一的东西,平时开发中数据库也很常见.有学过mysql的读者可能会觉得sql语句确实让人很难受.同样android中,虽然有内置数据库SQLite,但是操作起来还是非常的不方便.跟网络请求类似,当我们用原生的HttpURLConnection请求数据再用json解析,过程很繁琐,所以我们一般是封装成一个工具类,但是retrofit出现了,他帮我们解决了网络请求和解析数据的封装,同时还支持RxJava的异步,十分强大.不了解retrofit的读者也建议你们去学习

随机推荐