Android
Meteor

Meteorの中を理解するために、Rocket.Chat.Android をつくってみたときの留意点をいくつか。

More than 3 years have passed since last update.

この記事はクラウドワークス Advent Calendar 2015の10日目の記事です。

クラウドワークスではAndroidアプリ開発を手がけているのですが、

そんなエンジニアは普段どんな思考/志向で過ごしているのかを少し共有すべく、趣味の時間で開発しているRocket.Chatのネタをあえて書きます。


Rocket.Chat.Androidをつくるモチベーション


表向きの理由


  • 自分が Rocket.Chat を使っていて、アプリが欲しかった

  • Rocket.Chat.Androidは開発が進められておらず、issueまで挙げられていた始末

→これはOSSへのコントリビューションのチャンスだ!!


個人的な理由

まぁ、エンジニアなら「理由もなく」やりたいと思うことが少なからずありますよね・・・w

Meteorの中の仕組みを知りたい というのが大きなの理由ですw

「大規模なOSSを理解する」には以下の2通りのアプローチがあるかなーと思っていて、私の性分としてはあきらかに後者を好む傾向にあります。


  • OSSを使うことで理解する

  • OSSをカスタマイズ/ポーティングすることで理解する

たとえば、Androidを理解するにも、アプリを10個作るより、frameworks/base/services/core/java/com/android の下あたりを読み漁って、てきとうに改造したROMを作ったほうがはるかに勉強になるかなーと思っています。

なので、Meteorも同じように中を読み漁って自分でAndroid版を車輪の再発明しつつ勉強していきたいなぁと思ったのです。


こんなアプリです

test

 

 


開発時に留意したこと

すみません、ようやく本題です。

Rocket.Chatをアプリ化するときに、気をつけたこととか共有してみたいことを書いていきます。


リアルタイム&リアクティブを実現するために…

メッセンジャーてきなものをつくるときに、ぱっと思いつくのは

てきとうなActivityなりFragmentがロードされた時にAPI subscribeして、コールバックを受けたらアダプターを更新しよう、というやり方です。

よくない例

しかし、このやり方は


  • コールバック地獄をActivityやFragmentに書かないといけず、Activity/FragmentがFatになっていく

  • メッセージのリストはActivityやFragmentを経由しないと書いたり消したりできない(サイドバーのXXXを押したらシステムメッセージを追加する、みたいなことをやるにも、Fragmentにたいして要求を出して、FragmentがListAdapterに要素を追加して、という構成をとらないといけない)

  • ListViewに表示するAdapterのデータが全部メモリ上にあるので、無数のメッセージを表示するにはメモリ的に不安

などなど、デメリットが多いです。(とくに1つめがヤバイ)


ModelとViewを分けよう

ようは、リストビューで表示するデータはDBで外部化し、Activity/FragmentはCursorAdapterでそれを表示するだけ、にしよう、ということです。

Modelを外部化

こういう構成にするだけで、


  • CursorAdapterはデータをDBに持っているため、ListViewで表示したいデータ量が増えてもメモリ使用量が青天井にふえていく、ということがなくなります

  • どこからでもDBに書ける構成となることで、APIのハンドリング部分もActivity/Fragment内にもつ必要がなくなり、Fat Activity/Fragment問題はだいぶ解決します


コールバック地獄をそもそも回避するために、Bolts/RxJavaを使おう

WebAPIを使うとき、

とりあえずConnectして

 →成功したらログインして

  →成功したらデータを取得して

みたいなコールバック地獄になりがちで、Boltsなどで回避するのが一般的ですね。

それに加えて、MeteorはDDPというWebSocketベースの独自プロトコルを使っていて、ざっくりいうと↓のように、コールバックがごちゃまぜで(IDで識別できるだけの形で)返ってきます。

Slice 2.png

「このコールバックってどのリクエストに対するものだっけ?」とかHashMapとかでテーブル持ってif文で分岐させて、とかやってたら恐ろしいことになりますね。

こういうときは、RxJavaを使ってコールバックを選択的に拾うとすっきりさせることができますね。

Slice 3.png

「メッセージを50件取得する」とか「アクティブユーザをとる」とかREST APIっぽく使いたいメソッドについては、↑のようなObservable/subscribeの処理を内包したBoltsタスクをつくることで、いちいちunsubscribeの世話を、API利用者が意識しなくてもいいようできます。


APIコールはすべてDBを通しておこなおう

画面はなるべく「DBにあるものを表示するだけ」にしたい、という考えのもと、ログインとかメッセージ送信とかも、すべてDBの変化通知を通して行うようにします。

Slice 1.png

たとえばメッセージ送信の場合は、↑のようにDBの状態に応じた処理を、画面/オブザーバにそれぞれ書くだけでよいのです。

こうすることで、Activiy/Fragmentなどで「いまメッセージ送信中だっけ・・・汗」みたいな考慮が一切いらなくなり、だいぶ実装がすっきりするはずです。


対応バージョン

世の中的には、Android 4.0(ICS)以上としているものが多いですが、Rocket.Chat.AndroidはAndroid 4.1(JB)以上です。

(じつはクラウドワークスのAndroidアプリも最初はJB以降しかサポートしていませんでした)

理由は、



  • ContentObserverのコールバックでUriが渡せるのがAndroid 4.1以降じゃないと使えない


  • adb shell content query --uri content://… の(デバッグ用)コマンドがAndroid 4.1以降じゃないと使えない

という、(全く利用者目線ではない)なんともしょぼい理由ですw

APIコールをDBの変化通知ベースでやってるからじゃないか!という声が聞こえてきそうですが、まぁ許してください。

 

 

 


おわりに

メッセンジャーてきなアプリをつくるさいには若干参考にしてもらえるかなーというネタを書いてみました。

Meteor はSEOのためにキーワードを並べただけか?!ってくらい、触れられてなくてすみません。

DDPのプロトコルとかGitHub OAuthをAndroidで実装したりしてるのですが、あまりにマニアックすぎるので、記事では書きませんでした。

 

 

最後に、クラウドワークスでは、エンジニアを募集しています!

一緒に楽しく働いてみたい!と思った方は、まずは、ぜひお気軽にランチなどしましょう!