看展讯平台的Camera有一段时间了,今天来整理下openCamera的流程。
其实,CameraAPP的主要逻辑可分为两大部分:界面显示 + camera处理。
展讯和MTK平台CameraAPP的共同点是两者都有Module的概念,即不同的Module处理app中不同的模式,比如拍照模式、美颜模式、录像模式等等。
但是在与camera这个硬件设备打交道的时候,设计却明显不同。MTK平台app是将UI的处理与camera处理均放在当前Module中处理,而展讯平台是Module只处理与当前模式相关的界面逻辑,而将camera操作统一封装,由专门的类去处理。
如下是处理与camera相关逻辑的类的继承关系
从图明显看出,在CameraAgent处分支了,一支类名中带有2,一支没有,这是与camera使用api1还是api2有关的。显然我们现在都是使用的api2,因此我们关注左边的分支。
与camera相关的打开、预览、拍照、数据callback、关闭等操作均在这几个类里面处理。
现在我们以普通的photo模式为例,来看下openCamera流程调用。
从PhotoModule的resume开始,
@Override
public void resume() {
Log.i(TAG, "resume start!");
//---此处省略与UI相关的处理
requestCameraOpen();
}
流程转到requestCameraOpen方法
/**
* Uses the {@link CameraProvider} to open the currently-selected camera
* device, using {@link GservicesHelper} to choose between API-1 and API-2.
*/
protected void requestCameraOpen() {
/**
* SPRD: fix bug47362 Log.v(TAG, "requestCameraOpen");
*/
/*
* SPRD:Fix bug 513841 KEY_CAMERA_ID may be changed during the camera in
* background if user do switch camera action in contacts, and then we
* resume the camera, the init function will not be called, so the
* mCameraId will not be the latest one. Which will cause the switch
* camera function can not be used. @{
*/
int mCameraID = mDataModule.getInt(Keys.KEY_CAMERA_ID);
if(mIsImageCaptureIntent) {
mCameraID = mDataModule.getInt(Keys.KEY_INTENT_CAMERA_ID);
}
if(mCameraID != mCameraId){
mCameraId = mCameraID;
mActivity.getCameraAppUI().updateModeList();
}
if (mCameraId == DreamUtil.FRONT_CAMERA && isSupportSensoSelfShotModule()) {
mCameraId = CameraUtil.SENSOR_SELF_SHOT_CAMERA_ID;
}
/* @} */
/*start add for w+t*/
if (CameraUtil.isWPlusTAbility(mActivity,mActivity.getCurrentModuleIndex(),mCameraId)){
mCameraId = CameraUtil.BACK_W_PLUS_T_PHOTO_ID;
}
/*end add for w+t*/
// if (mActivity.getCurrentModuleIndex() == SettingsScopeNamespaces.AUTO_PHOTO && !DreamUtil.isFrontCamera(mActivity,mCameraId)) {
if (CameraUtil.isTcamAbility(mActivity,mActivity.getCurrentModuleIndex(),mCameraId)) {
mCameraId = CameraUtil.BACK_TRI_CAMERA_ID;
}
/**
* SPRD: Change for New Feature VGesture and dream camera
* original code
* @{
mActivity.getCameraProvider().requestCamera(
mCameraId,
GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity
.getContentResolver()));
*/
Log.i(TAG, "requestCameraOpen mCameraId:" + mCameraId);
doCameraOpen(mCameraId);
/**
* @}
*/
}
此方法的前半部分是对mCameraId各种情况下的校验,最后是调用doCameraOpen(mCameraId)
protected void doCameraOpen(int cameraId){
mActivity.getCameraProvider().requestCamera(cameraId, useNewApi());
}
doCameraOpen方法很简单,就是将流程又转到 requestCamera,这里我们关注下 mActivity.getCameraProvider()是个什么类型。
在CameraActivity中,我们看到getCameraProvider就是CameraController类, 去看下CameraController。
public class CameraController implements CameraAgent.CameraOpenCallback, CameraProvider
CameraController 内容不多,因为实现了CameraAgent.CameraOpenCallback,所以其主要内容是camera状态的回调,但是并不处理这些回调,是在在接收到回调后,转发给相应的处理者。
还是回到从PhotoModule跟过来的requestCamera方法。
@Override
public void requestCamera(int id, boolean useNewApi) {
Log.i(TAG, "requestCamera id="+id);
Log.i(TAG, "mRequestingCameraId = " + mRequestingCameraId + "mInfo = " + mInfo);
if (mRequestingCameraId != EMPTY_REQUEST || mRequestingCameraId == id) {
return;
}
mRequestingCameraId = id;
mActiveCameraDeviceTracker.onCameraOpening(CameraId.fromLegacyId(id));
// Only actually use the new API if it's supported on this device.
useNewApi = mCameraAgentNg != null && useNewApi;
CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent;
if (mCameraProxy == null) {
// No camera yet.
checkAndOpenCamera(cameraManager, id, mCallbackHandler, this);
} else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) {
boolean syncClose = GservicesHelper.useCamera2ApiThroughPortabilityLayer(mContext
.getContentResolver());
Log.v(TAG, "different camera already opened, closing then reopening");
mCameraProxy = null;
// Already has camera opened, and is switching cameras and/or APIs.
if (mUsingNewApi) {
if (mCameraAgentNg != null)
mCameraAgentNg.closeCamera(mCameraProxy, true);
} else {
// if using API2 ensure API1 usage is also synced
mCameraAgent.closeCamera(mCameraProxy, syncClose);
}
checkAndOpenCamera(cameraManager, id, mCallbackHandler, this);
} else {
// The same camera, just do a reconnect.
Log.v(TAG, "reconnecting to use the existing camera");
mCameraProxy.reconnect(mCallbackHandler, this);
mCameraProxy = null;
}
mUsingNewApi = useNewApi;
}
有与CameraAgent相关的:
CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent;
我们在CameraAPP中可以看到很多类似的,通过useNewApi 这个boolean值判断使用哪个Agent,这其实就是我们前面说的api1与api2的兼容处理。
我们以打开CameraAPP,第一次请求openCamera的流程来跟踪,那么 mCameraProxy == null 是符合的,进入第一个if分支:checkAndOpenCamera
private static void checkAndOpenCamera(CameraAgent cameraManager,
final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) {
Log.i(TAG, "checkAndOpenCamera");
try {
CameraUtil.throwIfCameraDisabled();
cameraManager.openCamera(handler, cameraId, cb);
} catch (CameraDisabledException ex) {
handler.post(new Runnable() {
@Override
public void run() {
cb.onCameraDisabled(cameraId);
}
});
}
}
关键代码:cameraManager.openCamera(handler, cameraId, cb);
而cameraManager就是前面通过useNewApi的boolean值判断得来的api2的那支的CameraAgent。
还记得最前面的那张继承关系图吗?
现在就要从CameraController中的openCamera方法转到那张继承关系图中的类了。
因为是继承关系,我也不知道具体的openCamera方法是在继承关系中的哪个类实现的,我们就一个个找吧。
最终在CameraAgent中找到了openCamera的实现:
public void openCamera(final Handler handler, final int cameraId,
final CameraOpenCallback callback) {
Log.e(TAG,"openCamera "+android.util.Log.getStackTraceString(new Throwable()));
try {
getDispatchThread().runJob(new Runnable() {
@Override
public void run() {
synchronized (mOpenCloseSyncLock) {
isOpeningAndCloseNeedWait = true;
}
getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0,
CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
}
});
} catch (final RuntimeException ex) {
getCameraExceptionHandler().onDispatchThreadException(ex);
}
}
看来CameraAgent的openCamera也没做什么事情,就是通过handler–message机制转发了下CameraActions.OPEN_CAMERA消息。
再来追CameraActions.OPEN_CAMERA消息的处理
我们在AndoridCamera2AgentImpl中找到了
case CameraActions.OPEN_CAMERA:
{
CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
int cameraIndex = msg.arg1;
if (mCameraState.getState() > AndroidCamera2StateHolder.CAMERA_UNOPENED) {
openCallback.onDeviceOpenedAlready(cameraIndex,
generateHistoryString(cameraIndex));
break;
}
mOpenCallback = openCallback;
mCameraIndex = cameraIndex;
/*
* SPRD: Fix bug 591216 that add new feature 3d range finding, only support API2 currently @{
* Original Code
*
mCameraId = mCameraDevices.get(mCameraIndex);
*/
mCameraId = "" + mCameraIndex;
/* @} */
Log.i(TAG, String.format("Opening camera index %d (id %s) with camera2 API",
cameraIndex, mCameraId));
if (mCameraId == null) {
mOpenCallback.onCameraDisabled(msg.arg1);
break;
}
reconnect = false;
mCameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, this);
break;
}
mCameraManager.openCamera,追到了CameraManager了。至此APP端的openCamera流程已经完成了。
最后,我们将上述流程画成时序图来总结下: