最近用到Camera.java这个类,记录下在开发过程中遇到的一些问题和最终是如何解决的。
1、ActivityCamera的启动和关闭十分缓慢,在普通性能的手机上运行比较明显;
分析:
从获取Camera的实例和进行一些配置后,到开启预览startPreview整个过程的占用主线程时间较长,直接影响了Activity的启动。
private void initCamera(SurfaceHolder surfaceHolder){
if (mCamera==null) {
try {
mCamera = Camera.open();//打开后置摄像头
}catch (RuntimeException e){
}
}
if (surfaceHolder!=null && sCamera!=null){
try {
sCamera.setPreviewDisplay(surfaceHolder);
initFromCameraParameters(sCamera.getParameters());
setDesiredCameraParameters(sCamera);
sCamera.startPreview();
sCamera.autoFocus(focusCallBack);
} catch (IOException e) {
e.printStackTrace();
}
}
}
一开始直接开启了一个子线程将initCamera直接放在子线程中执行,发现Activity启动时间确实不再受影响,但是释放Camera的时候并没有效果,分析Camera的源码发现,
1、Camera.open();打开默认的后置摄像头CAMERA_FACING_BACK
public static Camera open() {
int numberOfCameras = getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
return new Camera(i);
}
}
return null;
}
1.2、new Camera(cameraId);
Camera(int cameraId) {
int err = cameraInitNormal(cameraId);
if (checkInitErrors(err)) {
if (err == -EACCES) {
throw new RuntimeException("Fail to connect to camera service");
} else if (err == -ENODEV) {
throw new RuntimeException("Camera initialization failed");
}
// Should never hit this.
throw new RuntimeException("Unknown camera error");
}
}
1.3、int err = cameraInitNormal(cameraId);
private int cameraInitNormal(int cameraId) {
return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
}
其中halVersion是硬件版本号
private int cameraInitVersion(int cameraId, int halVersion) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mUsingPreviewAllocation = false;
mZoomListener = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
ActivityThread.currentOpPackageName());
}
private native final int native_setup(Object camera_this, int cameraId, int halVersion,
String packageName);
native_setup是一个本地方法,具体是在:/frameworks/base/core/jni/android_hardware_Camera.cpp
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
// Convert jstring to String16
const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
env->GetStringChars(clientPackageName, NULL));
jsize rawClientNameLen = env->GetStringLength(clientPackageName);
String16 clientName(rawClientName, rawClientNameLen);
env->ReleaseStringChars(clientPackageName,
reinterpret_cast<const jchar*>(rawClientName));
sp<Camera> camera;
if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
// Default path: hal version is don't care, do normal camera connect.
camera = Camera::connect(cameraId, clientName,
Camera::USE_CALLING_UID);
} else {
jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
Camera::USE_CALLING_UID, camera);
if (status != NO_ERROR) {
return status;
}
}
if (camera == NULL) {
return -EACCES;
}
// make sure camera hardware is alive
if (camera->getStatus() != NO_ERROR) {
return NO_INIT;
}
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
// This should never happen
jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
return INVALID_OPERATION;
}
// We use a weak reference so the Camera object can be garbage collected.
// The reference is only used as a proxy for callbacks.
sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
context->incStrong((void*)android_hardware_Camera_native_setup);
camera->setListener(context);
// save context in opaque field
env->SetLongField(thiz, fields.context, (jlong)context.get());
return NO_ERROR;
}
----------
2、mCamera.setPreviewDisplay(surfaceHolder);
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
if (holder != null) {
setPreviewSurface(holder.getSurface());
} else {
setPreviewSurface((Surface)null);
}
}
public native final void setPreviewSurface(Surface surface) throws IOException;
static void android_hardware_Camera_setPreviewSurface(JNIEnv *env, jobject thiz, jobject jSurface)
{
ALOGV("setPreviewSurface");
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
sp<IGraphicBufferProducer> gbp;
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
if (surface != NULL) {
gbp = surface->getIGraphicBufferProducer();
}
}
if (camera->setPreviewTarget(gbp) != NO_ERROR) {
jniThrowException(env, "java/io/IOException", "setPreviewTexture failed");
}
}
3、mCamera.startPreview();开启预览
public native final void startPreview();
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
ALOGV("startPreview");
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
if (camera->startPreview() != NO_ERROR) {
jniThrowRuntimeException(env, "startPreview failed");
return;
}
}
释放和关闭相机
static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz)
{
ALOGV("stopPreview");
sp<Camera> c = get_native_camera(env, thiz, NULL);
if (c == 0) return;
c->stopPreview();
}
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
ALOGV("release camera");
JNICameraContext* context = NULL;
sp<Camera> camera;
{
Mutex::Autolock _l(sLock);
context = reinterpret_cast<JNICameraContext*>(env->GetLongField(thiz, fields.context));
// Make sure we do not attempt to callback on a deleted Java object.
env->SetLongField(thiz, fields.context, 0);
}
// clean up if release has not been called before
if (context != NULL) {
camera = context->getCamera();
context->release();
ALOGV("native_release: context=%p camera=%p", context, camera.get());
// clear callbacks
if (camera != NULL) {
camera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
camera->disconnect();
}
// remove context to prevent further Java access
context->decStrong((void*)android_hardware_Camera_native_setup);
}
}
else if (hadSurface) {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
mSurfaceHolder.mSurfaceLock.lock();
try {
mSurfaceHolder.mSurface = new Surface();
} finally {
mSurfaceHolder.mSurfaceLock.unlock();
}
}