Androidアプリを高速化しよう -ANR編-

はじめに

この投稿はAndroid Advent Calendar 30日目の記事です。

前回の25日目では目に見えないボトルネックを探す手法を紹介しましたが、今回はAndroidアプリケーション開発を行う上で避けては通れないANRとその対処法について書きます。

環境

この記事で紹介するソースコードの動作確認は以下の環境で行いました。

また、この記事で紹介するコードは以下のリポジトリにあります https://github.com/yuyakaido/AndroidAdventCalendar2014

ANR (Application Not Responding)

ANRとはメインスレッド上で重たい処理を行った際に表示される警告で、ユーザーには「〜は応答していません」というダイアログとして表示されます。ここで言う重たい処理というのは具体的には以下の通りです。

  • メインスレッド上で5秒以上かかる処理を実行した
  • BroadcastReceiverが10秒以内で終了しない

実験

では、実際にANRを発生させてみます。

アプリに適当なボタンを配置してそれを押すと以下のコードが実行されるようにします。

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

アプリケーションを実行し、ボタンを押してしばらくすると以下のようにANRが発生します。

f:id:yuyakaido:20141230154001p:plain

対処法

上記のサンプルでは原因が明らかですが、実際はコードがもっと複雑で一見するとどこが原因となっているのかを特定するのが困難である場合がほとんどだと思います。ですが、実はANRは端末内の以下のファイルに逐一記録されています。

/data/anr/traces.txt

上記のファイルを確認することでANRの原因を特定することが可能となります。

adbを用いて端末内のtraces.txtをローカルマシンに持ってきます。

adb pull /data/anr/traces.txt ~/

ANRのログはこのファイルの先頭に逐一追加されていきます。 ローカルマシンに持ってきたファイルを開いてみると、以下のように先程の実験で発生させたANRが記録されていました。

----- pid 1219 at 2014-12-30 06:54:15 -----
Cmd line: com.yuyakaido.androidadventcalendar2014

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)

"main" prio=5 tid=1 TIMED_WAIT
  | group="main" sCount=1 dsCount=0 obj=0xa62904b0 self=0xb9109510
  | sysTid=1219 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1216953280
  | schedstat=( 81308922 43683273 389 ) utm=6 stm=1 core=0
  at java.lang.VMThread.sleep(Native Method)
  at java.lang.Thread.sleep(Thread.java:1031)
  at java.lang.Thread.sleep(Thread.java:1013)
  at com.yuyakaido.androidadventcalendar2014.MainActivity.performApplicationNotRespondingTest(MainActivity.java:71)
  at com.yuyakaido.androidadventcalendar2014.MainActivity.access$400(MainActivity.java:17)
  at com.yuyakaido.androidadventcalendar2014.MainActivity$5.onClick(MainActivity.java:63)
  at android.view.View.performClick(View.java:4084)
  at android.view.View$PerformClick.run(View.java:16966)
  at android.os.Handler.handleCallback(Handler.java:615)
  at android.os.Handler.dispatchMessage(Handler.java:92)
  at android.os.Looper.loop(Looper.java:137)
  at android.app.ActivityThread.main(ActivityThread.java:4745)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:511)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
  at dalvik.system.NativeStart.main(Native Method)

このようにtraces.txtを参照することでANRの原因を特定することが出来ました。原因が分かってしまえば後は煮るなり焼くなりしてしまいましょう。

さいごに

この年末はAndroidではなく、ひたすらSwiftiOSアプリを開発しています。決してAndroid開発に嫌気がさしたわけではないですよ?

それではAndroid開発者の皆様、良いお年を!