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

これはAndroid Advent Calendar 2014の30日目の記事です。

はじめに

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

環境

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

ANR

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を用いて端末内のANRログを取り出します。

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)

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

さいごに

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

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