【Android】Google MLKit & Android X Cameraを使用してAndroidの高速QRコードスキャンアプリを実装
コードの書き方を直接見たい場合は:コード解析へ
このプロジェクトでは、Kotlin、GoogleのMLKit、およびネイティブのAndroidXカメラを使用してQRスキャナーを実装しています
過去には、いくつかのサードパーティライブラリを使用してQRコードスキャナーを作成していました
例えばzxingやzbarなどです
過去のプロジェクトで、現在のQRコードスキャナーの精度がそれほど高くないというQAのフィードバックを受けました
そこで調査し、実際にテストを行った結果、なぜ精度がそれほど高くない
のかがわかりました
実際の原因は:
プロジェクトで使用していたのは古いandroid.hardware.Camera
カメラとzbar
を組み合わせてQRコードを解析していたためです
QRコードからの距離が遠かったり、斜めの角度からスキャンしていたりしました
そのため、このようなQRコードスキャナーはiOSほど速く感じられませんでした
そこで今回は、Googleのmlkit
を使用して実装してみることにしました
- このAPIは
Android API 21
以上が必要です - このAPIは2つのバージョンを提供しています
一つはgmsから動的にモジュールをダウンロードするもの
もう一つはライブラリに組み込まれたモジュールを使用するものです。以下の図のように: - 私は新しいネイティブカメラライブラリ
androidx.camera
を使用してML Kit
と組み合わせて使用する例を参考にしました
元のzbarとandroid.hardware.Camera
を組み合わせて使用するプロジェクトと比較して:
↪ どちらのカメラライブラリもAndroid API 21以上でサポートされています
↪ zbarとandroid.hardware.Camera
を組み合わせて使用するバージョンのコードには3つのワークアラウンドがあります
これは元々このプロジェクトを書いた人や以前の例が、アクティビティ内でカメラの初期化問題を解決するために以下のような書き方を採用していたためだと推測されます:
↪ 実際に見てみると、android.hardware.Camera
はスレッドセーフではなく、後に@Deprecatedになっています
↪ 一方、新しいandroidx.camera.core.Camera
はライフサイクルを持っており、カメラのライフサイクル問題を処理できます
これにより、いくつかの奇妙なライフサイクル問題を回避できます
✅ したがって、新しいandroidx.camera.core.Camera
に切り替えることで、ライフサイクルの問題をより効果的に回避できると推測されます(自分で制御できるため) - 新しいandroidx.camera.core.Cameraはライフサイクルを持っており、カメラのライフサイクル問題を処理でき、いくつかの奇妙なライフサイクル問題を回避できます
- スキャナーのデコードにはMLkitのBarcodeScannerOptionsを使用します
これにより、解析可能なバーコードの種類が増えました
サポートされているフォーマットについてはこのURLを参照してください。以下はスキャナーのデコードコードです:
-
LG K520 Android 11
でテスト LG K520 Android 11
で実際にテストしたところ、カメラの全体的な動作時間に大きな差はありませんでした。
実際には、カメラの初期化
からQRコードの解析
まで約1秒かかります。
しかし、ライフサイクルによるカメラのクラッシュリスク
を回避できます。-
実際のテストでは、元の方法よりも応答速度が少し速くなりました(良いスマホでは違いがわかりにくく、体感は小さいです)。
例えば、zbar
+android.hardware.Camera
は完全にフォーカスが完了
してからQRコードの解析を開始しますが、
mlkit
+androidx.camera.core.Camera
はフォーカスが半分
完了した時点でQRコードの解析を開始できます。 - 左の画像は
zbar
を使用、右の画像はML kit
を使用しています(このGIFは0.25倍
に減速しています)。 androidx.camera.core.Camera
はライフサイクルに関連する問題を処理できます。
新しい方法では0.25倍
に減速して見ると、体感的には少し待ってからカメラが表示される
(右の画像)ようになります。- しかし、
より感じられる
のは、奇妙な角度やカメラが遠い場合でもスキャンしやすくなることです。
- ImageAnalysisを作成する際に目標解像度設定を追加します
.setTargetResolution(Size(screenResolutionForCamera.x, screenResolutionForCamera.y))
- ここで、screenResolutionForCameraは画面解像度を使用してカメラの目標解像度を設定します。
カメラの目標解像度を設定するかどうかの違いは、カメラが遠くからスキャンする成功率が高い
ことです。 補足
:このAPIのJavaドキュメントには以下のことが記載されています。- 目標解像度を設定しない場合、カメラのデフォルト解像度は
640*480
です。 - 設定後も優先順位に従って実際の
解像度
が選択されます。 - ここでは概要を示します:設定に近く、かつ大きい目標解像度を優先的に選択します。次に、設定に近く、かつ小さい目標解像度を選択します。その際、解像度の比率は提供された
サイズ
によって優先的に決定されます。 - したがって、
ハードウェアの制約
により、各デバイスで解析できる距離
の程度が異なる可能性があります。
- 目標解像度を設定しない場合、カメラのデフォルト解像度は
- 上記の調整と最適化を行った後、QAの同僚にテストを依頼しました。
彼のフィードバックによると、以前提供したアプリよりも精度が向上したとのことです。
詳細を尋ねると、側面
や遠くからのスキャン成功率
や検出される確率
が確かに高くなったと言っていました。 これで一つの問題が解決しましたXD
-
上記の分析を読んでいただきありがとうございます。
このページの最上部にソースコードを掲載しました。
興味がある方は直接クローンして変更してみてください。 -
以下では、主なQRコードスキャナーのコード解析を共有します。
興味がある方は参考にしてください。
コード解説
BaseCameraLifecycleObserverクラスは、他のクラスが継承するための基本的なクラスです。基本的なカメラの初期化内容が含まれています。
また、ライフサイクルに関連する内容も処理しています。
(例:ライフサイクルに対応したカメラの処理)
また、カメラを手動でオンまたはオフにするためのメソッドも公開しています。
cameraProviderDeferred
特筆すべきは、ここでcameraProviderDeferredを使用していることです。これはProcessCameraProviderを取得するためのものです。
この操作は時間がかかるため、非同期操作を使用して柔軟に対応しています。
(ただし、ProcessCameraProviderを直接インスタンス化することも試しました。
ほとんどの場合、ANRやフリーズの問題は発生しませんが、
古いデバイス(例:Android 5.0)では稀に1、2回発生することがあります。)
ここでの概念は
CompletableDeferredの中で.applyを使用していることです
applyの範囲内でProcessCameraProviderを作成します
必要な場所で`await()`を使用して待機します
非同期操作を行いながら、実際に作成されるのを待って操作を行うことができます
CompletableDeferredと一般的なDeferredの違いは
complete()を使用して手動で完了のタイミングを制御できることです
この例に適しています
ProcessCameraProviderのリスナーが返されるのを待つ必要があります
初期化が完了したことを知るためです
コード解説
ScannerLifecycleObserverは比較的シンプルですBaseCameraLifecycleObserverを継承しています
主にプロジェクトの要件に応じて必要な機能をいくつか記述しています
例えば、いくつかの拡張内容
getCameraDisplayOrientationのようなものです
カメラがサポートする解像度を調整するために使用できます
またはgetImageAnalysisでImageAnalysisを取得します
ImageAnalysisは主にMLKitでどのバーコードタイプを解析できるかを設定するために使用されます
コード解説
ScannerAnalyzerはバーコードを解析するための実装ですBaseCameraLifecycleObserverを継承しています
QRコードを解析するためにFORMAT_QR_CODEを使用しています