Firebase Crashを使ってみた

Firebase Crash Reportingを使ってみた。今まではCrashlyticsを使っていたのだが、最近はFirebaseをアプリに組み込むことが多いので、クラッシュレポートもFirebaseでやってみようかなというのがことの始まり。

導入手順的に考えると、Firebase Crash Reportingはとても簡単。Firebaseを使うプロジェクトであれば、dependenciesに'com.google.firebase:firebase-crash:<VERSION>'を追加するだけで終わり。これだけでアプリがクラッシュしたら勝手にレポートをあげてくれる。

Firebaseを使う設定に関しても、Android Studioに組み込まれているFirebaseのツール(?)を使えばいとも簡単に使えるようになるので、導入の敷居はCrashlyticsに比べるととても楽である。

一方で、Crashlyticsと比較すると面倒くさいポイントもいくつかあって、単純に乗り換えればいいやという話でもなさそうなのが悩ましい。

mapping.txtのアップロード

ProGuardをかける場合に難読化されたスタックトレースを解読するため、mapping.txtのアップロードが必要になる。Crashlyticsの場合、設定が必要だが自動的にアップロードを行ってくれる。

一方でFirebase Crash Reportingは自分でFirebase Consoleにアップロードしなければならない。gradleタスクでアップロードするための方法が用意されてはいるが、Crashlyticsと比較すると「自動アップロード」とはいえない。初期導入が簡単な反面、ProGuardのmapping.txtをアップロードする設定を行う手間がある。

https://firebase.google.com/docs/crash/android

  1. ルートのbuild.gradleのclasspassに`'com.google.firebase:firebase-plugins:1.0.5'`を追加
  2. app/build.gradleに`apply plugin: 'com.google.firebase.firebase-crash'`を追加
  3. Firebase Consoleからプロジェクトの設定→サービスアカウント→クラッシュレポートから、新しい秘密鍵の生成を行いダウンロードする(jsonファイル)
  4. ダウンロードしたファイルへのパスを`FirebaseServiceAccountFilePath`というプロパティに記述する1
  5. `./gradlew :app:firebaseUploadReleaseProguardMapping`を実行してアップロード(buildVariantなどによってタスク名は変わる)
`firebaseUploadXXX`というタスクを実行しないといけないので、そのままだと確実に忘れそう。`assembleRelease`を実行したらこのタスクも実行するように指定できたらなぁと思ったのだけど、やり方がわからなかった。

そして依存させるなら、assembleReleaseよりもapkをGoogle Playにアップロードするタスク(自動化しているなら)に依存させるのが良さそうではある。

debugビルドでアップロードしてほしくない問題

Firebase Crashは特に何もしなくとも、アプリがクラッシュすればスタックトレースをアップロードしてくれる。カスタムApplicationクラスに初期化処理を書いて・・・なんてことすら必要ない。ContentProviderの初期化の仕組みを使ってライブラリ側で勝手に初期化しているとかなんとか見た気がする。ある意味便利ではあるが、一方で不便なところもある。それは、クラッシュレポートを送信させない手段が存在していないところである(たぶんない)。

CrashlyticsはカスタムApplicationで初期化をする必要があり、ここで例えばデバッグビルド中は送信しないようにしたり設定できる。Firebase Crashにはそういうのはないっぽい。そもそも自分で初期化しないし、送信を停止するようなメソッドも見当たらない。

これはFirebase CrashをreleaseCompileで組み込めば一応回避は可能である。

一方で、プライバシーポリシーの問題というか、ユーザの許可を得ずにクラッシュ情報を収集してよいのかという問題があると思う。このあたりの法的問題に、他の開発者さんはどう対処しているのか私は知らないが、個人を特定する情報は含まれていないとしても、例えばユーザにクラッシュレポートを送信しないような選択肢を提供したいときに、Firebase Crashではそれができないということになる。クラッシュレポートについてオプトアウトできるようにしてあるアプリがあるのかと言われるとよくわからないけれども。まあもし対処する必要が出てきたとしたら、きっとしれっと無効にできるようにアップデートされるのかもしれない。

logとreport

Firebase Crashは基本的には組み込めばそれで終わりな感じで、後は任意のタイミングでFirebaseCrash.log()とかFirebaseCrash.report()などを使ってクラッシュ時の情報を付け加えるくらいしかやることはない。

log()はクラッシュレポートにイベントとして情報を追加することができるものである。

report()は例えばtry~catchでcatchした例外のスタックトレースを送信するのに使う。

私はどちらもうまいこと使いこなせる自信がない。今までもクラッシュレポート見ても、一体どういう状況で発生しているのかよく分からなくて対応ができなかったことがよくある。log()を使えば原因を特定するのに有効な情報を付け足せるのだろうが、どういう情報を付け足せば原因把握に役立つのかはいまいち分からない。


  1. gradle.propertiesなどで指定すれば良い。プロジェクトルートに秘密鍵のファイルを配置したのであれば、`FirebaseServiceAccountFilePath=../<秘密鍵のファイル名>`という感じ。 

Crashlyticsのクラッシュレポートをdebugビルドでは行わないようにする

新しいアプリを作るときにしかやらないので、いつもやり方を忘れてしまうCrashlyticsのレポート設定のメモ。

デバッグビルドでまでクラッシュレポートが報告されてしまうと、通知がうっとうしいだけですし、手元でスタックトレースが読めるわけですから完全に無駄です。必要なのは基本的にはリリースビルドだけでしょうから、開発中は無効にしてしまうのが便利です。

やることは下記のページのとおりです。

https://docs.fabric.io/android/crashlytics/build-tools.html?gradle#build-tools

まずはレポートを無効にしたいbuildTypeにext.enableCrashlytics = falseを追加します。

この設定はGradleのビルド時に、Crashlyticsで使うIDなんかを生成する処理を行わないようにするもののようです。レポート送信を止めるわけではないので、これを追加しただけでは下記のようなエラーが出てしまいます。

This app relies on Crashlytics. Please sign up for access at https://fabric.io/sign_up,
                                                   install an Android build tool and ask a team member to invite you to this app's organization.

開発時にレポート送信自体を止めるためには、アプリ実行時にCrashlytics自体が動かないようにする必要があります。そのためには、Crashlyticsの初期化を行う部分で、下記のように初期化を行います。

        Crashlytics crashlyticsKit = new Crashlytics.Builder()
                .core(new CrashlyticsCore.Builder()
                        .disabled(BuildConfig.DEBUG).build())
                .build();
        Fabric.with(this, crashlyticsKit);

ちなみにdebuggableがfalseなんだけどCrashlyticsのレポートを無効化したい、なんていう場合には、.disabled(BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("test"))のようにしてやればOK。

レポートが送信されないことの確認

アプリの任意の場所でthrow new RuntimeException("Crashlytics report test");と例外を投げてみればOK。

ただし例外を投げる場所に注意が必要です。Crashlyticsの初期化が終わっていないと、そもそもレポートが送信できません。Applicationクラスの外か、ActivityのonCreateの外で例外を投げるようにしないとテストにならないので注意しましょう。

https://docs.fabric.io/android/crashlytics/introduction.html

Bluetooth経由でデバッグする

Android WearデバイスをBluetooth経由でデバッグする方法について。

Android WearもUSB経由でパソコンに接続してデバッグする方が何かと便利です。ですが、USB経由で接続しようと思うと、Wearデバイスに直接USBケーブルをつなぐタイプのものなら問題無いでしょうが、クレードル経由で接続するタイプの製品だと腕につけた状態でデバッグできません。

そんなときはBluetooth経由でデバッグすると便利です。

https://developer.android.com/training/wearables/apps/bt-debugging.html

Android Wearでの事前準備

Bluetooth経由でのデバッグを有効化しておく必要があります。

まず設定→端末情報→ビルド番号を7回タップして開発者オプションを有効にします。

すると開発者オプションを選択できるようになるので、そこからADBデバッグとBluetooth経由でデバッグを有効にします。

以上でWear側の事前準備はOK。

スマホ側での事前準備

Android Wearとペアリングしているスマホ側でもBluetooth経由のデバッグを有効化してやる必要があります。

Android Wear companion app(日本語だと単にAndroid Wear)を実行します。Android Wearとのペアリングしたりするアプリです。

使いたいAndroid Wearデバイスとのペアリングした状態で、Appbarにある歯車アイコンを押します。

すると設定画面が開くので、その一番下にあるBluetooth経由のデバッグを有効にしてやります。

ホスト:未接続、ターゲット:接続済みとなっていると思います。

それができたら次のステップ。

パソコンからターミナルで操作

以下のコマンドを実行。

adb forward tcp:4444 localabstract:/adb-hub
adb connect localhost:4444

adb connect localhost:4444でConnection Refuesedとなってしまう場合、localhostの部分を127.0.0.1とすれば接続できると思います。

接続できればスマホで「ホスト:未接続」となっていた部分が「ホスト:接続済み」となると思います。

そうすればAndroid StudioからAndroid Wearデバイスが見えるようになっていると思います。

切断

adb disconnect 127.0.0.1:4444
adb forward —remove tcp:4444

ちなみにポートフォワーディングしているかどうかを確認するにはadb forward —listで確認可能。

Read full post gblog_arrow_right

式の即時評価を利用してオブジェクトの状態を調べる

式の即時評価が便利だよねという話です。

私は以前Calendarクラスを使って日付の処理をしようとしていました。そのとき、どのフィールドを参照すれば目的の値が引っ張ってこれるかを確認するのに、愚直にLog.d()を使っていました。1つ1つメソッドの返り値を出力して(文字列の連結でさらにカオスになる)、目的の数値がちゃんと取れているのか確認していたのです。

ドキュメントを読めよっていう話なんですが、読んでもどういう値が取れるのかいまいち分からなかったんですよね・・・。

まあそんなアホなことをやっていたので、当然のようにバグを仕込んでいました。そんなバグに気づくきっかけとなったのが式の即時評価機能です。以来、とてもお世話になっています。

式の即時評価を使えば、ブレークポイントを設定してデバッグ実行するだけで、任意のメソッドや変数の確認ができるようになります。

式の即時評価

メソッド呼び出しとその結果が確認できる

ブレークポイントで一時停止させないと使えないので、状態の変化を追うのには向かないかもしれません。それでもlogcat頼みのデバッグより捗る場面があると思います。

どんなときに便利か

今日遭遇したエラーで、なんかのタイミングでNullPointerExceptionが発生してクラッシュする現象が発生しました。

特定の状況で例外発生によるクラッシュ

例外の発生する箇所はわかっているものの、どういう状況でそれが生じているのかがよく分かりませんでした。

そこで例外の発生する部分をtry-catch文で囲み、例外をキャッチした所にブレークポイントを置いて調べてみることにしました。

try-catchで止めてみる

ブレークポイントで止めればコールスタックを遡ってオブジェクトの状態を確認できますが、目当ての変数を探すのが大変なので、そういうときに式の即時評価が便利です。だと思います。

アプリのパフォーマンスを向上させる GPUオーバードロー

LinearLayoutをネストしすぎたりするなど、Viewの階層を深くするとアプリのパフォーマンスに良くないという話はよく聞くと思います。

それと似たような話で、画面を何回描画しているかを確認して、アプリのパフォーマンスに役立てることができきます。今回はそれの紹介です。

確認の仕方

端末の開発者オプションで「GPUオーバードローをデバッグ」を有効にします。

GPUオーバードローをデバッグ

これを有効にすると、目に悪そうな色で画面が表示されるようになります。

各色の意味

この各色が、GPUによって何回上書き描画されているのかを示しています。

  • 青色:1回
  • 緑色:2回
  • 薄赤:3回
  • 濃赤:4回以上

この状態で画面が真っ赤っ赤だと、描画方法を改善した方がいいぞということになります。

対策

例えばFrameLayoutでbackgroundDrawableを持ったViewを何個も重ねていくと、見えているのは一番上のものだけなのに、見えない下の要素まで描画するため上書き回数が増えて赤色になってしまいます。

そのため不要なbackgroundDrawableを描画しないようにすることが、この問題の対策になります。

例えばActivityでgetWindow().setBackgroundDrawable(null)とするだけでも画面の赤色が薄くなると思います。(ただし、これをやるとListViewやGridViewなど、スクロールをともなうViewの描画がおかしくなります)

重ねて描画せざるをえない場合は、canvas.clipRectを使って重なって見えない部分を描画しないようにすることで対応できるようです。

効果

ムダな描画回数を減らすことにつながるので、その分アプリの動きが軽快になるでしょう。

さらにバッテリーにも優しくなると思います。

ただし、アプリのもっさり感解消のための施策としては、優先度は低いのかなと思います。ちまたに出ているアプリでも、割と真っ赤なアプリが多いですし、赤くとも動作がもっさりしているものは少ない印象です。

やらないよりやった方がマシでしょうが、ここを気にするより、メモリの使用量を抑えるといったチューニングの方が、アプリのパフォーマンスにとって効果が高いような気がします。

Android Performance

この話はUDACITYのAndroid Performanceという動画を見て知りました。

英語オンリーかつ字幕すらありませんが、大体雰囲気で分かるんじゃないかなと思います。

Android Performance – UDACITY

その画面がどんなViewを使って作られているか調べる方法

「このアプリのデザインを参考にしたいんだけど、どうやって作ってるのか知りたい」というときに便利かもしれないコマンドです。

調べたい画面を表示させた状態で、ターミナルからadb shell dumpsys activity topと入力すると、現在表示中のView階層などが表示されます。

View階層だけを調べたいなら、hierarchyviewerを使った方がグラフィカルに見えて便利なのですが、hierarchyviewerはroot権限がないと起動しないので、実機で調べたい画面を表示して解析することができません。

その点、このadb shell dumpsys activity topはroot権限を必要としないので、実機でちょっと調べたいという時に便利だと思います。

どこからどこまでがActionBarの領域で、どこがコンテンツの領域なのかが非常に分かりづらいのですが、Viewに割り振られているIDも一緒に表示されるのである程度把握できると思います。

このIDが表示されるのを利用して、View階層の中でIDの衝突が起こっていないかなんてことを調べるのにも便利かもしれません。

ちなみにadb shell dumpsys activityと最後のtopを省略すると、Activity Managerの情報がズラズラと表示されます。

Broad castがどうなってるかとか、Content Providerがどんなのが動いているかとか、どんなServiceが動いているかとか、スタックがどうなってるかとかが出力されます。

実機でコマンドを打つだけで調べられるので、手軽で便利だと思います。

Android StudioでLogcatをフィルタリングする

Logcatでアプリのデバッグをする際に、自分のアプリからのログ出力だけ見たいなんてときありますよね。

そんなときにどうやってフィルタリングをするかという話です。

キーワードやログレベルでのフィルタリングはここでできます。

ログレベル・キーワードでフィルタリング

ちなみにログレベルでのフィルタリングは、指定したレベル以下のものを出力するというフィルタ設定になります。

例えばここでInfoを選ぶとInfo以下のものだけが出力されるようになり、VerboseとDebugレベルのログは表示されなくなります。

ログレベルは下図で囲った部分です。ログレベル/タグという形式で出力されています。

ログレベル

特定のアプリからのログ出力だけ見たい場合は、Show only selected applicationのところをクリックして、Edit Filter Configrationを選びます。

Edit Filter Configration

ここでPackage Nameのところで調べたいアプリのパッケージ名を指定してやると、自分のアプリからの出力しか表示されなくなります。

Package Nameでアプリからの出力のみに絞れる

こういったフィルタリングの設定をうまく使えば、アプリ開発も捗ると思います。

一方でLogcatにこれらのフィルタ設定をしているときには注意しなければならないことがあります。それは、アプリが落ちた時のスタックトレースまでフィルタされて表示されないことがあるということです。

「アプリが落ちたのにLogcatに何も表示されない・・・」と混乱しないようにしましょう。

Androidアプリを開発する上で賢いLogの出力方法(とその確認の仕方)

今までずっとLog.d("test",”デバッグメッセージだよ”);みたいな感じでLogを出力し、Logcatで確認しながらプログラミングしていたのですが、とあるサンプルを見ていた時にこんなコードに出くわしました。

    private static final String TAG = "DigitalWatchFaceConfig";

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onConnected: " + connectionHint);
        }

プログラムを実行しても、このログはlogcatに出力されません。

「なんでだ?」と思って調べているうちに、この方法はAndroidアプリ開発していく上で賢い選択なのだなということが分かってきました。

ベストプラクティスなのかどうかまでは分かりませんが、少なくともいきなりLog.d()で出力したり、アプリ内でprivate boolean isDebug = true;みたいにしてデバッグログを出力させるよりは賢いなと思いました。

参考サイト

[Log.isLoggable – API Refference](https://developer.android.com/reference/android/util/Log.html#isLoggable(java.lang.String, int))

ログレベルを制御する – TechBooster

ログの出力はアプリのパフォーマンスを下げる

ログの出力はアプリのパフォーマンスに影響します。少なくともStringオブジェクトを作ってそれを出力するわけですからね。

リリース時にはLog出力する部分を全部削除するのが一番いいのでしょうが、さすがにそれは手間が大きすぎます。それにリリースしたからといって開発が終わるわけでもなく、メンテのためにまた1からLogを出力するように直すのはあまりにも馬鹿らしいです。

Logを使わず開発するのはそもそも無理です。

Log.isLoggableによるチェック

Log.isLoggableによるチェックは、端末に設定されているログ出力レベルを判定しています。デフォルトでは全てのタグについてINFOが設定されています。

つまり最初のコードのようなLog.DEBUGでチェックをかけるとfalseが返ってくるのでログが出力されません。

ログ出力レベルの変更

ではどうやってログが出力されるようにすればいいのかというと、ターミナルでadb shell setpropコマンドを使います。

adb shell stop
adb shell setprop log.tag.設定したいタグ名 ログレベル
adb shell start

最初の例のログを出力させようと思ったらadb shell setprop log.tag.DigitalWatchFaceService DEBUGとターミナルから打ち込んでやればOKです。(ちなみにadb shellで端末にログインしてからであれば、いきなりsetpropから初めてOKです)

ログ出力レベルの確認

タグごとのログ出力レベルを確認するには、adb shell getprop log.tag.タグ名を使います。何も設定していない状態であれば空白が返って来ます。setpropで設定してやると、現在設定されているログ出力レベルが返って来ます。

Read full post gblog_arrow_right

adbコマンドを使ってアプリを手動で削除する

adbコマンドを使ってアプリを手動で削除するのは、スマホのアプリを作成する上では必要ないと思っていましたが、Android Wearアプリを作る場合には覚えておいた方がいいです。

というのもWearに一度インストールしたアプリは、Wear上での操作では削除することができないからです。

スマホ経由でインストールしたアプリの場合、スマホ側でアプリを削除すればWearにインストールされたアプリも一緒に消えてくれますので、一般的にはあまり問題にならないのかもしれません。

しかしWearアプリを開発していると、デバッグのためなどでWearに直接アプリをインストールすることが多くなります。こうなるとWearがデバッグ用のアプリで埋め尽くされる事態になってくるのです。

インストールされているパッケージの一覧を確認する

adb shell pm list package

ターミナルからコマンドを入力すると、対象デバイスにインストールされているパッケージの一覧が表示されます。

adb shell pm list package

アプリを削除するためにはパッケージ名が必要になるので、このコマンドで確認しましょう。

ちなみにパソコンに複数の端末が接続されている場合、adbコマンドを送るデバイスを指定する必要があるので注意しましょう。

アプリを手動で削除する

adb uninstall アプリのパッケージ名

adb uninstall

Wearのアプリを削除する場合は、コマンドの結果が返ってくるまで時間がかかります。

Wearの初期化のほうが早いかもしれない

ちなみに、Wearを初期化してしまえば済む話でもあるので、手動で消さなくてもなんとかなる話ではあります。Wearは初期化したところでスマホとペアリングし直すだけでほぼ元に戻ると言っても過言ではないため、初期化したほうが手っ取り早いのかもしれません。

ただ、サンプルアプリを1個インストールする度に初期化するのも馬鹿らしいので、コマンドを使えば簡単にアプリを消せるということを知っておくと何かと便利かもしれません。

Android Wearアプリ開発にエミュレータを利用する

Android Wearアプリを開発する際にエミュレータを利用する場合の話です。

開発中のアプリを実行してデバッグを行うためには、エミュレータなり実機なりを用意する必要があります。Wearアプリの場合、スマホとWear端末が必要になるため、普通のスマホアプリを開発するより用意するものが多くて面倒なところです。

最も簡単なのはスマホとWear端末共に実機を用意することです。既にペアリングを行った実機があれば、その2つをパソコンにUSB接続すればデバッグできます。特に設定を要しません。

一方で、片側をエミュレータ、もしくは両方をエミュレータでデバッグしたい場合は話がややこしくなります。

エミュレータを利用するにしてもスマホは実機で

エミュレータではBluetooth通信まではエミュレートしてくれないので、ポートフォワーディングを利用してスマホ・Wear端末間の通信を再現します。

片方をエミュレータにする場合でも簡単なのはスマホを実機で、Wear端末はエミュレータでやる方法です。

というのもAndroid Wearとのペアリングを行うためには、スマホ側で「Android Wear」というアプリを操作する必要があります。

スマホをエミュレータに置き換えてやろうとすると、このAndroid Wearを使えるようにするのが難しいようなので大変だと思います。どうも直接apkを拾ってきてエミュレータにインストールしないといけないようなので・・・。

Android Wearアプリを開発する場合、基本的にはスマホもWear端末も実機を用意するのがオススメです。実行速度の観点からもそれが一番だと思います。

Android WearのエミュレータはAVDを利用せざるを得ず、上述のポートフォワーディングを利用してペアリングを行ったとしても、端末間の通信は実機に比べるとだいぶタイムラグが生じます。

スマホ実機とWearエミュレータをペアリング

パソコンにスマホ実機をUSBで接続し、AVDでWearエミュレータを起動した状態から始めます。

まずターミナルからポートフォワーディングを行うようコマンドを入力します。

adb -d forward tcp:5601 tcp:5601

ポート番号は何でもいいようです。(ただし現在使われていないものでないとダメ)

ちなみに-dはUSBで接続されているデバイスという意味です。-dをつけるか、デバイスを直接指定しないとerror: more than one device and emulatorとエラーが出ます。

特にエラーメッセージが表示されなければ成功です。

スマホからAndroid Wearアプリを起動し、右上の三点ドットをタップ→新しいウェアラブルとペア設定を選びます。(歯車アイコンの設定ではないです)

端末を選択画面が表示されるので、右上の三点ドットをタップして「エミュレータをペア設定」を選べばエミュレータと接続完了します。

エミュレータをペア設定

どうでもいいことですが、ペアリングが完了するまでの速度だけは実機より早いです。

Wear端末(実機)とペアリングし直す場合は、エミュレータから切断を行った後、端末を選択画面から自分の使っているWear端末を選べばペアリングし直されます。

adbによるポートフォワーディングについて補足

ちなみに現在adbでポートフォワードを行っているポートを確認するには以下のコマンドを使います。

adb forward --list

現在行っているポートフォワーディングを止めるには以下のコマンドを使います。

adb -d forward --remove ポート番号

全てのポートフォワーディングを解除してしまって問題ないならadb forward --remove-allでもいけます。–remove-allの方が入力する量が少なくて楽かもしれません。