組み込みの現場でRTOSベースの既存システムをAndroidに移植しようとした時にありがちなこと

今の仕事は、いちおうAndroidをプラットフォームにした仕事ではあるのだけど、もともと組み込みのRTOSベースのシステムを移植するようなプロジェクトでシステムのアーキテクチャはAndroidというより"組み込み"の考え方。過去「スマートフォン開発もやっぱりデスマか」でも書いたのに似ていて以下のような感じ。

  • 他社から出てくるモジュールのJavaレイヤのI/FがC(Native)レイヤの関数をそのままラップしている
  • RTOSアーキ時代のタスクがそのままAndroidのSerivceになってる
  • Service間やActivityの間でやたらと非同期の呼出しや通知が多い
  • RTOS時代はこの方式で上手くいってたので、と押し切られる

他社から出てくるモジュールのJavaレイヤのI/FがC(Native)レイヤの関数をそのままラップしている

例えばJavaレイヤに、int xxxFunc(byte[] data) のようなI/Fが提供されて、引数byte[]に自分でバイナリデータを組み立てて(しかもビット単位で)呼び出さないといけない。nバイト目のn~mビット目にはxxを指定する、のような。

システム内で広く使われる予定のコアなモジュールの割にはI/Fが不親切。既存のNativeライブラリがそういうI/Fだからということらしいが、ヘルパーメソッドくらい用意すべきと思う。

こういうケースは、

  • 担当者がAndroid(というかJava)の経験が浅いか無い
  • 単に期間的、工数的に楽になる実装にしたかった(ラップするだけだものね)

であることが多いように思う。

でもそのI/Fの使用者がシステム内の各所にいるような重要なモジュールなら多少はヘルパーなり呼び出しやすいI/Fで提供して欲しいところなのだが、要請しても必要性が分からないとか「今までのシステム(RTOS)はこうだったから」と言われてお終い。

経験上、こういうひどいI/Fを出して来るときの一番多い言い訳が「呼び出し側の実装の自由度を考えた」というのだけど、いちいち引数組み立てたりしないといけない実装を各モジュールで作らせるのが自由度とは思わないけどもね。

こういうひどい設計は、上手く呼び出し側の他社も巻き込んで「使いにくいから考え直せ」と攻めてみるのが筋だろうけども…。

RTOSアーキ時代のタスクがそのままAndroidのSerivceになってる

xxxxTask -> xxxxServiceに名前が変わって再実装。

モジュール分割がRTOSと同じ感覚でちょっと細かすぎやしないだろうか、というケース。AndroidのServiceが乱立するのもね。RTOS時代から関わってる人には同じ感覚で仕事できるからいいのかもしれないけど。

Service間やActivityの間でやたらと非同期の呼出しや通知が多い

RTOSのタスク間メッセージと同じ感覚でI/F設計してしまうから。

→"xx要求"
←"xx受付通知"(相手側が受け付けましたという非同期通知)
←"xx応答"(相手の処理が完了して結果が乗ってくる通知)
でワンセットになってたりするケース。

応答を受けるまでは次の要求を投げてもエラーになります、キューイングはしないので呼び出し側で管理してください、が割合多い。

確かに、ハードウェアまでアクセスするために応答時間が数100msかかる可能性があるので非同期処理、というのは組み込みでは多い。

RTOSならセマフォやシグナルとか駆使して実装するのがセオリーだろうけど、Androidだとメインスレッドはブロックできないし、I/Fを使う側としては呼び出しと応答後の続きの処理がソースコード内の離れたところになってしまう。なのでソースコードを見たときに処理が見通しにくい。

多少、ブロッキングするメソッドであっても同期型でI/Fを用意してもらって、使う側でスレッド立ててその中で呼び出すなりできるけどなぁとは思うけれど正解がよく分からない。

ただ、コールバックやスレッドを上手く設計しておかないと後からバグだらけの懸念。内部の状態管理もきちんとしないといけないだろうし。

RTOS時代はこの方式で上手くいってたので、と押し切られる

…今回のプラットフォームはAndroidなんですけど…?

開発メンバーにRTOS時代からずっと関わってる人が多いと、「今までのやりかたが最強」ということになりがち。上に書いてるように、モジュール構成とかI/Fの考え方を既存のRTOSでのシステムからそっくり持ってくるからってことなんだろうけど。

でもAndroidのアーキテクチャと合わない設計をして無理やり作り込んで後で火を噴かなければいいけども…。ActivityとかServiceのライフサイクルは考え方としてそれなりに特殊だし、Android Frameworkとの兼ね合いもどう付けるのか…。たぶん火を噴いた時にはさらにパッチ的な回避をしてどんどん複雑化していくのだろうな。

こういう感じのシステムだと

もはや作っているものはAndroidというよりRTOSの仕事をしているのと大差が無くなってしまっている。よく分からないxx管理タスクと先にやりとりしないとアプリの起動ができないとか、シーケンスを引いていくとアプリ層やService層でやたらとあちこちの"タスク"に対して複雑なシーケンスをこなさないと処理が進まない仕組みに。

いろいろ改善を試みて一部はとりあってもらったものもあるけど、もう既に各社で設計が進んでしまっててひっくり返すには遅すぎるし、プロジェクト内のパワーバランス的にこちら側の力が足りない。

たぶんこれは結合試験以降の工程で大変なことになりそうだ…。

1つの処理に対してシーケンスが多いからユーザ目線でのレスポンシビリティーが心配だとか、クロスシーケンスを各社どこまで事前にケアできてるかとか(下位レイヤは単なるラッパーレベルでしか作ってこないだろうから期待薄)、どこかにしわ寄せが来そうな気がする。

携帯電話開発の現場でも、ガラケーからAndroidスマホに開発がシフトしていく時に通った道ではあるけど、これからAndroidの採用が見込まれている色んなデバイスの開発現場は「Androidでの初物」の開発では同じ道を通っていくのだろうか。

せっかくAndroid経験のあるメンバーを投入しても、もう少しアーキテクチャや設計の考え方には、既存システムの開発に長けていて自負があったとしても耳を傾けたほうが良いと思う。数年前と違ってAndroidのエンジニアは大きく増えているしAndroidのノウハウを持った人間もたくさんいるのに、その意見を頭から潰していたら良い物はできないと思う。

既存のRTOSの資産やノウハウを流用するとしてもAndroidに載せたときにどうするのが良いのか折衷案は出せると思うし、あとあと火を噴かないために注意すべきこととかは上流工程で検討しておくのが良いはずだけど。

往々にして開発スケジュールに余裕が無いからなどの理由で既存をベースに流用すればなんとかいけるだろう、というのはどこでもある話だけれども、それで進めると設計、実装、単体テストあたりまでは一見うまくいく。けど、各モジュールを結合していくとシーケンスの不備、Android特有の動作、動いてもモッサリ、メモリ食い、Low Memory Killerで殺される現象などにきっと泣くことになる…と思うなぁ。

タイトルとURLをコピーしました