如何通过JNI函数控制Android相机闪光灯的开关状态?
刚好我之前做过类似的JNI闪光灯控制需求,Android NDK的ACamera系列API确实没有像Java层CameraManager.setTorchMode()那样直接的开关接口,但我们可以通过配置相机的捕获请求来实现完全等价的功能。下面给你详细的实现方案:
使用Android NDK Camera API实现JNI闪光灯控制
核心实现思路
要在Native层控制闪光灯,我们需要完成这几个关键步骤:
- 获取相机管理器实例,枚举所有可用相机,筛选出支持闪光灯的后置相机
- 打开目标相机设备,创建捕获会话(即使不需要预览画面,也需要一个有效的输出目标来满足API要求)
- 通过修改捕获请求的曝光模式和闪光灯模式参数,实现闪光灯的开关切换
完整JNI代码实现
#include <jni.h> #include <android/log.h> #include <camera/NdkCameraManager.h> #include <camera/NdkCameraDevice.h> #include <camera/NdkCameraCaptureSession.h> #include <camera/NdkCameraMetadata.h> #include <android/native_window.h> #define LOG_TAG "FlashlightJNI" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) // 全局变量保存相机相关资源 static ACameraManager* gCameraManager = nullptr; static ACameraDevice* gCameraDevice = nullptr; static ACameraCaptureSession* gCaptureSession = nullptr; static ACameraOutputTarget* gOutputTarget = nullptr; static ACaptureRequest* gCaptureRequest = nullptr; static bool gIsFlashOn = false; static ANativeWindow* gDummyWindow = nullptr; // 相机设备状态回调(处理相机连接/断开事件) static void cameraDeviceCallback(void* context, ACameraDevice* device, ACameraDeviceStatus status) { if (status == ACAMERA_DEVICE_STATUS_DISCONNECTED) { LOGD("Camera device disconnected"); // 这里可以添加资源清理逻辑 } } // 捕获会话状态回调 static void captureSessionCallback(void* context, ACameraCaptureSession* session, ACameraCaptureSessionStatus status) { if (status == ACAMERA_CAPTURE_SESSION_STATUS_CLOSED) { LOGD("Capture session closed"); } } // 查找支持闪光灯的后置相机ID const char* findFlashSupportedCamera(ACameraManager* manager) { ACameraIdList* cameraIdList = nullptr; if (ACameraManager_getCameraIdList(manager, &cameraIdList) != ACAMERA_OK) { LOGE("Failed to get camera ID list"); return nullptr; } for (int i = 0; i < cameraIdList->numCameras; i++) { const char* cameraId = cameraIdList->cameraIds[i]; ACameraMetadata* metadata = nullptr; if (ACameraManager_getCameraCharacteristics(manager, cameraId, &metadata) != ACAMERA_OK) { continue; } // 检查是否支持闪光灯 uint8_t flashAvailable = 0; if (ACameraMetadata_getConstEntry(metadata, ACAMERA_FLASH_INFO_AVAILABLE, &flashAvailable) == ACAMERA_OK && flashAvailable == 1) { // 检查是否为后置相机 int32_t lensFacing = 0; if (ACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &lensFacing) == ACAMERA_OK && lensFacing == ACAMERA_LENS_FACING_BACK) { ACameraMetadata_free(metadata); ACameraManager_deleteCameraIdList(cameraIdList); return cameraId; } } ACameraMetadata_free(metadata); } ACameraManager_deleteCameraIdList(cameraIdList); LOGE("No back camera with flash found"); return nullptr; } // 创建虚拟输出目标(满足捕获会话的要求) bool createDummyOutputTarget() { // 创建一个最小尺寸的离屏窗口作为虚拟输出 gDummyWindow = ANativeWindow_create(); if (!gDummyWindow) { LOGE("Failed to create dummy window"); return false; } ANativeWindow_setBuffersGeometry(gDummyWindow, 1, 1, WINDOW_FORMAT_RGBA_8888); gOutputTarget = ACameraOutputTarget_create(gDummyWindow); return gOutputTarget != nullptr; } // 切换闪光灯状态 void toggleFlashState() { if (!gCaptureRequest) return; gIsFlashOn = !gIsFlashOn; if (gIsFlashOn) { // 设置闪光灯常亮模式 ACaptureRequest_setEntry_i32(gCaptureRequest, ACAMERA_CONTROL_AE_MODE, ACAMERA_CONTROL_AE_MODE_ON_ALWAYS_FLASH); ACaptureRequest_setEntry_i32(gCaptureRequest, ACAMERA_FLASH_MODE, ACAMERA_FLASH_MODE_TORCH); } else { // 关闭闪光灯,恢复自动曝光 ACaptureRequest_setEntry_i32(gCaptureRequest, ACAMERA_CONTROL_AE_MODE, ACAMERA_CONTROL_AE_MODE_ON); ACaptureRequest_setEntry_i32(gCaptureRequest, ACAMERA_FLASH_MODE, ACAMERA_FLASH_MODE_OFF); } // 更新捕获请求到会话 if (gCaptureSession) { ACameraCaptureSession_setRepeatingRequest(gCaptureSession, nullptr, 1, &gCaptureRequest, nullptr); } } // 初始化相机并创建捕获会话 bool initCamera(const char* cameraId) { // 打开相机设备 if (ACameraManager_openCamera(gCameraManager, cameraId, cameraDeviceCallback, nullptr, &gCameraDevice) != ACAMERA_OK) { LOGE("Failed to open camera"); return false; } // 创建虚拟输出目标 if (!createDummyOutputTarget()) { ACameraDevice_close(gCameraDevice); gCameraDevice = nullptr; return false; } // 创建捕获会话 if (ACameraDevice_createCaptureSession(gCameraDevice, 1, &gOutputTarget, captureSessionCallback, nullptr, &gCaptureSession) != ACAMERA_OK) { LOGE("Failed to create capture session"); ACameraOutputTarget_free(gOutputTarget); ACameraDevice_close(gCameraDevice); gCameraDevice = nullptr; return false; } // 创建捕获请求并初始化闪光灯状态 if (ACameraDevice_createCaptureRequest(gCameraDevice, TEMPLATE_PREVIEW, &gCaptureRequest) != ACAMERA_OK) { LOGE("Failed to create capture request"); ACameraCaptureSession_close(gCaptureSession); ACameraOutputTarget_free(gOutputTarget); ACameraDevice_close(gCameraDevice); gCameraDevice = nullptr; return false; } // 默认关闭闪光灯 ACaptureRequest_setEntry_i32(gCaptureRequest, ACAMERA_CONTROL_AE_MODE, ACAMERA_CONTROL_AE_MODE_ON); ACaptureRequest_setEntry_i32(gCaptureRequest, ACAMERA_FLASH_MODE, ACAMERA_FLASH_MODE_OFF); ACameraCaptureSession_setRepeatingRequest(gCaptureSession, nullptr, 1, &gCaptureRequest, nullptr); return true; } JNIEXPORT void JNICALL Java_com_android_rxjava_flashlightflicker_MainActivity_flasher(JNIEnv *env, jobject instance) { // 初始化相机管理器 if (!gCameraManager) { gCameraManager = ACameraManager_create(); if (!gCameraManager) { LOGE("Failed to create camera manager"); return; } } // 相机未初始化时,先初始化相机 if (!gCameraDevice) { const char* cameraId = findFlashSupportedCamera(gCameraManager); if (!cameraId) return; if (!initCamera(cameraId)) return; } // 切换闪光灯状态 toggleFlashState(); } // 释放所有相机资源的JNI方法,必须在Activity销毁时调用 JNIEXPORT void JNICALL Java_com_android_rxjava_flashlightflicker_MainActivity_releaseFlashResources(JNIEnv *env, jobject instance) { if (gCaptureSession) { ACameraCaptureSession_stopRepeating(gCaptureSession); ACameraCaptureSession_close(gCaptureSession); gCaptureSession = nullptr; } if (gOutputTarget) { ACameraOutputTarget_free(gOutputTarget); gOutputTarget = nullptr; } if (gCaptureRequest) { ACaptureRequest_free(gCaptureRequest); gCaptureRequest = nullptr; } if (gCameraDevice) { ACameraDevice_close(gCameraDevice); gCameraDevice = nullptr; } if (gDummyWindow) { ANativeWindow_destroy(gDummyWindow); gDummyWindow = nullptr; } if (gCameraManager) { ACameraManager_delete(gCameraManager); gCameraManager = nullptr; } }
关键注意事项
- 权限要求:必须在AndroidManifest.xml中声明
CAMERA权限,Android 6.0及以上还需要动态申请权限,否则相机无法打开。 - 虚拟输出目标:Native Camera API要求捕获会话必须绑定有效的输出目标,这里我们创建了一个最小尺寸的虚拟窗口来满足要求,避免了必须显示预览画面的限制。
- 资源释放:一定要在Activity销毁时调用
releaseFlashResources()方法,否则会导致相机资源泄漏,影响其他应用使用相机。 - 设备兼容性:部分设备的相机特性可能有差异,建议在初始化前检查相机的闪光灯支持状态,避免崩溃。
Java层配合代码示例
public class MainActivity extends AppCompatActivity { static { System.loadLibrary("flashlight-jni"); } public native void flasher(); public native void releaseFlashResources(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 动态申请相机权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1001); } } // 点击按钮切换闪光灯 public void onToggleFlashClick(View view) { flasher(); } @Override protected void onDestroy() { super.onDestroy(); // 释放Native层资源 releaseFlashResources(); } }
内容的提问来源于stack exchange,提问作者Alex2452
相关产品推荐
相关产品推荐

