本文以SnapdragonCamera为例,分析骁龙Camera的拍照流程,其实现与camera2大致相同。

首先将SnapdragonCamera源码倒入android studio, 具体操作,可查看如何用Android Studio调试Android源码一文。打开camera,点击拍照,ShutterButton类的performClick()方法会被调用。(如何定位点击拍照会调用ShutterButton类的performClick()方法,请参考使用Android Studio中的HierarchyViewer 及UI Automator Viewer定位当前UI界面的代码位置。)

    @Override    public boolean performClick() {        boolean result = super.performClick();        if (mListener != null && getVisibility() == View.VISIBLE) {            mListener.onShutterButtonClick();        }        return result;    }

按Ctrl+Alt+B 看onShutterButtonClick()方法是在哪里实现的。如下图,有四个地方调用,由于我们只分析拍照流程,只用查看PhotoModule即可。

在PhotoModule中,onShutterButtonClick()的实现如下

    @Override    public synchronized void onShutterButtonClick() {        if ((mCameraDevice == null)                || mPaused || mUI.collapseCameraControls()                || !mUI.mMenuInitialized                || (mCameraState == SWITCHING_CAMERA)                || (mCameraState == PREVIEW_STOPPED)                || (mCameraState == LONGSHOT)                || (null == mFocusManager)) return;                                .                                .                                .                                                       if (seconds > 0) {            String zsl = mPreferences.getString(CameraSettings.KEY_ZSL,                    mActivity.getString(R.string.pref_camera_zsl_default));            mUI.overrideSettings(CameraSettings.KEY_ZSL, zsl);            mUI.startCountDown(seconds, playSound);        } else {            mSnapshotOnIdle = false;            initiateSnap();//关键方法        }    }

为了便于查看,我们省略掉一些条件判断的代码,最终调用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 == null,SelfieThread中的                                                                        selfieThread.start();             //mFocusManager.doSnap()方法被调用            }        } else {            mFocusManager.doSnap();//关键方法        }    }

接着查看doSnap()方法

    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();//关键方法,没有聚焦时调用        }    }

我们分析第一种情况,第二种情况,涉及callShutterButtonFocus()的调用流程,这里暂不做分析,接着我们看PhotoModule中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                || mActivity.getMediaSaveService() == null                || mActivity.getMediaSaveService().isQueueFull()) {            return false;        }            ...            ... //省略相关参数设置,及条件判断代码            ...        if (mCameraState == LONGSHOT) {            mLongShotCaptureCountLimit = SystemProperties.getInt(                                    "persist.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 != CameraInfo.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;    }

其中关键代码为mCameraDevice.takePicture(),拍照后会回调JpegPictureCallback中的onPictureTaken()方法。将从底层返回的数据进行处理。

    @Override        public void onPictureTaken(byte [] jpegData, CameraProxy camera) {            mUI.stopSelfieFlash();            mUI.enableShutter(true);            if (mUI.isPreviewCoverVisible()) {                 // When take picture request is sent before starting preview, onPreviewFrame()                 // callback doesn't happen so removing preview cover here, instead.                 mUI.hidePreviewCover();            }            if (mInstantCaptureSnapShot == true) {                Log.v(TAG, "Instant capture picture taken!");                mInstantCaptureSnapShot = false;            }            if (mPaused) {                return;            }            if (mIsImageCaptureIntent) {                if (!mRefocus) {                    stopPreview();                }            } else if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {                mUI.showSwitcher();                mUI.setSwipingEnabled(true);            }            mReceivedSnapNum = mReceivedSnapNum + 1;            mJpegPictureCallbackTime = System.currentTimeMillis();            if(mSnapshotMode == CameraInfo.CAMERA_SUPPORT_MODE_ZSL) {                Log.v(TAG, "JpegPictureCallback : in zslmode");                mParameters = mCameraDevice.getParameters();                mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter");            }            Log.v(TAG, "JpegPictureCallback: Received = " + mReceivedSnapNum +                      "Burst count = " + mBurstSnapNum);            // If postview callback has arrived, the captured image is displayed            // in postview callback. If not, the captured image is displayed in            // raw picture callback.            if (mPostViewPictureCallbackTime != 0) {                mShutterToPictureDisplayedTime =                        mPostViewPictureCallbackTime - mShutterCallbackTime;                mPictureDisplayedToJpegCallbackTime =                        mJpegPictureCallbackTime - mPostViewPictureCallbackTime;            } else {                mShutterToPictureDisplayedTime =                        mRawPictureCallbackTime - mShutterCallbackTime;                mPictureDisplayedToJpegCallbackTime =                        mJpegPictureCallbackTime - mRawPictureCallbackTime;            }            Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "                    + mPictureDisplayedToJpegCallbackTime + "ms");            mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.            boolean needRestartPreview = !mIsImageCaptureIntent                    && !mPreviewRestartSupport                    && (mCameraState != LONGSHOT)                    && (mSnapshotMode != CameraInfo.CAMERA_SUPPORT_MODE_ZSL)                    && (mReceivedSnapNum == mBurstSnapNum);            if (needRestartPreview) {                setupPreview();                if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(                    mFocusManager.getFocusMode())) {                    mCameraDevice.cancelAutoFocus();                }            } else if ((mReceivedSnapNum == mBurstSnapNum)                        && (mCameraState != LONGSHOT)){                mFocusManager.resetTouchFocus();                if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(                        mFocusManager.getFocusMode())) {                    mCameraDevice.cancelAutoFocus();                }                mUI.resumeFaceDetection();                if (!mIsImageCaptureIntent) {                    setCameraState(IDLE);                }                startFaceDetection();            }            mLastPhotoTakenWithRefocus = mRefocus;            if (mRefocus) {                final String[] NAMES = { "00.jpg", "01.jpg", "02.jpg", "03.jpg",                    "04.jpg", "DepthMapImage.y", "AllFocusImage.jpg" };                try {                    FileOutputStream out = mActivity.openFileOutput(NAMES[mReceivedSnapNum - 1],                            Context.MODE_PRIVATE);                    out.write(jpegData, 0, jpegData.length);                    out.close();                } catch (Exception e) {                }            }            if (!mRefocus || (mRefocus && mReceivedSnapNum == 7)) {                ExifInterface exif = Exif.getExif(jpegData);                int orientation = Exif.getOrientation(exif);                if(mCameraId == CameraHolder.instance().getFrontCameraId()) {                    IconListPreference selfieMirrorPref = (IconListPreference) mPreferenceGroup                            .findPreference(CameraSettings.KEY_SELFIE_MIRROR);                    if (selfieMirrorPref != null && selfieMirrorPref.getValue() != null &&                            selfieMirrorPref.getValue().equalsIgnoreCase("enable")) {                        jpegData = flipJpeg(jpegData);                        exif = Exif.getExif(jpegData);  //将图片信息存入Exif中                        exif.addOrientationTag(orientation);                    }                }                if (!mIsImageCaptureIntent) {                    // Burst snapshot. Generate new image name.                    if (mReceivedSnapNum > 1) {                        mNamedImages.nameNewImage(mCaptureStartTime, mRefocus);                    }                    // Calculate the width and the height of the jpeg.                    Size s = mParameters.getPictureSize();                    int width, height;                    if ((mJpegRotation + orientation) % 180 == 0) {                        width = s.width;                        height = s.height;                    } else {                        width = s.height;                        height = s.width;                    }                    String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT);                    if (pictureFormat != null && !pictureFormat.equalsIgnoreCase(PIXEL_FORMAT_JPEG)) {                        // overwrite width and height if raw picture                        String pair = mParameters.get(KEY_QC_RAW_PICUTRE_SIZE);                        if (pair != null) {                            int pos = pair.indexOf('x');                            if (pos != -1) {                                width = Integer.parseInt(pair.substring(0, pos));                                height = Integer.parseInt(pair.substring(pos + 1));                            }                        }                    }                    NamedEntity name = mNamedImages.getNextNameEntity();                    String title = (name == null) ? null : name.title;                    long date = (name == null) ? -1 : name.date;                    // Handle debug mode outputs                    if (mDebugUri != null) {                        // If using a debug uri, save jpeg there.                        saveToDebugUri(jpegData);                        // Adjust the title of the debug image shown in mediastore.                        if (title != null) {                            title = DEBUG_IMAGE_PREFIX + title;                        }                     }                     if (title == null) {                         Log.e(TAG, "Unbalanced name/data pair");                     } else {                        if (date == -1) {                            date = mCaptureStartTime;                        }                        if (mHeading >= 0) {                            // heading direction has been updated by the sensor.                            ExifTag directionRefTag = exif.buildTag(                              ExifInterface.TAG_GPS_IMG_DIRECTION_REF,                              ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);                            ExifTag directionTag = exif.buildTag(                              ExifInterface.TAG_GPS_IMG_DIRECTION,                              new Rational(mHeading, 1));                            exif.setTag(directionRefTag);                            exif.setTag(directionTag);                        }                        String mPictureFormat = mParameters.get(KEY_PICTURE_FORMAT);                            mActivity.getMediaSaveService().addImage(                                    jpegData, title, date, mLocation, width, height,                                    orientation, exif, mOnMediaSavedListener,                                    mContentResolver, mPictureFormat);//将图片数据写入到文件和数据库中                            if (mRefocus && mReceivedSnapNum == 7) {                                 mUI.showRefocusToast(mRefocus);                            }                        }                        // Animate capture with real jpeg data instead of a preview frame.                        if (mCameraState != LONGSHOT) {                            Size pic_size = mParameters.getPictureSize();                            if ((pic_size.width <= 352) && (pic_size.height<= 288)) {                                mUI.setDownFactor(2); //Downsample by 2 for CIF & below                            } else {                                mUI.setDownFactor(4);                            }                            if (mAnimateCapture) {                                mUI.animateCapture(jpegData);                            }                        } else {                            // In long shot mode, we do not want to update the preview thumbnail                            // for each snapshot, instead, keep the last jpeg data and orientation,                            // use it to show the final one at the end of long shot.                            mLastJpegData = jpegData;                            mLastJpegOrientation = orientation;                        }                    } else {                        stopPreview();                        mJpegImageData = jpegData;                        if (!mQuickCapture) {                            mUI.showCapturedImageForReview(jpegData, orientation, false);                        } else {                            onCaptureDone();                        }                    }                    if(!mLongshotActive) {                        mActivity.updateStorageSpaceAndHint(                                new CameraActivity.OnStorageUpdateDoneListener() {                            @Override                            public void onStorageUpdateDone(long storageSpace) {                                mUI.updateRemainingPhotos(--mRemainingPhotos);                            }                        });                    } else {                        mUI.updateRemainingPhotos(--mRemainingPhotos);                    }                    long now = System.currentTimeMillis();                    mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;                    Log.v(TAG, "mJpegCallbackFinishTime = "                            + mJpegCallbackFinishTime + "ms");                    if (mReceivedSnapNum == mBurstSnapNum) {                        mJpegPictureCallbackTime = 0;                    }                    if (mHiston && (mSnapshotMode ==CameraInfo.CAMERA_SUPPORT_MODE_ZSL)) {                        mActivity.runOnUiThread(new Runnable() {                        public void run() {                            if (mGraphView != null) {                                mGraphView.setVisibility(View.VISIBLE);                                mGraphView.PreviewChanged();                            }                        }                    });                }                if (mSnapshotMode == CameraInfo.CAMERA_SUPPORT_MODE_ZSL &&                        mCameraState != LONGSHOT &&                        mReceivedSnapNum == mBurstSnapNum &&                        !mIsImageCaptureIntent) {                    cancelAutoFocus();                }            }        }

其中, mActivity.getMediaSaveService().addImage();方法将返回的图片数据存入文件和数据库中。MediaSaveService中的addImage()方法如下

    public void addImage(final byte[] data, String title, long date, Location loc,            int width, int height, int orientation, ExifInterface exif,            OnMediaSavedListener l, ContentResolver resolver, String pictureFormat) {        if (isQueueFull()) {            Log.e(TAG, "Cannot add image when the queue is full");            return;        }        ImageSaveTask t = new ImageSaveTask(data, title, date,                (loc == null) ? null : new Location(loc),                width, height, orientation, exif, resolver, l, pictureFormat);        mMemoryUse += data.length;        if (isQueueFull()) {            onQueueFull();        }        t.execute();    }

最后通过ImageSaveTask异步任务,实现图片的存储,接着看ImageSaveTask中的doInBackground()方法

@Overrideprotected Uri doInBackground(Void... v) {    if (width == 0 || height == 0) {        // Decode bounds        BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeByteArray(data, 0, data.length, options);        width = options.outWidth;        height = options.outHeight;    }    return Storage.addImage(            resolver, title, date, loc, orientation, exif, data, width, height, pictureFormat);}

最后调用Storage.addImage()方法

   // Save the image with a given mimeType and add it the MediaStore.    public static Uri addImage(ContentResolver resolver, String title, long date,            Location location, int orientation, ExifInterface exif, byte[] jpeg, int width,            int height, String mimeType) {        String path = generateFilepath(title, mimeType);        int size = writeFile(path, jpeg, exif, mimeType); //存文件        // Try to get the real image size after add exif.        File f = new File(path);        if (f.exists() && f.isFile()) {            size = (int) f.length();        }        return addImage(resolver, title, date, location, orientation,                size, path, width, height, mimeType);//存数据库    }

存完之后调用ImageSaveTask中的onPostExecute()方法,通知图片已经存完。

    @Override        protected void onPostExecute(Uri uri) {            if (listener != null) listener.onMediaSaved(uri);            boolean previouslyFull = isQueueFull();            mMemoryUse -= data.length;            if (isQueueFull() != previouslyFull) onQueueAvailable();        }

以上是对拍照流程的简单分析,只是大致做了梳理,有很多细节为涉及,以后再慢慢讨论。
 

更多相关文章

  1. Android(安卓)PackageManagerService(二)下载安装详解
  2. ListView的item高度调整
  3. Android(安卓)API Demo实例解析
  4. WakeLock使用方法示例代码
  5. 全志A40i Android7永不休眠及不锁屏的修改方法
  6. android sensor framework
  7. android 运行时生成dex文件,并装载调用
  8. android面试题总结加强再加强版(三)
  9. Android4.4 Camera Gallery 分离

随机推荐

  1. 安卓开源框架集合
  2. 安卓布局应用学习代码附带效果图
  3. WebRTC-M68-官方编译文档-Android'
  4. Understanding Android(安卓)Custom Attr
  5. 使用TypefaceSpan
  6. Android(安卓)Volley
  7. 24 Android(安卓)自定义对话框
  8. AndroidStudio编译FBreaderJ
  9. [Android][工具类]AppUtils
  10. Android(安卓)版本速查表