未分類

AndroidでBackキー押したときにActivityが終了してアプリが切り替わる部分を調べた

ゴールデンウィークで暇だったので、AndroidでBackキーを押したときにActivityが終了してアプリが切り替わる部分を調べてみる。
仕事でこのあたりいじって独自処理を埋め込むことになりそうなので。

Androidのバージョンは5/2時点のAOSP master。まぁLollipopの間はがっつり変わる部分では無いでしょう。

スタート地点はActivity.javaのonkeyDown。
onKeyDownの中でonBackPressed呼んでますね。

onBackPressedもActivityで実装されています。
mFragmentsでBackStackが無ければfinishAfterTransitionですね。

ActivityTransitionStateは終了時のアニメーションですかね。
中は見ていないですが、アニメーションが無ければ即finishと言うところでしょうか。

finishは次のような感じ。
mParentは親となるActivityですね。ActivityGroupでActivityの中にActivityを埋め込んだ時のかな。
ActivityManagerServiceのfinishActivityを呼んでいますね。

ここからはSystemServiceのプロセスです。
ActivityManagerServiceのコード量は多いので、一部分だけ抜粋。
ActivityManagerService.finishActivityです。
finishTaskがtrueかつ終了するActivityがルートであれば、removeTaskByIdLockedでタスクを終了しています。
そうでなければ、ActivityStackのrequestFinishActivityLockedを呼んでいます。
同一タスク内でのActivity終了によるActivity切替は深く追いかけませんが、この後いろいろあってActivityStack.finishCurrentActivityLockedあたりで終了処理をしています。

removeTaskByIdLockedの中身です。
removeTaskActivitiesLockedはperformClearTaskAtIndexLocked(0)が呼ばれ、すべてのActivityがfinishされます。finishの処理はActivityStack.finishActivityLockedですね。この中身は後で見ています。

cleanUpRemovedTaskLockedの中身です。
killProcessは今回falseなので、プロセスをkillする前にreturnします。mRecentTasksは最近使ったアプリ一覧です。TaskRecord.removedFromRecentsはサムネイルの削除などの後始末をしています。

ActivityStack.finishActivityLockedの中身を見てみます。
長いので一部省略しています。
終了処理をしたり、キーのディスパッチを停止したりしていますね。
Activityがなくなった場合はendTaskがtrueになり、mWindowManager.prepareAppTransitionしてmWindowManager.setAppVisibilityでVisibilityをfalseにしています。

WindowManagerService.setAppVisibilityを見てみます。
長いので省略・・・。
wtokenはAppWindowTokenです。
wtoken.hiddenRequestedがtrueになり、WindowManagerServiceのmClosingAppsにwtokenがaddされています。
mClosingAppsに追加されたAppWindowTokenが実際に処理される場所はどこでしょう。
handleAppTransitionReadyLockedかな?

とりあえずアプリ切替はWindowManagerServiceのsetAppVisibilityをfalseとする事で実現されているようです。
このあたりをゴニョゴニョしたらホニャホニャできそうです。

なお、この調査はコードを追いかけただけで、実際にログを仕込んで確認したわけでは無いのであしからず・・・。

kernel_taskが暴走してMacがとても遅くなったが解決した

MacBook Pro(15-inch Late 2011)を使っていて、いつの間にかkernel_taskがCPUを使いまくってマシンのレスポンスがとても遅くなってしまった。日本語で「あいうえお」と入力すると、1秒ごとにあ、い、う、え、おと入力されるぐらい遅い。

結論を言うと、ファンのホコリの除去とSMCをクリアしたら直った。
同じような現象が出た方のために経緯とチェックすべき点を書いておく。


最初、VMware Fusionを起動しているときに遅くなったことに気がついたので、VMwareが原因かなと考えていた。

が、VMwareを起動していないときにも遅くなってきた。
iStatで確認すると、kernel_taskがCPUを400%ぐらい使っている。
しかもロードアベレージがやたら高い。起動直後はロードアベレージが150とかになっている。
そして起動直後からファンがフル回転の6200回転固定になっている。

これはどうやら何かがおかしいと言うことで、まずはOSのクリーンインストールを試してみた。
必要なデータはDropbox等のクラウドに置いているので、クリーンインストールの精神的なハードルがとても低くなり、良い。
しかし、Yosemiteをクリーンインストールしたが、まだkernel_taskがCPUを使っている。

これはどうやらソフトウエアの問題では無いようだ。

ファンがまだフル回転しているので、熱の問題かと考えた。
iStatでは温度は特に問題無いが(CPUは70度程度)、センサーに問題があるかもしれない。
マシンが極端に遅くなっているのは、熱が原因でCPUのクロック数が下がっている可能性を考える。

そこで、CPUのクロック数をチェックすることができるIntel Power Gadgetを入れてみた。
CPUは処理の負荷によって動的にクロック数を変化させるのだけど、Intel Power Gadgetはリアルタイムにそれを確認することができる。

IntelPowerGadget

すると、このMacはCPUの定格が2.4GHzなのだが、0.8GHzで固定されているような動作をしていた。
マシンが遅いのはこいつが原因のようだ。

ファンの調子が悪いかもしれないので、MacBook Proの排気口にエアダスターを吹きかけて、ホコリを吹き飛ばしてみた。
4年近く使っているので、結構な量のホコリが出てきた。大きなホコリの塊もある。

吹き飛ばしてみると、0.8GHz固定ではなくなったが、それでも2.4GHzは出ない。
そしてファンの回転数も6200回転で動かない。

次に、SMCをクリアしてみた。SMCはMacの電源周りをコントロールしているチップで、これが原因でスリープ等の電源周りがおかしくなることがあるらしい。
Intel ベースの Mac:SMC (システム管理コントローラ) のリセットを参考にし、SMCをリセットしてみた。

すると、これがビンゴでファンの回転数が正常に戻り、CPUのクロック数も2.4GHz以上出た。
マシンが遅くなることも、kernel_taskがCPUを食いつぶすこともなくなった。

解決したので、Macの動作が異常に遅くなった場合の確認ポイントをまとめてみる。

  • まず、ActivityモニタかiStatのようなCPUを使っているプロセスを確認することができるアプリで、どのアプリがCPUを食っているか確認する。
  • kernel_taskがCPUを食っている場合、様々な原因が考えられるが、今回のようにCPUをアイドル状態にしてCPU温度が上がらないように制限する場合もkernel_taskのCPU利用率が上がる。
  • 遅くなっている原因がCPUのクロックダウンにあるかどうかをIntel Power Gadgetで確認する。
  • 常にクロックダウンしているのであれば、熱とSMCの問題が考えられるので、ファンを掃除し、SMCをクリアする。

 

MacにEmacs 24.4を入れる

Mac(Mavericks)上でEmacsを使っていると、distnotedというプロセスが暴走してファンが止まらずうるさい。
一応パッチがあるようだけど、なんだか効果が無いようなので、直っているという24.4を入れてみた。
(単にパッチにしくじっているだけかもしれない)

インストールは次のコマンドでOK

$ brew install emacs --HEAD --use-git-head --cocoa --with-gnutls
$ brew linkapps

まだ正式リリースされていないバージョンと言うことで、設定ファイルに互換性が無いとか動作が怪しいとかいう情報があったけど、特に問題なく使えている。

CookpadのレシピをAndroid Wearに転送するアプリを作った

Cookpadって便利ですよね。でも料理をしている最中にレシピを確認するのって意外と大変です。
そんな時、Android Wearでレシピ情報が確認できれば非常に便利そう!
と言う事で作ってみました。

まず、AndroidのCookpadアプリでレシピを表示します。

Screenshot_2014-07-09-23-32-28

 

「レシピを送る」ボタンを選択し、「料理パッドWear」を選択します。

Screenshot_2014-07-09-23-32-40

すると、Android Wearに通知が表示されます。

device-2014-07-09-233302

スワイプすることで行程を確認することができます。

device-2014-07-09-233335

 

料理をしながら手元でレシピを確認できる!便利ですね!

 

2014-07-09 23.34.14

 

処理は次のような感じにしています。

  1. Intent.ACTION_SEND text/plainを受け取るActivityを用意します。
  2. ActivityでIntentを受け取り、その中にCookpadレシピのURLがあれば、HTMLを取得します。
  3. HTMLの取得に成功すれば、HTMLをパースし、行程のテキストを抜き出します。
  4. Android WearのMessageApiを使い、行程のテキストをAndroid Wearに送信します。
  5. Notificationを使い、行程を表示します。

実は30秒経過するとAndroid Wearの画面が時計に戻っちゃうので、このままでは使い物になりませんが、そのあたりちゃんと作れば便利そうです。(できるんかな?)
使い方次第ではAndroid Wearって以外と便利なガジェットになりそうですね。

Android Wear(G Watch)のgetSystemAvailableFeatures

日本でもついにAndroid Wearが発売されましたね。
ちょっと使ってみましたが、結構良い感じでは無いでしょうか。
小さい時計型端末の中でAndroidが動いているのがわくわくします。

まだ一般の方にはオススメできるような出来では無いですが、将来性はある予感がします。

ところでAndroid Wearでのアプリ開発も、ドキュメントが公開されていますね。
Creating Wearable Apps

「基本的にはAndroid標準のAPIがそのまま使えるが、Wearableな端末で動かすときは hasSystemFeature()でチェックしろよな!」とあります。
ちなみにAndroid標準APIの中で使えないのは次のものが使えないと書かれています。

では早速PackageManager.getSystemAvailableFeaturesで有効なFeatureを確認してみましょう。
G Watchでは次の17のFeatureが有効でした。
なお、Nexus5では47のFeatureが有効で、G Watchのみに存在したFeatureはandroid.hardware.type.watchだけでした。
マイクはついてるのに音は鳴らせないんですね。

FeatureInfo=FeatureInfo{adf97e68 android.hardware.sensor.gyroscope fl=0x0}
FeatureInfo=FeatureInfo{adf97ef8 android.hardware.touchscreen.multitouch fl=0x0}
FeatureInfo=FeatureInfo{adf97f98 android.hardware.sensor.compass fl=0x0}
FeatureInfo=FeatureInfo{adf98028 android.hardware.sensor.accelerometer fl=0x0}
FeatureInfo=FeatureInfo{adf980c0 android.hardware.location fl=0x0}
FeatureInfo=FeatureInfo{adf98140 android.hardware.type.watch fl=0x0}
FeatureInfo=FeatureInfo{adf981c8 android.hardware.sensor.stepcounter fl=0x0}
FeatureInfo=FeatureInfo{adf98260 android.hardware.bluetooth_le fl=0x0}
FeatureInfo=FeatureInfo{adf982e8 android.hardware.bluetooth fl=0x0}
FeatureInfo=FeatureInfo{adf98368 android.hardware.usb.accessory fl=0x0}
FeatureInfo=FeatureInfo{adf983f0 android.hardware.sensor.stepdetector fl=0x0}
FeatureInfo=FeatureInfo{adf98488 android.hardware.screen.portrait fl=0x0}
FeatureInfo=FeatureInfo{adf98518 android.hardware.touchscreen fl=0x0}
FeatureInfo=FeatureInfo{adf985a0 android.software.home_screen fl=0x0}
FeatureInfo=FeatureInfo{adf98628 android.hardware.microphone fl=0x0} マイク
FeatureInfo=FeatureInfo{adf986b0 android.hardware.faketouch fl=0x0}
FeatureInfo=FeatureInfo{adf98730 glEsVers=3.0 fl=0x0}

 

Javaで名前付きパイプを使う

名前付きパイプというものがあると言うことは聞いていたけれど、使ったことは無いのでちょっと使ってみました。
環境はMac OS(Mavericks)です。

名前付きパイプの説明はこちら。簡単に言うとファイルみたいに読み書きできるパイプで、プロセス間通信に使うことが多いようです。お互いのプロセスで名前付きパイプを開き、片方でwriteすると、もう片方でreadすることができます。
一見ファイルですが、実際にディスクに読み書きするわけでは無いので省エネということですね。

Macで名前付きパイプを作成する場合は、mkfifoコマンドを使います。次のようにします。

$ mkfifo named_pipe

lsすると、普通のファイルのように作成されます。

prw-r--r--  1 kitamura  staff  0  2  6 14:46 named_pipe

今回は、echoコマンドで名前付きパイプに書き込み、それをJava側で読み込んでみます。

書き込み側は次の通りです。

$ echo "ふがふがふが子\nほげほげほげ太" > named_pipe

これを実行すると、Java側で読み込むまでブロックします。

Java側は次のようにします。

File file = new File("/Users/kitamura/Documents/tmp/named_pipe");
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}

Macでは読み込み側、書き込み側両方がopenするまで、openでブロックしました。JavaではFileInputStreamのnewでブロックします。書き込む場合は、FileOutputStreamのnewでブロックします。

Linuxでは、Macと挙動が違い、Readをopenしなくてもバッファがいっぱいになるまで書き込みができます。

OSによって挙動の違いが気になりますが、気軽にプロセス間通信できそうですね。

 

キーボード、マウス共有アプリShareMouseを導入してみた。

複数のPCを同時に使う必要がある場合、それぞれのPCにキーボード、マウスを繋いで使い分けるのはとても不便です。

そんな時は、1台のキーボードとマウスを複数のマシンで使い回すことができるキーボード共有アプリを使うと幸せになれます。

キーボード共有アプリはオープンソースだと有名なSynergyがあり、今までSynergyを使ってきたのですが、ちょっとバグに悩まされたので別のソリューションを探してみました。

そんな中で一番良さそうだったのは有料になってしまいますが、ShareMouseです。
対応OSはWindowsとMacのみなのですが、次の利点がありました。

  • マシン間のドラッグアンドドロップでファイルorディレクトリをコピーできる。
  • ファイルorディレクトリをクリップボードにコピーし、それを別のマシンでもペーストできる。
  • サーバ、クライアントの区別が無く、さらに接続先のサーバIP設定なども必要ないので設定が楽。
  • マシン2台、モニタ2台だと無料で使える。

デモビデオを見ると便利さがわかります。

 

ファイルのコピーをドラッグアンドドロップでできるのが非常に便利です。
試しに2.5Gのファイルをドラッグアンドドロップすると、コピー中を示すShareMouseのプログレスダイアログが表示され、ちゃんとコピーできました。

クリップボードの共有はちょっと癖があり、ペーストとして設定したキーを押した瞬間に共有処理が実行されます。
私は、Mac側ではKeyRemap4MacBookを使ってEmacsキーバインドのCtrl+Yで貼り付けとしているのですが、これではローカルのみの貼り付けで、共有クリップボードの貼り付けにはなりませんでした。その場合は、ShareMouseの共有クリップボード貼り付けのショートカットをCtrl+Yに設定してあげると解決です。

ペーストしようとした際に共有処理が実行されるので、巨大なテキストをコピーした状態でマシンをまたぐと固まってしまうと言うSynergyの悲しい挙動に悩まされることはありません。

価格はマシン2台モニタ2台であれば無料(ただし一部機能制限あり)、マシン2台モニタ無制限であれば20ドル、マシン無制限モニタ無制限であれば50ドルです。
このライセンスには1年間のアップグレードがついており、その間であれば新しいバージョンを利用できるようです。期限が来てもアップグレードができませんが使い続ける事は可能です。

と言う事で、Linuxに対応していないのが残念ですが、WindowsとMacのみであればShareMouseがとても便利です。

git diffで新規追加したファイルをdiff結果に含める

みなさん、git diffしてますか?patchを作成するときに便利ですよね。

しかし、コミット前の状態でgit diffすると、新規作成したファイルは反映されず、patchを当ててもビルドエラーなんて悲しいことになった事がある方も多いと思います。

そんなときはgit add -N FILENAMEをすれば解決。
新規追加したファイルとマークをつけることができ、git diffの結果に含まれるようになります。

注意点ですが、コミット前の状態で普通にgit diffすると、追加ファイルがnew fileにならないので、git diff HEADを実行する必要があります。

$ git diff
// 変更点無し

// ファイル追加
$ echo "HogeHoge" > file2.txt

// 差分確認
$ git diff
// 変更点無し

// git add -N実行
$ git add -N file2.txt

// 差分確認
$ git diff HEAD
diff --git a/file2.txt b/file2.txt
new file mode 100644
index 0000000..1a12b63
--- /dev/null
+++ b/file2.txt
+HogeHoge

Mac(Mountain Lion)でRAMディスクを作る

Macで作業するとき、たまに「高速なディスクが欲しい!」と思うことがあります。

そんなときはメモリをディスクとして使うことができるRAMディスクを用いることで解決することができます。

もちろんメモリなので電源を切っちゃうと、RAMディスクの中身も消えてしまいますので注意!

作り方もコマンドライン一行なのでお手軽!

Mountain Lionで動作確認していますが、バージョンによっては動かないかも。

 

情報元はこちら。How to Create a 4GB/s RAM Disk in Mac OS X

4Gの場合

$ diskutil erasevolume HFS+ ‘RAM Disk’ hdiutil attach -nomount ram://8388608

 

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がいるのですが、複雑なのでまだちゃんと理解はしていません。
ここもきっちり理解しないとモヤモヤ感が消えず、何となく理解程度で止まりそうです。