Androidフレームワーク周りのサービスをRPCする仕組みメモ

Androidのフレームワーク周りを見ていると、なんちゃらNative、なんちゃらProxyがいっぱい出てきます。
そして、たとえばActivityManagerServiceを停止させるshutdownメソッドはインターフェイスIActivityManagerで定義されており、ActivityManagerServiceで実装されていて、ActivityManagerNativeでも実装されてるし、ActivityManagerProxyでも実装されています。

どれがどれやら混乱したので自分用にメモしました。

Androidではプログラム毎にプロセスが異なります。
そのため、他のプログラムからサービスの機能を呼び出そうとするとプロセス間通信を行う必要があります。このややこしい仕組みはリモートプロシージャコール(以後RPC)を意識せずAndroidのサービスを扱うためのものです。

ActivityManagerの場合、クラス図は次のような感じです。他のサービスも同じ仕組みでRPCを実現していました。

binder

各クラスの目的は次のような感じです。

  • IActivityManager – ActivityManagerのインターフェイスを定義
  • ActivityManagerProxy – プロセス間通信が必要な場合、このProxy内部でRPCを行い、プロセス間通信をしていることを隠す
  • ActivityManagerNative – ProxyからのRPC要求を受け取り、実装クラス(ActivityManagerService)のメソッドを実行し、結果を返す処理を実装
  • ActivityManagerService – ActivityManagerの処理を実装

ActivityManagerを利用する場合、次のようにしてインスタンスを取得します。

IActivityManager  am = ActivityManagerNative.getDefault();

Binderの機能により、プロセス間通信の必要が無い場合(ActivityManagerのプロセスから実行した場合)は、ActivityManagerServiceを返します。
プロセス間通信が必要な場合(ActivityManagerのプロセス以外から実行した場合)は、ActivityManagerProxyを生成し返します。

プロセス間通信が必要ない場合はActivityManagerServiceで実装したメソッドはそのまま直接実行されます。
プロセス間通信が必要な場合はProxyのメソッドを実行することになり、Proxyの中でBinderのRPC機能を利用してActivityManagerのプロセスにあるNativeのインスタンスを経由してActivityManagerServiceのメソッドを実行します。

この仕組みがあるおかげでRPCしている事を意識せず各サービスを利用できるわけですね。便利!ありがとうBinder!

RPCを担当している裏にはServiceManagerやBinderがいるのですが、複雑なのでまだちゃんと理解はしていません。
ここもきっちり理解しないとモヤモヤ感が消えず、何となく理解程度で止まりそうです。

ネットにより加速する、世論における正の反応度フィードバック

現在京都の実家に戻っていると言うことで、昨日は京都時代の友人と飲みに行ってきました。
その友人はあらゆる事に問題意識を持っており、話すたびに新しい視点での意見を聞くことができるので、一緒に飲むのは京都の実家に戻った際の楽しみの一つです。

そんな彼は、昨日このようなことを言っていました。

「あるお婆さんが、ネットで見かけるような攻撃的な右寄りの発言をしているのを聞いてびっくりした。5年前はネット世界の住人の間だけで盛り上がっていた嫌韓が、リアルの世界にも広まりつつある」

このお婆さんだけに限らず、周りの人間が右傾化している事を懸念しており、その原因がネットであると言うことです。

ネットは、TVや新聞と違い、取得する情報を自分で選択することができます。
そのため、自分にとって都合の良い情報だけを集めてしまい、思想が一方向に極端に傾きやすいのでは無いかと言うことでした。

それは、正常な状態から外れるとますます正常な状態から遠ざかるよう動いてしまう正の反応度フィードバックによる影響で破滅的な結果になってしまったチェルノブイリのRBMK型原子炉と同じように、日本にも破滅的な結果を招くのでは無いかと言うことです。

この話をしているとき、村上春樹の「沈黙」を思い出しました。

僕が本当に怖いと思うのは、青木のような人間の言いぶんを
無批判に受け入れて、そのまま信じてしまう連中です。

自分では何も生み出さず、何も理解していないくせに、
口当たりの良い、受け入れやすい他人の意見に踊らされて集団で行動する連中です。

彼らは自分が何か間違ったことをしてるんじゃないかなんて、
これっぽっちも、ちらっとでも考えたりはしないんです。

この友人が懸念している事柄には、次の二つの性質が関係していると考えています。

  • 人は自分に都合の良い情報だけを受け入れる性質がある。
  • そのため、都合の良い情報だけを集めることができるネットには、思想を一方向に加速しやすい性質がある。

これにより、アラブの春的な、ネットがきっかけによる大きな動きになってしまうのでは無いかと少し不安です。

残念ながら、その場では具体的にどのようにしたらそのような動きを止めることができるか、良いアイデアは思い浮かびませんでした。

しかし、どのような情報に対しても、それが本当なのか、それを信じて良いのかどうか疑いながら情報を取得するというのを意識する必要があると改めて考えさせられた夜でした。

pushStateでURLを書き換える

Rails4でturbolinksと言う機能が増えました。
これは、Rails4上のリンクをクリックすると、実際には画面遷移せず、Ajaxでコンテンツだけ取得してbodyを書き換え、より高速に画面を切り替えるという機能です。

詳しくはこちら。
Rails 4のturbolinksについて最低でも知っておきたい事

で、実際に動かしてみるとURLも普通に書き換わるし、戻る進むボタンも有効になっているし、普通の画面遷移と見分けがつきません。

JavaScriptによるURLの書き換えはwindow.location.hashによるアンカーしか変更できなかったはずでは・・。
と思ったら、HTML5からpushState、popStateという機能が追加され、JavaScriptからURLの書き換えができるようになっていました。

ということで早速やってみました。
適当すぎるデモはこちら。
デモではボタンを押すとURLが変わりますが、実際には画面遷移していません。

参考にしたのは「AjaxでもURLを更新して履歴を作れるHTML5のpushState試してみた

以下のコードでURLが変わります。何という簡単さ!

window.history.pushState(null,null,"URL");

戻るボタンなどは次のコードで対応できます。

  onpopstate = function(event) {
    // 戻る、進むボタンで呼び出される。
    // location.pathnameでURLがとれるので、そのURLをみて適切なコンテンツを表示
  }

簡単ですね!

Androidでメモリリークの調査と、そのヒープダンプから画像を抽出する

Androidアプリを開発していると、画像の取り扱いでメモリリークしていたり、OutOfMemoryErrorが発生する事があります。
そのような場合は、Javaヒープメモリのダンプを取り、どのBitmapがメモリリークしているか、または無駄にメモリを食っていないかを確認し対応します。また、Javaヒープダンプから画像データを抽出し、具体的にそのオブジェクトがどの画像かを確認します。

この記事でしていること。
・Androidアプリのヒープダンプを取得してメモリリークの調査。
・ヒープダンプからBitmapを復元し画像を確認。

用意するもの。
・DDMSでヒープダンプを取得するためAndroid SDK
・ヒープダンプを解析するためのMemory Analyzer(MAT)
・ヒープダンプから抽出した画像を確認するためのGIMP

まずヒープダンプを取りましょう。
解析対象のプログラムを動作させた状態で、USBをつなぎます。
解析対象のプログラムはdebuggableをtrueにしておく必要があります。
そしてDDMSからそのプログラムを選択し、ヒープダンプ取得ボタンを選択します。
memleak1
するとヒープダンプとしてhprofファイルを保存することができます。

AndroidのヒープダンプはAndroid用のフォーマットのため、そのままではMATでhprofファイルを開くことができません。
そこでフォーマットを変換してやる必要があります。
adt-bundle-windows-x86\sdk\toolsにある、hprof-conv.exeを使い変換します。
引数は次のような感じで、入力ファイルと変換後の出力ファイルを指定します。

HOGE> hprof-conv.exe com.example.memoryleaktest.hprof memleakcheck.hprof

変換が終わったらMATのメニューから、[File]-[Open Heap Dump…]で変換したhprofファイルを開きます。
次のダイアログが表示されますが、そのままFinishでOK。
memleak2

そしてDoninator Treeを開きます。
するとメモリを90%使ってる怪しいやつが・・・。
memleak3

ツリーを開くとBitmapのインスタンスと判明。
なぜメモリが解放されないかというと、そのインスタンスへの参照を誰かが保持してためなので、それを確認します。
byte配列を右クリックし、[Path To GC Roots]-[exclude weak/soft references]を選択します。
memleak5
するとbitmapListから参照されているので解放されないと言うのがわかります。

次に、このbyte配列が具体的にどんな画像かを確認します。
byte配列を右クリックして[Copy]-[Save Value To File]を選択。
memleak4

保存したファイルをGIMPで開きます。
開く際はファイル形式の選択でRaw画像データを選択します。

画像の種類はRGBアルファを選択します。
なんだか画像が崩れてますね。
memleak6

幅を変更していくと・・・。
memleak7

無事に画像が復元できました。実際のバグで画像を確認できると「犯人はおまえかぁっ!」となり、後はハッピーエンドですね。
memleak8

ちなみに今回メモリリークさせたのは次のコードでした。再現コードにしては適当すぎてアレですが・・・。
Bitmapを生成して、それをリストに入れて放置。当然GCされませんね。


for(int i = 0 ; i < 100 ; i++){ Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.android); bitmapList.add(bitmap); }[/java]

ruby-modeで{}を入力しようとするとSymbol’s value as variable is void: last-command-charと怒られるのを解決する

Macのemacsをemacs24.3にあげてrubyを書こうとすると、{}を入力する際に「Symbol’s value as variable is void: last-command-char」とエラーが出て{}を入力することができなくなってしまった。

ruby-mode.elにあるlast-command-charが、24.1.50から無くなってしまった事による影響らしい。

対応版のruby-mode.elがここにあるので、落とすと解決した。

以下のコマンドでチェックアウトして、misc以下にruby-mode.elはある。
$ svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby

Emacs24で追加されたパッケージ管理機能をProxy内から使う

Emacs24から、M-x list-packagesでパッケージ管理機能を呼び出すことができる。
リストから選ぶだけでパッケージをダウンロード&インストールすることができる便利機能である。

便利な起動だがネットワークに接続するので、Proxyの設定が必要な環境だと.emacsファイルなど適切な設定ファイルに設定してあげる必要がある。

次のような感じでOK。

(setq url-proxy-services '(("http" . "hoge.jp:8080")))

UbuntuでNICを認識しない場合の対応

Ubuntuをインストールしても、有線LANがつながらない場合がある。
そんな場合はドライバが入っていない可能性があるので、入れるとOKだ。

まずはLANがつながらない理由が、NICを認識していないかどうかの確認のため、次のコマンドを実行。
$ ifconfig

127.0.0.1のローカルループバックのみしか表示されていなければ、それはNICを認識していないことになる。ここに有線LANまたは無線LANが表示されていれば、つながらない理由は他にあるので今回の対応方法以外の解決策が必要だ。

ローカルループバックのみ表示されているならば、今回の手順でドライバを入れればOKとなる。
次に何のドライバが必要か確認する。次のコマンドを実行すると、イーサネットのチップとして何を使っているかが確認できるので、メーカーからドライバをダウンロードしよう。
$ lspci | grep ‘Ethernet|Network’

今回はIntel Corporation Device 1503と表示されたので、Intelからドライバをダウンロードする。
有線LANはこちら。無線LANはこちら

今回は有線LANのため、e1000e-2.2.14.tar.gzをダウンロード。
他のマシンでダウンロードし、LinuxマシンにUSBメモリなどでコピーする。
ファイルを解凍し、srcディレクトリに移動した後次のコマンドを実行する。
$ sudo make install
$ sudo modprobe -r e1000e
$ sudo modprobe e1000e

これでドライバが入ったはずだ。確認は次のコマンドで行う。例のようにe1000eが表示されればOK。
$ lsmod | grep e1000e
e1000e 158424 0

再びifconfigを実行して、NICが認識されていればOK!

AndroidとMacでUSBテザリング

AndroidのテザリングにはUSB、Wifi、Bluetoothの3種類ある。
安定度はもちろんUSBによる有線接続が一番なのだけど、MacはUSBのドライバがないためWifiかBluetooth経由のテザリングしかできなかった。

PdaNetなどのテザリングソフトを使うとMacでもUSBテザリングを実現できるものもあったけど、PdaNetは有線接続するドライバを入れるとADBドライバが使用不能になるという致命的なデメリットがあって使わなくなってしまった。

そのため、今までWifiのテザリングを使っていたけれど、これがすごく不安定かつ遅く、使っていてとてもストレスがたまるものだった。

そんな中、オープンソースで開発されたHoRNDISというMacのUSBテザリング用ドライバが出た!

ということで早速試してみる。環境は次の通りだ。
・Galaxy Nexus Android 4.2.1(Softbank)
・MacBook Pro Late 2011 10.8.2

サイトからドライバをダウンロードし、インストールする。
現時点での最新バージョンはrelease 3だった。

インストールした後は、端末をUSB接続し、設定からUSBテザリングにチェックを入れるだけ。
するともうネットにつながっている!簡単!

Wifiテザリング時はなんだかもっさりしていたネットが、USBテザリングではさくさくになった。
Wifiテザリングではしょっちゅう通信が固まり、そのたびにWifiテザリングを設定しなおしていたが、そんな作業からも解放されて快適だ。
USBデバッグも同時に使えるため、Androidの開発を行いながら、その端末でテザリングということもできる。

Galaxy Nexusだけでなく、Galaxy S III 4.1.2 (Docomo)でも試してみたけどOKだった。

SSH接続時に、自動切断されないようにする

sshでサーバ接続中に、操作しなかったら自動切断されることがある。
そんなときはキープアライブをしてあげればOK。
root権限がある場合は/etc/ssh_configに、無い場合は~/.ssh/configに次の設定を追加しよう。

ServerAliveInterval 30

上記の設定で、30秒ごとに通信を行って自動切断を防いでくれる。