پیاده سازی قابلیت های مبتنی بر دوربین در سیستم عامل اندروید، همواره یکی از پیچیده ترین و چالش برانگیزترین بخش های مهندسی نرم افزارهای موبایل بوده است. تفاوت های گسترده در سخت افزار دوربین سازندگان گوناگون، مشکلات چرخش تصویر و رفتارهای پیش بینی نشده، کار با دوربین را برای سال ها به کابوس توسعه دهندگان بدل کرده بود.
بر اساس آمارهای معتبر مهندسی گوگل، پیاده سازی دوربین با کتابخانه مدرن CameraX حجم کدهای مورد نیاز را تا ۷۰ درصد نسبت به Camera۲ کاهش می دهد. این کتابخانه به عنوان بخشی از بسته نرم افزاری Jetpack معرفی شده است تا یکپارچگی پایدار در سراسر اکوسیستم اندروید فراهم آورد.
کتابخانه مدرن CameraX با پوشش بیش از ۹۸ درصد گوشی های فعال اندرویدی در سراسر جهان، ناسازگاری های سخت افزاری را برطرف می کند. این ابزار به طور خودکار رفتارهای اختصاصی دستگاه ها (Device Quirks) مانند جهت سنسور و نسبت های ابعادی نامتعارف را شناسایی و اصلاح می نماید.
در این راهنمای جامع و مرجع تخصصی، معماری مدرن کتابخانه CameraX با زبان کاتلین را بررسی می کنیم. همچنین پیاده سازی پیش نمایش تصویر، عکاسی، ضبط ویدیو، آنالیز بلادرنگ فریم ها برای هوش مصنوعی و رعایت الگو های امنیتی را گام به گام تحلیل خواهیم کرد.
معماری زیرسیستم دوربین و چرخه حیات در CameraX
در الگو های قدیمی تر فریم ورک اندروید مانند Camera۲، توسعه دهنده موظف بود سشن های تصویربرداری، تردها و وضعیت چرخهحیات اکتیویتی را به صورت دستی مدیریت کند. کوچک ترین غفلت در بستن دوربین، موجب قفل شدن سخت افزار سنسور و مصرف شدید باتری گوشی می شد.
هسته معماری CameraX بر پایه مفهوم Lifecycle-Awareness یا آگاهی کامل از چرخه حیات بنا شده است. با اتصال سشن دوربین به یک شیء LifecycleOwner (مانند Activity یا Fragment)، سیستم عامل به صورت خودکار تخصیص و آزادسازی سخت افزار دوربین را کنترل می کند.
ارتباط برنامه با سخت افزار از طریق ابجکت ProcessCameraProvider آغاز می شود. این ابجکت به عنوان واسط مرکزی، وظیفه اتصال یا انقطاع یوزکیس های مختلف به دوربین را بر عهده دارد. اجزای کلیدی این معماری شامل یوزکیس های زیر است:
- Preview: وظیفه نمایش زنده جریان تصویر سنسور دوربین بر روی رابط کاربری اپلیکیشن را بر عهده دارد.
- ImageCapture: برای ثبت تصاویر تک فریم با کیفیت بسیار بالا که پس از ذخیره سازی، نمایش بهینه آن در رابط کاربری با لود و کش تصویر با Coil در کامپوز انجام میشود.
- VideoCapture: موتور اختصاصی بر پایه کلاس جدید Recorder برای ضبط فایل های ویدیویی با رزولوشن انتخابی و صدای همگام.
- ImageAnalysis: یک پایپ لاین پردازشی برای ارسال فریم های بافر تصویر به کتابخانه های بینایی ماشین و هوش مصنوعی.
مدیریت پیکربندی دوربین توسط رابط CameraControl و دریافت اطلاعات سنسور از طریق CameraInfo انجام می گیرد. با CameraControl می توانید زوم دیجیتال، فوکوس خودکار، تنظیمات نوردهی و چراغ قوه را کنترل نمایید.
از سوی دیگر، CameraInfo وضعیت پشتیبانی سخت افزار از ویژگی های پیشرفته مانند عکاسی در شب (Night Mode) یا فیلم برداری HDR را در قالب جریان های واکنشی LiveData گزارش می دهد.
انتخاب سنسور دوربین نیز از طریق شیء CameraSelector مدیریت می شود. شما می توانید دوربین پشتی (DEFAULT_BACK_CAMERA) یا دوربین سلفی (DEFAULT_FRONT_CAMERA) را انتخاب نمایید. همچنین در دستگاه های دارای چند لنز، امکان فیلتر کردن لنزهای اولتراواید یا تله فوتو از طریق فیلترهای سفارشی لنز وجود دارد.
مدیریت خودکار منابع: با متد bindToLifecycle، به محض رفتن اکتیویتی به پس زمینه در متد onStop، سنسور دوربین خاموش می شود تا مصرف باتری متوقف گردد؛ و با بازگشت کاربر، پیش نمایش بلافاصله بدون قطعی از سر گرفته می شود.
در پروژه های بزرگ سازمانی، تفکیک دسترسی به این زیرسیستم بر اساس اصول معماری تمیز در اندروید انجام می گیرد تا منطق پردازش تصاویر کاملا از فریم ورک اندروید مجزا بماند.
همچنین با پیاده سازی ماژولار و تزریق نمونه های دوربین از طریق فریم ورک تزریق وابستگی با Hilt، فرآیند تست نویسی و جداسازی کدهای پلتفرم با سهولت و دقت مهندسی بالا انجام می پذیرد.
مقایسه یوزکیس های چهارگانه در کتابخانه CameraX
پیش از بررسی پیاده سازی فنی هر یوزکیس، ضروری است تفاوت ها و مشخصات عملکردی هر مؤلفه را بشناسیم. جدول زیر مقایسه ای ساختاریافته از این چهار مؤلفه اصلی ارائه می دهد:
| نام UseCase | فرمت خروجی داده | میزان مصرف رم و پردازنده | وابستگی به Surface | کاربرد عملیاتی اصلی |
|---|---|---|---|---|
| Preview | SurfaceRequest | بسیار کم و بهینه شده | مستقیما به PreviewView متصل می شود | چشم انداز و کادربندی تصویر توسط کاربر |
| ImageCapture | JPEG / Ultra HDR بافر | کوتاه مدت در زمان شات | بدون نیاز به Surface نمایشی | عکس برداری اسناد، چهره و تصاویر پروفایل |
| VideoCapture | MP۴ کانتینر تصویری و صوتی | متوسط تا بالا بر اساس انکودر | سطح انکودر سخت افزاری دستگاه | فیلم برداری پیوسته، استوری و گزارش ویدیویی |
| ImageAnalysis | ImageProxy (YUV_۴۲۰_۸۸۸) | وابسته به الگوریتم آنالیزور | بافر مموری بدون وابستگی به ویو | اسکن بارکد، تشخیص چهره و یادگیری ماشین |
شما می توانید ترکیبی از این یوزکیس ها را به طور همزمان به سنسور متصل نمایید؛ به عنوان مثال Preview به همراه ImageCapture و ImageAnalysis به صورت همزمان اجرا شوند.
تلفیق چند یوزکیس به این معناست که سنسور فیزیکی دوربین تنها یک بار باز می شود، اما جریان فریم ها به صورت موازی به چند مسیر مختلف هدایت می گردد که مصرف انرژی را به حداقل می رساند.
محدودیت های ترکیبی بر اساس سطح سخت افزاری دستگاه (Hardware Level در Camera۲ مانند LIMITED یا FULL) تعیین می شوند. کتابخانه CameraX بهترین رزولوشن مشترک را به گونه ای انتخاب می کند که هیچ تداخلی در پردازش رخ ندهد.
آماده سازی نیازمندی ها و دریافت دسترسی های سیستم عامل
برای شروع کار با CameraX، ابتدا باید وابستگی های پایدار را به فایل تنظیمات بیلد گریدل (build.gradle.kts) ماژول اپلیکیشن اضافه کنیم. توصیه می شود از نسخه های هماهنگ شده Bill of Materials استفاده نمایید:
dependencies {
val cameraxVersion = "1.4.0"
implementation("androidx.camera:camera-core:$cameraxVersion")
implementation("androidx.camera:camera-camera2:$cameraxVersion")
implementation("androidx.camera:camera-lifecycle:$cameraxVersion")
implementation("androidx.camera:camera-video:$cameraxVersion")
implementation("androidx.camera:camera-view:$cameraxVersion")
}
سپس در فایل مانیفست اپلیکیشن (AndroidManifest.xml)، مجوزهای سخت افزاری مربوط به دوربین و ضبط صدا را اعلان می کنیم:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>
تعریف تگ uses-feature با مقدار any تضمین می کند که اپلیکیشن بر روی دستگاه هایی که تنها دوربین سلفی دارند (مانند برخی تبلت ها یا دستگاه های صنعتی) نیز بدون محدودیت در استورها قابل نصب باشد.
هشدار امنیتی و حریم خصوصی: در اندروید ۶ به بعد، مجوزهای CAMERA و RECORD_AUDIO در دسته خطرناک قرار دارند و باید در زمان اجرا تایید شوند. همچنین در اندروید ۱۴ برای سرویس های فیلم برداری باید صراحتا FOREGROUND_SERVICE_TYPE_CAMERA مشخص گردد.
برای دریافت این مجوزها در کاتلین با استفاده از قراردادهای نوین Activity Result، کدی مشابه قطعه زیر پیاده سازی می کنیم:
class CameraPermissionManager(private val activity: ComponentActivity) {
private var onGrantedCallback: (() -> Unit)? = null
private val permissionLauncher = activity.registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] == true
if (cameraGranted) {
onGrantedCallback?.invoke()
}
}
fun checkAndRequest(onGranted: () -> Unit) {
this.onGrantedCallback = onGranted
val cameraPermission = Manifest.permission.CAMERA
if (ContextCompat.checkSelfPermission(activity, cameraPermission) ==
PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
permissionLauncher.launch(arrayOf(cameraPermission, Manifest.permission.RECORD_AUDIO))
}
}
}
در زمان تقاضای مجوز از کاربر، پیاده سازی دیالوگ های پیشین (Rationale) برای تبیین ضرورت دسترسی، نرخ تایید کاربر را به طور محسوسی ارتقا می بخشد.
پیاده سازی پیش نمایش زنده با ویجت PreviewView
کتابخانه CameraX ویجت ویژه ای به نام PreviewView در ماژول camera-view فراهم کرده است. این ویجت رابط کاربری به صورت هوشمند تصمیم می گیرد که برای نمایش تصویر از SurfaceView استفاده کند یا TextureView.
در حالت پیش فرض، PreviewView از شیوه PERFORMANCE (مبتنی بر SurfaceView) استفاده می کند که کمترین مصرف باتری و تاخیر را به همراه دارد. اما اگر نیازمند اعمال انیمیشن های چرخشی روی صفحه دوربین هستید، حالت COMPATIBLE (مبتنی بر TextureView) مناسب خواهد بود.
کد زیر نحوه راه اندازی و اتصال پیش نمایش زنده را در کاتلین نمایش می دهد:
fun startCameraPreview(
lifecycleOwner: LifecycleOwner,
context: Context,
previewView: PreviewView
) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.surfaceProvider = previewView.surfaceProvider
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview
)
} catch (exc: Exception) {
Log.e("CameraX", "اتصال پیش نمایش ناموفق بود", exc)
}
}, ContextCompat.getMainExecutor(context))
}
فراخوانی متد cameraProvider.unbindAll() پیش از اتصال مجدد الزامی است؛ این کار اطمینان حاصل می کند که هیچ سشن یتیم یا معلقی از چرخه های قبلی در حافظه دستگاه باقی نمانده باشد.
ویجت PreviewView همچنین امکان تنظیم نوع مقیاس بندی (ScaleType) مانند FILL_CENTER یا FIT_CENTER را فراهم می آورد تا کادربندی تصویر سنسور متناسب با ابعاد صفحه نمایش گوشی هماهنگ گردد.
علاوه بر این، می توان با تعریف یک ViewPort نسبت ابعاد دلخواه (مانند ۱۶:۹ یا ۴:۳) را برای تمام یوزکیس ها به اشتراک گذاشت تا آنچه کاربر در صفحه می بیند، دقیقا منطبق بر خروجی عکس یا ویدیو باشد.
عکاسی با وضوح بالا با کلاس ImageCapture
یوزکیس ImageCapture برای عکاسی طراحی شده است. این ابزار وظیفه همگام سازی فوکوس خودکار، تنظیم سرعت شاتر، تراز سفیدی و صدور فایل تصویر نهایی با فرمت استاندارد را بر عهده دارد.
شما می توانید راهبرد عکاسی را میان حداکثر کیفیت یا حداقل تاخیر شات تعیین کنید:
- CAPTURE_MODE_MAXIMIZE_QUALITY: اعمال الگوریتم های چندفریمی پردازش تصویر و کاهش نویز برای دست یابی به بالاترین وضوح.
- CAPTURE_MODE_MINIMIZE_LATENCY: کاهش زمان پردازش برای ثبت فوری تصویر در صحنه های با سرعت بالا.
- CAPTURE_MODE_ZERO_SHUTTER_LAG: ذخیره پیوسته فریم ها در بافر چرخشی برای ثبت لحظه ای عکس درست در کسری از ثانیه که کاربر دکمه را لمس می کند.
همچنین تنظیمات حالت فلاش شامل سه گزینه FLASH_MODE_AUTO، FLASH_MODE_ON و FLASH_MODE_OFF است که متناسب با نور محیط توسط کاربر یا هوش مصنوعی قابل تغییر خواهد بود.
کد زیر نحوه تنظیم ImageCapture و ذخیره سازی فایل خروجی در حافظه عمومی رسانه ها (MediaStore) را تشریح می کند:
fun takePhoto(
context: Context,
imageCapture: ImageCapture,
onSuccess: (Uri) -> Unit
) {
val name = "IMG_${System.currentTimeMillis()}.jpg"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
}
val outputOptions = ImageCapture.OutputFileOptions.Builder(
context.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e("CameraX", "خطا در ثبت عکس: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
output.savedUri?.let { onSuccess(it) }
}
}
)
}
با بهره گیری از ابزار های غیرهمزمان و جریان های داده در کورتین ها و Flow در کاتلین، می توانید رویداد ثبت تصویر را به سادگی به لایه های بالایی معماری ارسال کنید.
ذخیره سازی مستقیم در MediaStore تضمین می کند که عکس بلافاصله در گالری پیش فرض سیستم عامل ثبت شده و دسترسی به آن نیازمند مجوزهای قدیمی مدیریت فایل نباشد.
قابلیت های افزونه دوربین (Camera Extensions) مانند حالت بوکه (Bokeh)، حالت شب (Night) و HDR نیز از طریق کلاس ExtensionManager به سادگی قابل فعال سازی هستند تا از پردازشگر تصویر اختصاصی شرکت سازنده گوشی بهره برداری شود.
آنالیز فریم و ادغام با هوش مصنوعی ML Kit
یکی از درخشان ترین قابلیت های CameraX برای پردازش هوشمند و ذخیره فراداده ها در دیتابیس Room در اندروید، یوزکیس ImageAnalysis است. این ابزار فریم های خام سنسور را در فرمت YUV_۴۲۰_۸۸۸ بدون هیچ گونه افت سرعت در پیش نمایش در اختیار کدهای شما قرار می دهد.
با استفاده از راهبرد بافرینگ STRATEGY_KEEP_ONLY_LATEST در ImageAnalysis، تاخیر پردازش هر فریم تصویر به زیر ۳۰ ms کاهش می یابد. این استراتژی در صورتی که پردازش یک فریم طول بکشد، فریم های انباشته شده را رها می کند تا جریان تصویر همیشه به روز بماند.
در نقطه مقابل، راهبرد STRATEGY_BLOCK_PRODUCER وجود دارد که پردازش تک تک فریم ها بدون جا افتادن حتی یک فریم را الزامی می کند؛ این حالت برای ثبت ویدیوهای تحلیلی فریم به فریم مناسب است.
کد زیر پیاده سازی آنالیزور تصویر برای اسکن بارکد و کدهای QR با استفاده از کیت یادگیری ماشین گوگل (ML Kit) را نمایش می دهد:
class QrCodeAnalyzer(
private val onBarcodeDetected: (String) -> Unit
) : ImageAnalysis.Analyzer {
private val scanner = BarcodeScanning.getClient()
@OptIn(ExperimentalGetImage::class)
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(
mediaImage,
imageProxy.imageInfo.rotationDegrees
)
scanner.process(image)
.addOnSuccessListener { barcodes ->
barcodes.firstOrNull()?.rawValue?.let { value ->
onBarcodeDetected(value)
}
}
.addOnCompleteListener {
imageProxy.close() // بستن بافر فریم الزامی است
}
} else {
imageProxy.close()
}
}
}
بستن شیء imageProxy.close() پس از اتمام پردازش فریم یک الزام حیاتی است. در صورت بسته نشدن این شیء، بافر حافظه پر شده و پایپ لاین آنالیزور به کلی متوقف می گردد.
همچنین می توانید با متد setTargetResolution رزولوشن ورودی آنالیزور را بر روی مقادیر سبک تر (مانند ۶۴۰x۴۸۰) تنظیم کنید تا بار پردازشی شبکه عصبی به حداقل ممکن برسد.
برای ترسیم کادرهای تشخیص داده شده دور اشیا بر روی صفحه نمایش، کلاس CoordinateTransform مختصات بافر آنالیزور را با ابعاد واقعی ویجت PreviewView تطبیق می دهد.
ضبط ویدیوی حرفه ای با Recorder و VideoCapture
برای پردازش و فشرده سازی پسزمینه ویدیوها توسط ورک منیجر WorkManager در اندروید، لازم است بدانید در نسخه های اولیه ضبط ویدیو دارای محدودیت بود. اما از نسخه ۱.۱ به بعد، گوگل معماری کاملا بازنویسی شده ای بر پایه کلاس های Recorder و VideoCapture معرفی کرد.
کلاس Recorder به شما اجازه می دهد کیفیت هدف (مانند ۱۰۸۰p یا ۴K) را از طریق یک QualitySelector مشخص کرده و در صورت عدم پشتیبانی سخت افزار دستگاه، رزولوشن مناسب بعدی را به عنوان جایگزین تعیین کنید.
کد زیر ساخت یوزکیس ضبط ویدیو با کیفیت فول اچ دی و پشتیبانی از رزولوشن های جایگزین را نشان می دهد:
fun buildVideoCaptureUseCase(): VideoCapture<Recorder> {
val qualitySelector = QualitySelector.from(
Quality.FHD,
FallbackStrategy.lowerQualityOrHigherThan(Quality.FHD)
)
val recorder = Recorder.Builder()
.setQualitySelector(qualitySelector)
.build()
return VideoCapture.withOutput(recorder)
}
سپس متد زیر فرآیند ضبط ویدیو به همراه همگام سازی صدا و ذخیره سازی در حافظه سیستم را اجرا می نماید:
fun startVideoRecording(
context: Context,
videoCapture: VideoCapture<Recorder>,
audioEnabled: Boolean,
onSaved: (Uri) -> Unit
): Recording {
val name = "VID_${System.currentTimeMillis()}.mp4"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(
context.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
).setContentValues(contentValues).build()
return videoCapture.output
.prepareRecording(context, mediaStoreOutput)
.apply { if (audioEnabled) withAudioEnabled() }
.start(ContextCompat.getMainExecutor(context)) { event ->
if (event is VideoRecordEvent.Finalize && !event.hasError()) {
onSaved(event.outputResults.outputUri)
}
}
}
ابجکت Recording بازگشتی این امکان را به شما می دهد که در هر لحظه با فراخوانی متدهای pause()، resume() یا stop() فرآیند ضبط ویدیو را کنترل نمایید.
مدیریت شنودگر رویدادهای ضبط (VideoRecordEvent Listener) وضعیت هایی چون حجم فایل ضبط شده، زمان سپری شده و میزان فضای باقی مانده دیسک را در زمان واقعی مخابره می کند.
تنظیمات تعاملی فوکوس لمسی، زوم دیجیتال و فلاش
یک اپلیکیشن دوربین کاربرپسند باید به تعاملات لمسی کاربر پاسخ دهد. رابط CameraControl که پس از بایند شدن به سشن دریافت می شود، این قابلیت ها را فراهم می سازد.
برای پیاده سازی قابلیت فوکوس با لمس (Tap-to-Focus)، مختصات لمس صفحه نمایش باید به سیستم مختصات سنسور سنس شود. کلاس MeteringPointFactory این تبدیل را به دقت انجام می دهد:
fun setupTapToFocus(
previewView: PreviewView,
cameraControl: CameraControl,
x: Float,
y: Float
) {
val factory = previewView.meteringPointFactory
val point = factory.createPoint(x, y)
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
.setAutoCancelDuration(3, java.util.concurrent.TimeUnit.SECONDS)
.build()
cameraControl.startFocusAndMetering(action)
}
همچنین با تنظیم setAutoCancelDuration، فوکوس پس از چند ثانیه به صورت خودکار به وضعیت مانیتورینگ پیوسته بازمی گردد.
برای کنترل زوم لمسی (Pinch-to-Zoom)، متد setLinearZoom یک مقدار اعشاری بین ۰.۰ تا ۱.۰ می پذیرد که با ژست دو انگشتی کاربر به آسانی قابل پیوند است.
تنظیم میزان روشنایی تصویر یا جبران نوردهی (Exposure Compensation) نیز با متد setExposureCompensationIndex انجام می شود تا در محیط های کم نور یا با تابش مستقیم آفتاب، کاربر تصویر را به صورت دستی تیره تر یا روشن تر کند.
همچنین برای فعال سازی چراغ قوه در حین فیلم برداری یا پیش نمایش در محیط های تاریک، کافی است متد enableTorch(true) را بر روی شیء CameraControl صدا بزنید.
مقایسه معماری CameraX در برابر چارچوب سنتی Camera۲
اگرچه CameraX بر بستر زیرساخت Camera۲ پیاده سازی شده است، اما رویکرد طراحی و سطح دسترسی آن ها تفاوت های بنیادین با یکدیگر دارد.
در توسعه روزمره اپلیکیشن ها، استفاده از CameraX انتخاب برتر و قطعی است. با این حال شناخت مرزهای توانمندی هر فریم ورک برای معماران نرم افزار ضروری است:
- سازگاری تضمین شده با دستگاه ها: تیم گوگل هزاران دستگاه اندرویدی را در آزمایشگاه های خود تست می کند تا رفتارهای متناقض سنسورها را با الگو های خودکار حل کند.
- کاهش شگفت انگیز کدهای تکراری: نوشتن قابلیت ساده پیش نمایش در Camera۲ نیازمند حداقل ۴۰۰ خط کد پر از ریسک کرش بود؛ اما در CameraX تنها با ۲۰ خط کد پیاده می شود.
- چه زمانی به Camera۲ نیاز داریم؟ اگر اپلیکیشن شما یک برنامه عکاسی فوق حرفه ای است که به تصاویر خام سنسور (RAW DNG) یا کنترل دستی میلی ثانیه ای سرعت شاتر و ISO نیاز دارد، همچنان دسترسی سطح پایین Camera۲ الزامی است.
خوشبختانه با بهره گیری از پکیج androidx.camera.camera2.interop می توانید در صورت نیاز، دستورات سطح پایین Camera۲ را با کدهای CameraX ترکیب نمایید.
این قابلیت بین پلتفرمی به شما امکان می دهد کنترلرهای اختصاصی CaptureRequest را مستقیما به یوزکیس های CameraX ضمیمه کرده و از بهترین های هر دو جهان بهره مند گردید.
خطایابی و رفع چالش های متداول در توسعه دوربین
مدیریت صحیح بافرهای فریم و پایش منابع با راهکارهای بهینه سازی مصرف رم و حل Memory Leak پایداری دوربین در استفاده طولانیمدت را تضمین مینماید.
در طول توسعه قابلیت های مبتنی بر دوربین، ممکن است رفتارهای پیش بینی نشده ای در برخی مدل های گوشی رخ دهد. در این بخش مهم ترین نکات عیب یابی را مرور می کنیم:
- چرخش نادرست تصویر ثبت شده: همواره مقدار چرخش فریم را از
imageInfo.rotationDegreesبخوانید و آن را در ماتریس تصویر یا EXIF فایل نهایی ذخیره کنید تا تصویر وارونه نشود. - افت شدید فریم در ImageAnalysis: اطمینان حاصل کنید پردازش های سنگین محاسباتی یا هوش مصنوعی بر روی ترد پس زمینه انجام شوند و هرگز ترد اصلی را مسدود نکنند.
- سیاه ماندن صفحه PreviewView: بررسی کنید که آیا متد
unbindAll()فراخوانی شده است و شیء LifecycleOwner به درستی پاس داده شده باشد. - خطای نبود سنسور دوربین: همیشه پیش از باز کردن دوربین، با متد
hasCamera(CameraSelector)از وجود فیزیکی سنسور پشتی یا سلفی اطمینان حاصل کنید.
برای خطایابی عمیق تر در زمان بیلد، می توانید از لاگ های داخلی CameraX با دستور زیر در خط فرمان ADB استفاده کنید تا وضعیت دقیق فریم های ارسالی سنسور را رصد نمایید:
adb shell setprop log.tag.CameraX DEBUG
همچنین دستور زیر در ترمینال، اطلاعات کاملی از وضعیت سنسورها و رزولوشن های پشتیبانی شده دستگاه متصل را گزارش می دهد:
پیشنهاد می شود همواره وضعیت مصرف حافظه و نرخ فریم بر ثانیه (FPS) را در حین کار با دوربین با ابزار های Android Profiler مانیتور نمایید تا از روان بودن رابط کاربری مطمئن گردید.
در سناریوهای ضبط ویدیو، ممکن است کاربر بخواهد در حین ضبط صدای محیط را قطع یا وصل کند. متد mute در شیء فعال Recording امکان قطع لحظه ای میکروفون را بدون قطع جریان ویدیویی به راحتی فراهم می آورد.
همچنین برای نوشتن تست های خودکار رابط کاربری در محیط شبیه ساز، ماژول اختصاصی androidx.camera:camera-testing این امکان را ایجاد می کند که بدون دسترسی به دوربین فیزیکی، رفتارهای پیش نمایش و ثبت شات را ماک کرده و سناریوهای مختلف را اعتبارسنجی کنید.
اتخاذ یک معماری استاندارد و تست های مستمر در نسخه های مختلف سیستم عامل، تضمین می کند که اپلیکیشن شما در تمام شرایط نوری و سخت افزاری، بالاترین بهره وری و رضایت کاربران را کسب نماید.
علاوه بر این، پیشنهاد می شود همواره وضعیت پایداری کرش ها و خطاهای سخت افزاری سنسور را از طریق سرویس Firebase Crashlytics زیر نظر داشته باشید تا در صورت بروز خطاهای مختص برندهای خاص، بی درنگ از آن مطلع شوید.
توسعه دهندگان اندروید با به کارگیری الگو های نوین CameraX و رعایت استانداردهای روز گوگل، می توانند قابلیت های دوربین برنامه خود را به یک مزیت رقابتی قدرتمند و تجربه ای لذت بخش برای کاربران تبدیل نمایند.
تفاوت اصلی CameraX با فریم ورک Camera۲ چیست؟
کتابخانه CameraX بر پایه مفهوم یوزکیس ها ساخته شده و به صورت خودکار چرخه حیات و ناسازگاری های سخت افزاری برندهای مختلف را مدیریت می کند؛ در حالی که Camera۲ سطحی بسیار پایین، دستی و پر از کدهای تکراری دارد.
چرا بستن imageProxy.close در ImageAnalysis الزامی است؟
هر فریم دوربین در بافر حافظه قفل می شود. تا زمانی که متد close فراخوانی نشود، فریم ورک بافر را آزاد نمی کند و پس از چند فریم، کل پایپ لاین پردازش تصویر مسدود خواهد شد.
تفاوت حالت های PERFORMANCE و COMPATIBLE در PreviewView چیست؟
حالت PERFORMANCE بر بستر SurfaceView اجرا شده و مصرف باتری و رم کمتری دارد؛ اما COMPATIBLE بر پایه TextureView است و امکان اجرای انیمیشن ها و ترنزیشن های پیچیده روی ویو را ممکن می سازد.
آیا CameraX از تمام نسخه های اندروید پشتیبانی می کند؟
کتابخانه CameraX از سطح API شماره ۲۱ (اندروید ۵.۰ لالی پاپ) به بعد پشتیبانی می کند که عملا بیش از ۹۸ درصد از کل دستگاه های فعال اندروید در دنیا را شامل می شود.
چگونه می توان همزمان عکاسی کرد و پیش نمایش را نشان داد؟
کافی است هر دو یوزکیس Preview و ImageCapture را با هم در متد bindToLifecycle به عنوان پارامتر ارسال کنید تا سخت افزار هر دو را در یک سشن مشترک اجرا کند.
چرا عکس های ثبت شده گاهی در گالری وارونه نمایش داده می شوند؟
سنسورهای دوربین در دستگاه ها زاویه های فیزیکی متفاوتی (مانند ۹۰ یا ۲۷۰ درجه) دارند. باید تگ EXIF مربوط به Orientation بر اساس rotationDegrees در فایل عکس ذخیره شود.
نقش ProcessCameraProvider در کدهای CameraX چیست؟
این کلاس تک نمونه (Singleton) مدیریت ارتباط مرکزی با سرویس دوربین در سیستم عامل را بر عهده دارد و امکان بایند کردن یوزکیس ها به چرخه حیات را فراهم می کند.
آیا می توان در CameraX از دوربین سلفی و پشتی به صورت همزمان استفاده کرد؟
استفاده همزمان (Dual Camera Concurrent Streaming) نیازمند پشتیبانی سخت افزاری دستگاه است و در دستگاه های سازگار از طریق ابزار ConcurrentCamera قابل پیاده سازی است.
چگونه می توان قابلیت زوم لمسی (Pinch-to-Zoom) را در CameraX پیاده کرد؟
با گوش دادن به ژست های حرکتی لمسی کاربر و فراخوانی متد setLinearZoom یا setZoomRatio بر روی شیء CameraControl می توان میزان زوم دوربین را به آسانی تغییر داد.
آیا برای ضبط ویدیو با کلاس Recorder نیاز به مجوز میکروفون است؟
اگر بخواهید ویدیو همراه با صدا ضبط شود، بله؛ متد withAudioEnabled نیازمند دریافت مجوز زمان اجرای RECORD_AUDIO از کاربر است.
چگونه می توان قابلیت فوکوس با لمس صفحه (Tap-to-Focus) ساخت؟
با استفاده از FocusMeteringAction و تبدیل مختصات لمسی به MeteringPoint از طریق PreviewView، و ارسال آن به متد startFocusAndMetering در شیء CameraControl پیاده می شود.
چرا با تغییر به تب دیگر دوربین به صورت خودکار خاموش می شود؟
به دلیل اینکه اتصال با bindToLifecycle انجام شده است، با فراخوانی رویداد ON_PAUSE یا ON_STOP توسط چرخه حیات فریم ورک، سخت افزار دوربین برای حفظ باتری فورا متوقف می گردد.


امتیاز و نظرات
هنوز نظری ثبت نشده است. اولین نفر باشید.