こんにちは、ラクマで主にAndroidを担当している@shin_nosakaです。
ラクマAndroidでは、v7.34.0でバーコード出品の機能で使用されるCamera APIをCameraXへの移行を行いました。 今回は、Android界隈では鬼門と言われる、ラクマでのカメラ機能の実装と、CameraXへの移行にあたっての経緯についてお話ししようかと思います。
ラクマAndroidにおけるカメラ周りの実装の歴史
ラクマではバーコード出品機能をv7.15.0から提供しています。
この頃、ラクマではAndroid 5.0未満(4.4)の端末をサポートしており、Camera2 APIはAndroid5.0以降でサポートされるということもあり、Camera API1で実装されました。
なお、Androidにおけるカメラ バージョンのサポートはこちらをご覧ください。
Camera API1での実装
カメラ関連のクラスはこんな感じです。
CameraSourcePreviewは内部でSurfaceViewを持っており、こちらにカメラの管理を行うためのクラスであるCameraSourceを保持しています。
CameraSourceは画面のライフサイクルを意識する必要があるため、onResume
でカメラの開始、onResume
でカメラの開始、onPause
でカメラの停止など、ライフサイクルに応じた制御を行うようにしています。
このCameraSourceでは、カメラから引き渡されたフレームデータを解析し、バーコード検出を行うBarcodeScanningProcessorを保持しています。
BarcodeScanningProcessorでは、バーコードの情報を表示するBarcodeGraphicと、カメラの情報を表示するCameraImageGraphicを生成し、CameraSourceにこれらの情報を表示するように制御されています。
なお、バーコード検出にはML Kit forFirebaseの機能を使用していました。
ご覧のとおり、Camera API1を使用した実装では、複雑な構造でバーコードスキャンの機能を実現しています🥺
Camera API1からCameraXへ
しかしながら、Android 5.0未満(4.4)のサポートも v7.17.0で終了し、CameraX Jetpackサポートライブラリが出てしばらく経ったこと、 加えて、ML Kit forFirebaseに代わり、スタンドアロンのMLキットSDKが提供されたこともあり、長らくレガシーなAPIや精度的に不安定だったCamera API1から、CameraXへの移行を行いました。
CameraXでの実装
CameraXでのクラスはこんな感じになりました。
お、とてもシンプルになりましたね🥰
各種グラフィックを描画を全体的に管理するGraphicOverlayや、バーコードの情報を表示するBarcodeGraphicはほぼそのままで、BarcodeAnalyzerでバーコードの情報を表示するBarcodeGraphicを生成し、描画しています。バーコード検出にはスタンドアロンのMLキットSDKを使用しています。
また、カメラの起動などの管理を行うためのクラスであるCameraSourceでの処理はViewModel側に移管されることになりました。
val cameraProviderLiveData = MutableLiveData<ProcessCameraProvider>() fun requestCameraProvider() { val cameraProviderFuture = ProcessCameraProvider.getInstance(getApplication()) cameraProviderFuture.addListener({ try { cameraProviderLiveData.setValue(cameraProviderFuture.get()) } catch (e: Exception) { // エラー処理 } }, ContextCompat.getMainExecutor(getApplication())) }
Activityでは、次のようにカメラのプレビューや、BarcodeAnalyzerをImageAnalysisのアナライザに設定しています。 また、CameraXではライフサイクルに対応したカメラの管理をしてくれます。
fun setupCamera(provider: ProcessCameraProvider) { val preview = Preview.Builder() .build() val imageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), barcodeAnalyzer) barcodeAnalyzer.callback = viewModel.barcodeAnalyzerCallback try { provider.unbindAll() val cameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build() provider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis) // ライフサイクルを任せられる! preview.setSurfaceProvider(binding.scanBarcodeCamera.viewFinder.surfaceProvider) } catch (e: Exception) { // エラー処理 } }
おわりに
CameraXを使用すると、ライフサイクルを自動で処理してくれたり、端末によってさまざまなアスペクト比、画面の向き、回転などを考慮してくれたりと、煩雑になりがちなAndroidでのカメラ機能の実装を簡単に実装することができます。 コードラボで実際にコードを記述しながら、書くとこんな簡単にできるんだとびっくりしますよ!
codelabs.developers.google.com
最後に、ラクマでは、User Firstをコア・バリューの一つに掲げ、一緒にアプリ開発をしてくれるメンバーを募集しています。 とくに、Androidで我こそは!という方がいらっしゃったら、ぜひカジュアルにお話だけでもしてみませんか?