各位早上好,這次給大家帶來的是Android9.0 Camera App的代碼閱讀,即是工作需要也是個人的筆記,幫助大家學習。
Android9.0 Camera App源代碼的位置(/android/packages/apps/SnapdragonCamera),其他的Camera App在Android9.0中已經棄用了,我把源碼導入到Android studio中讓大家看看它的代碼結構,如圖:
這裡着重要講的是src裡面的Java代碼,導入到Android studio會有很多錯誤,部落客一開始天真地以為可以在Android studio裡面編譯,但發現錯誤太多不現實,還是在源碼裡面編譯吧,android studio就作為代碼閱讀工具用,大家也可以用source insight閱讀,至于部落客,兩個都用。
好了,廢話不多說我們開始閱讀代碼吧。
第一個問題:Carmera App是怎麼啟動的?
有Android App開發基礎的同學就知道,Android App啟動的時候第一個啟動的就是Application.那麼這個類在哪裡,我們可以找到AndroidManifest.xml檔案來看
看在這裡,我們找到com.android.camera.app.CameraApp先
look!看到了吧。好現在來看看它做了哪些工作,一般來講這個類肯定是做一些初始化的工作。我們看他的源碼:
@Override
public void onCreate() {
super.onCreate();
ActivityManager actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
actManager.getMemoryInfo(memInfo);
mMaxSystemMemory = memInfo.totalMem;
if(mMaxSystemMemory <= LOW_MEMORY_DEVICE_THRESHOLD) {//檢查裝置的存儲空間是否充足
mIsLowMemoryDevice = true;
}
SettingsManager.createInstance(this);//執行個體化一個Camera設定管理類
UsageStatistics.initialize(this);
CameraUtil.initialize(this);//Camrera工具類初始化
SDCard.initialize(this);//SD卡初始化
}
這裡面Camre是存儲空間需求比較大的應用,是以有個最低記憶體要求。
接下來要看看App啟動的第一個界面,我們再看一下AndroidManifest.xml裡面的代碼:
<activity
android:name="com.android.camera.CameraActivity"
android:clearTaskOnLaunch="true"
android:configChanges="orientation|screenSize|keyboardHidden"
android:icon="@mipmap/ic_launcher_camera"
android:label="@string/snapcam_app_name"
android:launchMode="singleTask"
android:logo="@mipmap/ic_launcher_gallery"
android:screenOrientation="portrait"
android:taskAffinity="com.android.camera.CameraActivity"
android:theme="@style/Theme.Camera"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
android:resizeableActivity="true"
android:visibleToInstantApps="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />//這個Activity作為主界面啟動
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="com.android.keyguard.layout"
android:resource="@layout/keyguard_widget" />
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
</activity>
我們找到com.android.camera.CameraActivity類,這個類代碼比較多我就隻摘取其中的片段來講。
首先我們找到Actively的生命周期方法onCreate()這是Activity啟動是首先要執行的方法,這裡面做了什麼工作呢?我們來看下源碼:
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
// Check if this is in the secure camera mode.
Intent intent = getIntent();
String action = intent.getAction();//Activity啟動會攜帶相應的action,根據action可以判斷Activity是從哪裡啟動的
if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
|| ACTION_IMAGE_CAPTURE_SECURE.equals(action)
|| intent.getComponent().getClassName().equals(GESTURE_CAMERA_NAME)) {
mSecureCamera = true;
} else {
mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
}
if (mSecureCamera) {//什麼是secure camera mode?就是你在鎖屏狀态下進入的Camera,這種狀态是的Camera功能是有限制的
// Change the window flags so that secure camera can show when locked
Window win = getWindow();
WindowManager.LayoutParams params = win.getAttributes();
params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
if (intent.getComponent().getClassName().equals(GESTURE_CAMERA_NAME)) {
params.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
PowerManager pm = ((PowerManager) getSystemService(POWER_SERVICE));
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.acquire();
Log.d(TAG, "acquire wake lock");
}
win.setAttributes(params);
}
//這裡檢查App是否有相關的權限
if (mSecureCamera && !hasCriticalPermissions()) {
return;
}
if (isStartRequsetPermission()) {
Log.v(TAG, "onCreate: Missing critical permissions.");
finish();
return;
}
boolean camera_api_1_support = true;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
//檢查裝置的api是否支援
if (!sharedPreferences.contains(CAMERA_API_1_SUPPORT)) {
camera_api_1_support = cameraAPICheck();
} else {
camera_api_1_support = sharedPreferences.getBoolean(CAMERA_API_1_SUPPORT,true);
}
mCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, null, null, null);
GcamHelper.init(getContentResolver());
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
LayoutInflater inflater = getLayoutInflater();
//擷取View
View rootLayout = inflater.inflate(R.layout.camera, null, false);
mCameraRootFrame = (FrameLayout)rootLayout.findViewById(R.id.camera_root_frame);
mCameraPhotoModuleRootView = rootLayout.findViewById(R.id.camera_photo_root);
mCameraVideoModuleRootView = rootLayout.findViewById(R.id.camera_video_root);
mCameraPanoModuleRootView = rootLayout.findViewById(R.id.camera_pano_root);
mCameraCaptureModuleRootView = rootLayout.findViewById(R.id.camera_capture_root);
//界面根據moduleIndex來決定加載那個module
int moduleIndex = -1;
if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
|| MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
moduleIndex = ModuleSwitcher.VIDEO_MODULE_INDEX;
} else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
|| MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
.getAction())) {
moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1)
== ModuleSwitcher.GCAM_MODULE_INDEX && GcamHelper.hasGcamCapture()) {
moduleIndex = ModuleSwitcher.GCAM_MODULE_INDEX;
}
} else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
|| MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
} else {
// If the activity has not been started using an explicit intent,
// read the module index from the last time the user changed modes
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
moduleIndex = prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1);
if ((moduleIndex == ModuleSwitcher.GCAM_MODULE_INDEX &&
!GcamHelper.hasGcamCapture()) || moduleIndex < 0) {
moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
}
}
boolean cam2on = PersistUtil.getCamera2Mode();
if (!cam2on && !camera_api_1_support)
cam2on = true;
CameraHolder.setCamera2Mode(this, cam2on);
if (cam2on && (moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX ||
moduleIndex == ModuleSwitcher.VIDEO_MODULE_INDEX))
moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX;
mOrientationListener = new MyOrientationEventListener(this);
setContentView(R.layout.camera_filmstrip);
mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view);
setModuleFromIndex(moduleIndex);//根據moduleIndex切換到相應的子產品
mActionBar = getActionBar();
mActionBar.addOnMenuVisibilityListener(this);
if (ApiHelper.HAS_ROTATION_ANIMATION) {
setRotationAnimation();
}
mMainHandler = new MainHandler(getMainLooper());
mAboveFilmstripControlLayout =
(FrameLayout) findViewById(R.id.camera_above_filmstrip_layout);
mAboveFilmstripControlLayout.setFitsSystemWindows(true);
mPanoramaManager = AppManagerFactory.getInstance(this)
.getPanoramaStitchingManager();
mPlaceholderManager = AppManagerFactory.getInstance(this)
.getGcamProcessingManager();
mPanoramaManager.addTaskListener(mStitchingListener);
mPlaceholderManager.addTaskListener(mPlaceholderListener);
mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel);
mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar);
mCameraPreviewData = new CameraPreviewData(rootLayout,
FilmStripView.ImageData.SIZE_FULL,
FilmStripView.ImageData.SIZE_FULL);
// Put a CameraPreviewData at the first position.
mWrappedDataAdapter = new FixedFirstDataAdapter(
new CameraDataAdapter(new ColorDrawable(
getResources().getColor(R.color.photo_placeholder))),
mCameraPreviewData);
mFilmStripView.setViewGap(
getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
mPanoramaViewHelper = new PanoramaViewHelper(this);
mPanoramaViewHelper.onCreate();
mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper);
// Set up the camera preview first so the preview shows up ASAP.
mFilmStripView.setListener(mFilmStripListener);
if (!mSecureCamera) {
mDataAdapter = mWrappedDataAdapter;
mFilmStripView.setDataAdapter(mDataAdapter);
if (!isCaptureIntent()) {
mDataAdapter.requestLoad(getContentResolver());
mDataRequested = true;
}
} else {
// Put a lock placeholder as the last image by setting its date to
// 0.
ImageView v = (ImageView) getLayoutInflater().inflate(
R.layout.secure_album_placeholder, null);
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
UsageStatistics.ACTION_GALLERY, null);
startActivity(IntentHelper.getGalleryIntent(CameraActivity.this));
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Failed to launch gallery activity, closing");
}
finish();
}
});
mDataAdapter = new FixedLastDataAdapter(
mWrappedDataAdapter,
new SimpleViewData(
v,
v.getDrawable().getIntrinsicWidth(),
v.getDrawable().getIntrinsicHeight(),
0, 0));
// Flush out all the original data.
mDataAdapter.flush();
mFilmStripView.setDataAdapter(mDataAdapter);
}
setupNfcBeamPush();
mLocalImagesObserver = new LocalMediaObserver();
mLocalVideosObserver = new LocalMediaObserver();
getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
mLocalImagesObserver);
getContentResolver().registerContentObserver(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
mLocalVideosObserver);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
mDeveloperMenuEnabled = prefs.getBoolean(CameraSettings.KEY_DEVELOPER_MENU, false);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
int lower = Math.min(width, height);
int offset = lower * 7 / 100;
SETTING_LIST_WIDTH_1 = lower / 2 + offset;
SETTING_LIST_WIDTH_2 = lower / 2 - offset;
registerSDcardMountedReceiver();
mAutoTestEnabled = PersistUtil.isAutoTestEnabled();
if (mAutoTestEnabled) {
registerAutoTestReceiver();
}
}
我們重點看下setModuleFromIndex(int moduleIndex) 方法,這個方法會根據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是什麼玩意:
public class PhotoModule
implements CameraModule,
PhotoController,
FocusOverlayManager.Listener,
CameraPreference.OnPreferenceChangedListener,
ShutterButton.OnShutterButtonListener,
MediaSaveService.Listener,
OnCountDownFinishedListener,
LocationManager.Listener,
SensorEventListener, MakeupLevelListener {
public interface CameraModule {
public void init(CameraActivity activity, View frame);
public void onPreviewFocusChanged(boolean previewFocused);
public void onPauseBeforeSuper();
public void onPauseAfterSuper();
public void onResumeBeforeSuper();
public void onResumeAfterSuper();
public void onConfigurationChanged(Configuration config);
public void onStop();
public void onDestroy();
public void installIntentFilter();
public void onActivityResult(int requestCode, int resultCode, Intent data);
public boolean onBackPressed();
public boolean onKeyDown(int keyCode, KeyEvent event);
public boolean onKeyUp(int keyCode, KeyEvent event);
public void onSingleTapUp(View view, int x, int y);
public void onPreviewTextureCopied();
public void onCaptureTextureCopied();
public void onUserInteraction();
public boolean updateStorageHintOnResume();
public void onOrientationChanged(int orientation);
public void onShowSwitcherPopup();
public void onMediaSaveServiceConnected(MediaSaveService s);
public boolean arePreviewControlsVisible();
public void resizeForPreviewAspectRatio();
public void onSwitchSavePath();
public void waitingLocationPermissionResult(boolean waiting);
public void enableRecordingLocation(boolean enable);
public void setPreferenceForTest(String key, String value);
}
它實作了CameraModule 接口,這個接口定義了Camera從建立到釋放的過程,我們來看下init()方法:
@Override
public void init(CameraActivity activity, View parent) {
...
if (mOpenCameraThread == null) {
mOpenCameraThread = new OpenCameraThread();//啟動一個線程
mOpenCameraThread.start();
}
...
}
private class OpenCameraThread extends Thread {
@Override
public void run() {
openCamera();//打開Camera
startPreview();//開啟預覽
}
}
private void openCamera() {
...
//建立一個Camera執行個體
mCameraDevice = CameraUtil.openCamera(
mActivity, mCameraId, mHandler,
mActivity.getCameraOpenErrorCallback());
...
}
代碼到了CameraUtil類裡面,我們看下它搞啥玩意:
public static CameraManager.CameraProxy openCamera(
Activity activity, final int cameraId,
Handler handler, final CameraManager.CameraOpenErrorCallback cb) {
try {
throwIfCameraDisabled(activity);
return CameraHolder.instance().open(handler, cameraId, cb);
} catch (CameraDisabledException ex) {
handler.post(new Runnable() {
@Override
public void run() {
cb.onCameraDisabled(cameraId);
}
});
}
return null;
}
//代碼到CameraHolder類的open方法
public synchronized CameraProxy open(
Handler handler, int cameraId,
CameraManager.CameraOpenErrorCallback cb) {
...
mCameraDevice = CameraManagerFactory
.getAndroidCameraManager().cameraOpen(handler, cameraId, cb);
...
return mCameraDevice;
}
//CameraManagerFactory類
public class CameraManagerFactory {
private static AndroidCameraManagerImpl sAndroidCameraManager;
/**
* Returns the android camera implementation of {@link CameraManager}.
*
* @return The {@link CameraManager} to control the camera device.
*/
public static synchronized CameraManager getAndroidCameraManager() {
if (sAndroidCameraManager == null) {
sAndroidCameraManager = new AndroidCameraManagerImpl();
}
return sAndroidCameraManager;
}
}
//AndroidCameraManagerImpl 類的cameraOpen
@Override
public CameraManager.CameraProxy cameraOpen(
Handler handler, int cameraId, CameraOpenErrorCallback callback) {
mCameraHandler.errorCbInstance = CameraOpenErrorCallbackForward
.getNewInstance(handler, callback);
//在這裡面使用mCameraHandler發送OPEN_CAMERA消息執行個體化一個Camera對象
mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0, mCameraHandler.errorCbInstance)
.sendToTarget();
mCameraHandler.waitDone();
if (mCamera != null) {
//将AndroidCameraProxyImpl代理類對象傳回給調用者,調用者對象将通過它來操作Camera執行個體
return new AndroidCameraProxyImpl();
} else {
return null;
}
}
//CameraHandler類,它是AndroidCameraManagerImpl 的内部類
@Override
public void handleMessage(final Message msg) {
try {
switch (msg.what) {
case OPEN_CAMERA:
try {
//這裡通過Java的反射機制來執行個體化Camrea
Method openMethod = Class.forName("android.hardware.Camera").getMethod(
"openLegacy", int.class, int.class);
mCamera = (android.hardware.Camera) openMethod.invoke(
null, msg.arg1, CAMERA_HAL_API_VERSION_1_0);
} catch (Exception e) {
/* Retry with open if openLegacy doesn't exist/fails */
Log.v(TAG, "openLegacy failed due to " + e.getMessage()
+ ", using open instead");
mCamera = android.hardware.Camera.open(msg.arg1);
}
if (mCamera != null) {
mParametersIsDirty = true;
// Get a instance of Camera.Parameters for later use.
if (mParamsToSet == null) {
mParamsToSet = mCamera.getParameters();
}
} else {
if (msg.obj != null) {
((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(msg.arg1);
}
}
return;
case RELEASE:
if (mCamera == null) {
return;
}
mCamera.release();
errorCbInstance = null;
mCamera = null;
return;
case RECONNECT:
mReconnectIOException = null;
try {
mCamera.reconnect();
} catch (IOException ex) {
mReconnectIOException = ex;
}
return;
....
}
代碼跟蹤到這裡就要進入系統層的代碼了,android.hardware.Camera的類在源碼目錄的位置/android/frameworks/base/core/java/android/hardware/Camera.java。
好了,文章到這裡就先告一段落了,我們來終結一下:
App啟動後代碼的大緻流程CameraApp-->CameraActivity.onCreate()-->CameraActivity.setModuleFromIndex()-->PhotoModule.init()-->啟動OpenCameraThread線程-->打開相機openCamera()-->在AndroidCameraManagerImpl的CameraHandler裡面通過反射機制執行個體化android.hardware.Camera
限于篇幅原因,我會在下一個部落格講解相機預覽。
謝謝大家,希望這篇部落格呢幫助到您,祝大家生活愉快,工作順心。