啟動相機拍照時拋出 Fail to connect to camera service、cannot connect to camera,相機資源被佔用或釋放不當。
| 原因 | 說明 |
|---|---|
| 相機未正確釋放 | 前次使用未呼叫 release(),或在 onPause/onStop 未釋放 |
| 多處同時存取 | 多個 Activity/Service 同時開啟相機 |
| 權限缺失 | Manifest 缺 CAMERA 權限或 uses-feature |
| 硬體不支援 | 模擬器無相機、裝置相機故障 |
public class CameraActivity extends AppCompatActivity {
private Camera camera;
private CameraPreview preview;
private static final int CAMERA_REQUEST = 1888;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
// 檢查相機硬體
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "裝置無相機", Toast.LENGTH_LONG).show();
finish();
return;
}
// 方式一:使用系統相機 Intent (推薦,簡單、生命週期由系統管理)
dispatchTakePictureIntent();
}
// 系統相機 Intent (最穩健)
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// 可選:指定輸出檔案路徑
File photoFile = createImageFile();
if (photoFile != null) {
Uri photoUri = FileProvider.getUriForFile(this,
"com.example.fileprovider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivityForResult(takePictureIntent, CAMERA_REQUEST);
}
}
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {
// 處理拍照結果 (若指定 EXTRA_OUTPUT,data 可能為 null)
Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath);
imageView.setImageBitmap(bitmap);
}
}
// 建立圖片檔案
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
currentPhotoPath = image.getAbsolutePath();
return image;
}
// 方式二:直接用 Camera API (需自行管理生命週期)
@Override protected void onResume() {
super.onResume();
if (camera == null) {
camera = Camera.open(); // 可能拋出 RuntimeException
preview = new CameraPreview(this, camera);
previewContainer.addView(preview);
}
}
@Override protected void onPause() {
super.onPause();
releaseCamera(); // 關鍵:釋放相機資源
}
private void releaseCamera() {
if (camera != null) {
camera.release();
camera = null;
}
}
}
// build.gradle
implementation "androidx.camera:camera-core:1.3.0"
implementation "androidx.camera:camera-camera2:1.3.0"
implementation "androidx.camera:camera-view:1.3.0"
// 使用
ProcessCameraProvider provider = ProcessCameraProvider.getInstance(this).get();
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(viewFinder.getSurfaceProvider());
ImageCapture imageCapture = new ImageCapture.Builder()
.setTargetRotation(viewFinder.getDisplay().getRotation())
.build();
CameraSelector selector = CameraSelector.DEFAULT_BACK_CAMERA;
provider.bindToLifecycle(this, selector, preview, imageCapture);
// 拍照
File photoFile = createImageFile();
OutputFileOptions options = new OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(options, ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override public void onImageSaved(@NonNull ImageCapture.OutputFileResults results) {
// 成功
}
@Override public void onError(@NonNull ImageCaptureException exception) {
// 失敗
}
});
camera.release() 且設為 null