Android9.0 Camera App代码跟踪之Camrea拍照

接上篇博文https://blog.csdn.net/weixin_38160277/article/details/102969875

上次讲到Camrea预览,这次要讲Camrea的拍照

大家还记得第一篇博客讲App启动的时候讲到的CameraActivity里面的setModuleFromIndex()函数吗,这个就是视频,拍照,全景等模块切换的函数,该函数通过moduleIndex来切换不同的模块,看代码:

 /**
     * Sets the mCurrentModuleIndex, creates a new module instance for the given
     * index an sets it as mCurrentModule.
     */
    private void setModuleFromIndex(int moduleIndex) {
        mCameraPhotoModuleRootView.setVisibility(View.GONE);
        mCameraVideoModuleRootView.setVisibility(View.GONE);
        mCameraPanoModuleRootView.setVisibility(View.GONE);
        mCameraCaptureModuleRootView.setVisibility(View.GONE);
        mCurrentModuleIndex = moduleIndex;
        switch (moduleIndex) {
            case ModuleSwitcher.VIDEO_MODULE_INDEX:
                if(mVideoModule == null) {
                    mVideoModule = new VideoModule();
                    mVideoModule.init(this, mCameraVideoModuleRootView);
                } else {
                    mVideoModule.reinit();
                }
                mCurrentModule = mVideoModule;
                mCameraVideoModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.PHOTO_MODULE_INDEX:
                if(mPhotoModule == null) {
                    mPhotoModule = new PhotoModule();
                    mPhotoModule.init(this, mCameraPhotoModuleRootView);
                } else {
                    mPhotoModule.reinit();
                }
                mCurrentModule = mPhotoModule;
                mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX:
                if(mPanoModule == null) {
                    mPanoModule = new WideAnglePanoramaModule();
                    mPanoModule.init(this, mCameraPanoModuleRootView);
                }
                mCurrentModule = mPanoModule;
                mCameraPanoModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.CAPTURE_MODULE_INDEX:
                if(mCaptureModule == null) {
                    mCaptureModule = new CaptureModule();
                    mCaptureModule.init(this, mCameraCaptureModuleRootView);
                } else {
                    mCaptureModule.reinit();
                }
                mCurrentModule = mCaptureModule;
                mCameraCaptureModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.PANOCAPTURE_MODULE_INDEX:
                final Activity activity = this;
                if(!PanoCaptureProcessView.isSupportedStatic()) {
                    this.runOnUiThread(new Runnable() {
                        public void run() {
                            RotateTextToast.makeText(activity, "Panocapture library is missing", Toast.LENGTH_SHORT).show();
                        }
                    });
                    mCurrentModuleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
                    //Let it fall through to photo module
                } else {
                    if (mPano2Module == null) {
                        mPano2Module = new PanoCaptureModule();
                        mPano2Module.init(this, mCameraPanoModuleRootView);
                    }
                    mCurrentModule = mPano2Module;
                    mCameraPanoModuleRootView.setVisibility(View.VISIBLE);
                    break;
                }
            case ModuleSwitcher.LIGHTCYCLE_MODULE_INDEX: //Unused module for now
            case ModuleSwitcher.GCAM_MODULE_INDEX:  //Unused module for now
            default:
                // Fall back to photo mode.
                if(mPhotoModule == null) {
                    mPhotoModule = new PhotoModule();
                    mPhotoModule.init(this, mCameraPhotoModuleRootView);
                } else {
                    mPhotoModule.reinit();
                }
                mCurrentModule = mPhotoModule;
                mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                break;
        }
    }

这里默认是进入到PhotoModule的也就是拍照模块,我们再来看看拍照的代码在哪里?打开PhotoModule.java,我们直接找一下有没有capture(),果然有,look:

 @Override
    public boolean capture() {
        // If we are already in the middle of taking a snapshot or the image save request
        // is full then ignore.
        if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
                || mCameraState == SWITCHING_CAMERA
                || mCameraState == PREVIEW_STOPPED
                || mActivity.getMediaSaveService() == null
                || mActivity.getMediaSaveService().isQueueFull()) {
            return false;
        }
        mCaptureStartTime = System.currentTimeMillis();
        mPostViewPictureCallbackTime = 0;
        mJpegImageData = null;

        final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
        if(mHiston) {
            if (mSnapshotMode != CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL) {
                mHiston = false;
                mCameraDevice.setHistogramMode(null);
            }
            mActivity.runOnUiThread(new Runnable() {
                public void run() {
                    if(mGraphView != null)
                        mGraphView.setVisibility(View.INVISIBLE);
                }
            });
        }

        if (animateBefore) {
            animateAfterShutter();
        }

        if (mCameraState == LONGSHOT) {
            mCameraDevice.setLongshot(true);
        }

        // Set rotation and gps data.
        int orientation = mOrientation;
        mJpegRotation = CameraUtil.getJpegRotationForCamera1(mCameraId, orientation);
        String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT);
        Location loc = getLocationAccordPictureFormat(pictureFormat);
        //这里是线程安全的,同一时间Camera只允许一个进程访问
        synchronized (mCameraDevice) {
            mParameters.setRotation(mJpegRotation);
            CameraUtil.setGpsParameters(mParameters, loc);

            if (mRefocus) {
                mParameters.set(CameraSettings.KEY_QC_LEGACY_BURST,
                        CameraSettings.KEY_QC_RE_FOCUS_COUNT);
            } else {
                mParameters.remove(CameraSettings.KEY_QC_LEGACY_BURST);
            }

            // Unlock AE&AWB, if they continue
            // to be locked during snapshot, then
            // side effects could be triggered w.r.t.
            // flash.
            mFocusManager.setAeAwbLock(false);
            setAutoExposureLockIfSupported();
            setAutoWhiteBalanceLockIfSupported();
            //设置Camrea参数,什么曝光量啊,色彩值啊,锐度啊都用Camrea的SetParameters里面设置,你只需要把这些参数封装好传给Camera就行
            mCameraDevice.setParameters(mParameters);
            mParameters = mCameraDevice.getParameters();
        }

        try {
            mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter");
        }catch (NumberFormatException ex){
            mBurstSnapNum = 1;
        }
        mReceivedSnapNum = 0;
        mPreviewRestartSupport = PersistUtil.isPreviewRestartEnabled();
        mPreviewRestartSupport &= CameraSettings.isInternalPreviewSupported(
                mParameters);
        mPreviewRestartSupport &= (mBurstSnapNum == 1);
        // Restart is needed  if HDR is enabled
        mPreviewRestartSupport &= !CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
        mPreviewRestartSupport &= PIXEL_FORMAT_JPEG.equalsIgnoreCase(
                pictureFormat);

        // We don't want user to press the button again while taking a
        // multi-second HDR photo. For longshot, no need to disable.
        if (mCameraState != LONGSHOT) {
            mUI.enableShutter(false);
        }

        if (!isShutterSoundOn()) {
            mCameraDevice.enableShutterSound(false);
        } else {
            mCameraDevice.enableShutterSound(!mRefocus);
        }

        mSaveBokehXmp = mIsBokehMode && mDepthSuccess;

        if (mCameraState == LONGSHOT) {
            mLongShotCaptureCountLimit = SystemProperties.getInt(
                                    "persist.sys.camera.longshot.shotnum", 0);
            mLongShotCaptureCount = 1;
            if(mLongshotSave) {
//调用Camera的takePicture方法传入回调函数,拍照完成会回调这些函数并传递图片信息过来给App存储和编辑
                mCameraDevice.takePicture(mHandler,
                        new LongshotShutterCallback(),
                        mRawPictureCallback, mPostViewPictureCallback,
                        new LongshotPictureCallback(loc));
            } else {
                mCameraDevice.takePicture(mHandler,
                        new LongshotShutterCallback(),
                        mRawPictureCallback, mPostViewPictureCallback,
                        new JpegPictureCallback(loc));
            }
        } else {
            mCameraDevice.takePicture(mHandler,
                    new ShutterCallback(!animateBefore),
                    mRawPictureCallback, mPostViewPictureCallback,
                    new JpegPictureCallback(loc));
            setCameraState(SNAPSHOT_IN_PROGRESS);
        }

        mNamedImages.nameNewImage(mCaptureStartTime, mRefocus);

        if (mSnapshotMode != CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL) {
            mFaceDetectionStarted = false;
        }
        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
                UsageStatistics.ACTION_CAPTURE_DONE, "Photo", 0,
                UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"));
        return true;
    }

代码跟踪到这里,你也许会有疑问,这个capture()函数是何时调用执行的?这当然是在按下快门键的时候调用的。接下来就跟踪一下快门键按钮,找到ShutterButton.java

/**
 * A button designed to be used for the on-screen shutter button.
 * It's currently an {@code ImageView} that can call a delegate when the
 * pressed state changes.
 */
public class ShutterButton extends RotateImageView {

    private class LongClickListener implements View.OnLongClickListener {
         public boolean onLongClick(View v) {
            ...
             return false;
         }
    }

    ....
    public interface OnShutterButtonListener {
        /**
         * Called when a ShutterButton has been pressed.
         *
         * @param pressed The ShutterButton that was pressed.
         */
        void onShutterButtonFocus(boolean pressed);
        void onShutterButtonClick();
        void onShutterButtonLongClick();
    }
...
private OnShutterButtonListener mListener;
....
public void setOnShutterButtonListener(OnShutterButtonListener listener) {
        mListener = listener;
        setOnLongClickListener(mLongClick);
    }
@Override
    public boolean performClick() {
        boolean result = super.performClick();
        if (mListener != null && getVisibility() == View.VISIBLE) {
            mListener.onShutterButtonClick();
        }
        return result;
    }
    ....
}

可看到它是一个自定义的ImageView来的里面有个OnShutterButtonListener 接口,这个接口是用来规定快门键点击事件的。现在来看下谁实现了这个接口

public class PhotoModule
        implements CameraModule,
        PhotoController,
        FocusOverlayManager.Listener,
        CameraPreference.OnPreferenceChangedListener,
        ShutterButton.OnShutterButtonListener,
        MediaSaveService.Listener,
        OnCountDownFinishedListener,
        LocationManager.Listener,
        SensorEventListener, MakeupLevelListener {

可见就是PhotoModule本身实现了这个接口,那接下来就看下它的实现方法

//当快门按钮点击的时候会调用到这个函数 
@Override
    public synchronized void onShutterButtonClick() {
        .....
            initiateSnap();
        ....
    }

private void initiateSnap()
    {
        if(mPreferences.getString(CameraSettings.KEY_SELFIE_FLASH,
                mActivity.getString(R.string.pref_selfie_flash_default))
                .equalsIgnoreCase("on") &&
                mCameraId == CameraHolder.instance().getFrontCameraId()) {
            mUI.startSelfieFlash();
            if(selfieThread == null) {
                selfieThread = new SelfieThread();
                selfieThread.start();
            }
        } else {
            mFocusManager.doSnap();
        }
    }
//FocusOverlayManager类
 public void doSnap() {
        if (!mInitialized) return;

        // If the user has half-pressed the shutter and focus is completed, we
        // can take the photo right away. If the focus mode is infinity, we can
        // also take the photo.
        if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
            capture();
        } else if (mState == STATE_FOCUSING) {
            // Half pressing the shutter (i.e. the focus button event) will
            // already have requested AF for us, so just request capture on
            // focus here.
            mState = STATE_FOCUSING_SNAP_ON_FINISH;
        } else if (mState == STATE_IDLE) {
            // We didn't do focus. This can happen if the user press focus key
            // while the snapshot is still in progress. The user probably wants
            // the next snapshot as soon as possible, so we just do a snapshot
            // without focusing again.
            capture();
        }
    }

private void capture() {
        if (mListener.capture()) {
            mState = STATE_IDLE;
            mHandler.removeMessages(RESET_TOUCH_FOCUS);
        }
    }

代码跟踪到这里mListener.capture()这行代码是干嘛的捏,请看mListener是什么玩意:

 public interface Listener {
        public void autoFocus();
        public void cancelAutoFocus();
        public boolean capture();
        public void startFaceDetection();
        public void stopFaceDetection();
        public void setFocusParameters();
    }

它是一个接口,定义在FocusOverlayManager类里面,那么谁实现了这个接口呢

look,又是PhotoModule

public class PhotoModule
        implements CameraModule,
        PhotoController,
        FocusOverlayManager.Listener,
        CameraPreference.OnPreferenceChangedListener,
        ShutterButton.OnShutterButtonListener,
        MediaSaveService.Listener,
        OnCountDownFinishedListener,
        LocationManager.Listener,
        SensorEventListener, MakeupLevelListener {

看下它的实现方法capture()

@Override
    public boolean capture() {
        // If we are already in the middle of taking a snapshot or the image save request
        // is full then ignore.
        if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
                || mCameraState == SWITCHING_CAMERA
                || mCameraState == PREVIEW_STOPPED
                || mActivity.getMediaSaveService() == null
                || mActivity.getMediaSaveService().isQueueFull()) {
            return false;
        }
        mCaptureStartTime = System.currentTimeMillis();
        mPostViewPictureCallbackTime = 0;
        mJpegImageData = null;

        final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
        if(mHiston) {
            if (mSnapshotMode != CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL) {
                mHiston = false;
                mCameraDevice.setHistogramMode(null);
            }
            mActivity.runOnUiThread(new Runnable() {
                public void run() {
                    if(mGraphView != null)
                        mGraphView.setVisibility(View.INVISIBLE);
                }
            });
        }

        if (animateBefore) {
            animateAfterShutter();
        }

        if (mCameraState == LONGSHOT) {
            mCameraDevice.setLongshot(true);
        }

        // Set rotation and gps data.
        int orientation = mOrientation;
        mJpegRotation = CameraUtil.getJpegRotationForCamera1(mCameraId, orientation);
        String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT);
        Location loc = getLocationAccordPictureFormat(pictureFormat);

        synchronized (mCameraDevice) {
            mParameters.setRotation(mJpegRotation);
            CameraUtil.setGpsParameters(mParameters, loc);

            if (mRefocus) {
                mParameters.set(CameraSettings.KEY_QC_LEGACY_BURST,
                        CameraSettings.KEY_QC_RE_FOCUS_COUNT);
            } else {
                mParameters.remove(CameraSettings.KEY_QC_LEGACY_BURST);
            }

            // Unlock AE&AWB, if they continue
            // to be locked during snapshot, then
            // side effects could be triggered w.r.t.
            // flash.
            mFocusManager.setAeAwbLock(false);
            setAutoExposureLockIfSupported();
            setAutoWhiteBalanceLockIfSupported();

            mCameraDevice.setParameters(mParameters);
            mParameters = mCameraDevice.getParameters();
        }

        try {
            mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter");
        }catch (NumberFormatException ex){
            mBurstSnapNum = 1;
        }
        mReceivedSnapNum = 0;
        mPreviewRestartSupport = PersistUtil.isPreviewRestartEnabled();
        mPreviewRestartSupport &= CameraSettings.isInternalPreviewSupported(
                mParameters);
        mPreviewRestartSupport &= (mBurstSnapNum == 1);
        // Restart is needed  if HDR is enabled
        mPreviewRestartSupport &= !CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
        mPreviewRestartSupport &= PIXEL_FORMAT_JPEG.equalsIgnoreCase(
                pictureFormat);

        // We don't want user to press the button again while taking a
        // multi-second HDR photo. For longshot, no need to disable.
        if (mCameraState != LONGSHOT) {
            mUI.enableShutter(false);
        }

        if (!isShutterSoundOn()) {
            mCameraDevice.enableShutterSound(false);
        } else {
            mCameraDevice.enableShutterSound(!mRefocus);
        }

        mSaveBokehXmp = mIsBokehMode && mDepthSuccess;

        if (mCameraState == LONGSHOT) {
            mLongShotCaptureCountLimit = SystemProperties.getInt(
                                    "persist.sys.camera.longshot.shotnum", 0);
            mLongShotCaptureCount = 1;
            if(mLongshotSave) {
                mCameraDevice.takePicture(mHandler,
                        new LongshotShutterCallback(),
                        mRawPictureCallback, mPostViewPictureCallback,
                        new LongshotPictureCallback(loc));
            } else {
                mCameraDevice.takePicture(mHandler,
                        new LongshotShutterCallback(),
                        mRawPictureCallback, mPostViewPictureCallback,
                        new JpegPictureCallback(loc));
            }
        } else {
            mCameraDevice.takePicture(mHandler,
                    new ShutterCallback(!animateBefore),
                    mRawPictureCallback, mPostViewPictureCallback,
                    new JpegPictureCallback(loc));
            setCameraState(SNAPSHOT_IN_PROGRESS);
        }

        mNamedImages.nameNewImage(mCaptureStartTime, mRefocus);

        if (mSnapshotMode != CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL) {
            mFaceDetectionStarted = false;
        }
        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
                UsageStatistics.ACTION_CAPTURE_DONE, "Photo", 0,
                UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"));
        return true;
    }

是不是似曾相识,哈哈,对又绕回来了,这个就是开始的时候讲的那个capture(),有点晕了吧,现在来理一下:

当快门按钮ShutterButton按下时会回调OnShutterButtonListener的onShutterButtonClick()方法,而实现该接口的PhotoModule类的onShutterButtonClick()会被执行,接着又调用到了FocusOverlayManager的doSnap() ,而doSnap() 又调用到了FocusOverlayManager.Listener的capture()函数,PhotoModule正是FocusOverlayManager.Listener的实现类,所以最终PhotoModule的capture()被执行,调用系统层api实现拍照功能。

讲到这里可能大家会郁闷,怎么那么多的接口,就不能直白一点吗?说实话我也郁闷,但是用接口写的好处就是结构清晰,写大型项目就需要这样的。当然你也可以直白一点写,但是项目大了就会出问题的。

好了,本博到此结束,下篇我还会接着讲Camera的其他功能流程,敬请期待。

谢谢大家,祝你工作顺利,生活愉快

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值