はじめに
はじめまして!この度Nefrockの2024年度インターンシップに参加する機会をいただきました、東京工業大学東京科学大学 修士1年の大谷です。
インターンシップのテーマはAndroid上で動作する照合アプリの開発でした。今回初めてAndroidアプリ開発を行うということで少し不安でしたが、結果的には非常に多くのことを学ぶことができました。この記事では完成したアプリと実装手順について紹介したいと思います。
インターンのテーマ
EdgeOCRについて
EdgeOCRはNefrockが提供しているハイスピードOCR(Optical Character Recognition)です。AIを用いて高速かつ高精度に文字を読み取ることが可能であり、他のアプリとも柔軟に連携できるように設計されています。
詳しくは以下の記事をご覧ください。
今回実装したアプリ
今回のインターンでは、EdgeOCRアプリと連携する照合アプリを開発しました。このアプリは主に倉庫での入出庫や在庫管理の現場での利用が想定され、検品表と実物の製品との照合を素早く行うことを目的としています。
検品表とは、製品情報とその個数を一覧にしたものです。たとえば下の図のようになります。
倉庫からの出庫で利用する場合、検品表は出庫すべき製品の情報とその個数の一覧ということになります。製品の情報は製品ID、Lot番号などの複数のキーワードからなり、出庫の際は実物の製品のうちキーワードが全て一致するものを、検品表に書かれた個数分だけ選ぶ必要があります。
照合アプリは、EdgeOCRの文字認識を利用してこのようなタスクを効率よく行うことを目指したアプリです。以下のような手順で使用します。
- 検品表に書かれた製品の情報(キーワード)をアプリに登録する.
- 実物の製品のキーワードをスキャンし, 登録しておいた製品のキーワードと一致したものの個数をカウントする.
検品表や実物の製品のキーワードのスキャンにEdgeOCRによる文字認識を利用し、照合アプリは製品の一致判定や個数管理を担当します。
インターンのテーマは、Android上で動作する照合アプリを実装することでした。続いて実装したアプリについて説明します。
成果物
実装した照合アプリのメイン画面はこのようになります。画面の上半分に、登録した製品の情報とこれまで読み取った個数が表示されています。真ん中右の+ボタンを押すと製品の登録ができ、下の読取ボタンを押すと照合ができます。
読取ボタンを押すとEdgeOCRアプリを呼び出します。EdgeOCRは複数文字の同時読みもサポートしているので、それを利用して複数のキーワードを一回のスキャンで読み取ることができます。
一致する製品が見つかったらメイン画面に反映されます。
一致するものがない場合はスキャンされたキーワードが画面に通知されます。
スキャンそのものが失敗する場合もあります(EdgeOCRアプリがインストールされていないなど)。その場合は画面下に通知されます。
+ボタンを押すと製品の登録ができます。キーワードをスキャンまたは手入力し、登録ボタンを押すと製品一覧に反映されます。
照合が終わった製品の削除もできます。
ここまでキーワードとして製品IDとLot番号のみを考えていましたが、当然他のキーワードも考えられます。キーワードの追加は設定画面で行います。
各キーワードをクリックするとキーワードの表示名やスキャン定義ID、シナリオIDを設定できます。詳細は割愛しますが、スキャン定義ID、シナリオIDはEdgeOCR側の読み取り方法(キーワードのフォーマットなど)を指定するためのIDです。
主要な画面をまとめるとこのようになります。
続いて実装手順を説明します。
実装手順
アプリの仕様を定義する
コードを書き始める前に、アプリの仕様を定義する必要があります。インターン初日に、照合アプリの要件が箇条書き形式で与えられます。以下は実際の要件の一部です。
- 照合アプリからEdgeOCRアプリを起動して文字を読み取る
- 結果を照合アプリで確認できる
- 照合方法は...
これを仕様として落とし込むために、要件から想像できるアプリのUIをGoogle Slides等で作成し、そこに機能を書き込んでいきます。下の図は実際に作成したものです。
作成した図をもとに、社員の方からフィードバックをもらいながら仕様を具体化していきます。ある程度仕様が固まり、アプリのイメージがついたら実装に移ります。
実装
今回の実装にはJetpack Composeというフレームワークを使用します。Jetpack Composeは、Googleが提供するAndroidアプリのUI開発用ツールキットです。SwiftUIやReactなどと同じく宣言型UIを採用しており、コンポーザブル関数の組み合わせによって再利用可能なUIを簡潔に構築できます。
私は今回のインターンで初めてJetpack Composeを使うので、照合アプリを実装する前にまずは公式のチュートリアルを写経しました。非常にわかりやすく書かれているのでこれからAndroidアプリ開発をしてみたい人にはおすすめです。
チュートリアルをある程度まで進めたら照合アプリの実装に入ります。といってもあとは仕様を見ながら頑張って実装するだけです。
私の場合は、以下のような順序で実装しました。
- 各画面のUIと画面遷移
- データの永続化
- EdgeOCRアプリとの連携
- 細かい機能追加、バグ修正
上3つについてはチュートリアルを参照したり社員の方に聞いたりすることで特に詰まることなく実装できました。苦労したのは4つ目で、細かい機能を追加している最中に上3つで埋め込んでしまったバグが顕在化して、その解決に時間を取られました。
特に苦労した部分についてこの後少し触れたいと思います。
開発中の課題と解決方法
アプリの仕様定義
インターン中はほとんどアプリの実装をしていたため、実装以外で苦労した部分はあまりなかったのですが、アプリの要求を仕様に落とし込む部分は少し苦労しました。説明を受けただけではどのようなアプリにすべきか全くイメージがわかなかったためです。
仕様定義の部分でも述べたように、これについては最初から完璧なものを作るのではなく、まずは大まかな仕様を作った後に少しずつブラッシュアップしていくイメージを持つことで上手く進めることができました。
Jetpack Composeの状態管理
状態とは、今回のアプリでいえば製品のリストやその個数、あるいは入力中のテキストフィールドの値などが該当します。Jetpack Composeでは状態管理のための複数のAPI(remember
, mutableState
, ViewModel
等)があり、対象の状態に応じて使い分ける必要があります。
またViewModel
については特有のルールがいくつかあり、チュートリアルにはほとんど説明がないので別でドキュメントを読む必要があります。
私の場合、これらのAPIをよく理解しないまま使っていたことでバグを埋め込んでしまっていました。
実際に起こったバグの内容(クリックすると展開されます)
公式のチュートリアルによるとViewModelを引数に取るコンポーザブル関数の定義方法は以下のとおりです。ただしSampleViewModel
は適当なユーザー定義のViewModelだとします。
@Composable fun SampleScreen(viewModel: SampleViewModel = viewModel()){ ... }
私はviewModel()
がコンストラクタ呼び出しだと思い込み、代わりに以下のようなコードを書いていました(viewModel()
をSampleViewModel()に変えた)。
@Composable fun SampleScreen(viewModel: SampleViewModel = SampleViewModel()){ ... }
SampleViewModel
のコンストラクタが引数を取らない場合にはviewModel()
でも良かったのですが、引数を取る場合はviewModel()
が渡せなかったので機械的にコンストラクタを書いていました。
しかし、この定義ではSampleScreen
の呼び出しのたびに新しいSampleViewModel
のインスタンスが作られるので、各UI要素がそれぞれ異なるViewModelインスタンスを参照してしまい、状態の整合が取れなくなります。
実際は引数に渡しているのはファクトリメソッドで、現在のスコープで初めて呼ばれたときにのみSampleViewModel
はインスタンス化されるようになっています。コンストラクタが引数を取る場合にはカスタムファクトリを渡す必要があります。
このバグについては社員の方に指摘していただいたことで発見できたのですが、1人では気づけなかったと思います。
これまで私は新しいフレームワークを学ぶとき、基本的な使い方をある程度覚えたら、原理を完全に理解できていなくても実践に移るようにしていました。しかし今回の経験から、実践を積んだ後に原理を理解する時間を取ることも大切だと学びました。
おわりに
今回、実際の現場で使用されるアプリがテーマだったため、ユーザーが使いやすいデザインや操作性について考える機会が多かったように思います。また、将来的な機能追加も想定されていたため、クラスの設計について議論する場面もあり、勉強になりました。
Jetpack Composeに触れるのは初めてでしたが、質問すると丁寧に教えていただける環境だったので大きく詰まることなく開発を進められたと思います。最終的には社内発表で「よくできている」と褒めていただけるレベルまで完成させることができ、自信につながりました。1人ではここまで来られなかったと思うので、本当に感謝しかありません。
最後になりますが、インターン生である自分を暖かく迎えてくださったNefrock関係者の皆様、メンターとして相談に乗ってくださった賀来さん、ありがとうございました!