adbコマンドは非常に便利で、端末(エミュレータ)の操作や確認など様々なことができます。
特にインストール/アンインストールやファイル操作などは、多くのAndroidエンジニアが実際に利用しているのではないかと思います。
adb -h
でadbの使い方が出てきますが、何故かここに出てこない便利コマンドがありますので、今回はそのコマンドについてご紹介します。
dumpsys
dumpsysコマンドはシステムサービスの状態をダンプしてくれます。 簡単な例で言うと、バッテリーの残量やメモリの消費量、ネットワーク通信状態などを、コマンド一発で確認することができます。
下記がdumpsysで取得できる情報のカテゴリになります。
コマンド
dumpsys | grep "DUMP OF SERVICE"
出力
DUMP OF SERVICE Genyd: DUMP OF SERVICE SurfaceFlinger: DUMP OF SERVICE accessibility: DUMP OF SERVICE account: DUMP OF SERVICE activity: DUMP OF SERVICE alarm: ・・・ DUMP OF SERVICE netstats: DUMP OF SERVICE network_management: DUMP OF SERVICE notification: ・・・ DUMP OF SERVICE wallpaper: DUMP OF SERVICE wifi: DUMP OF SERVICE wifip2p: DUMP OF SERVICE window:
dumpsysの使用用途
バッテリーの残量やメモリの消費量、ネットワーク通信状態など、端末の情報がコマンド一発で確認できますので、開発者以外のAndroidユーザーにとっても普通に便利です。 (開発環境は入れないといけないですが・・・)
ただここはエンジニアブログ。
前回の投稿「今回、エンジニアブログはお休みです」で弊社レジェンドエンジニアから
実用的なものをお求めの方は、次回のエンジニアブログを楽しみにしてください。
との素敵なパスを頂きましたので、今回は開発者が実際の開発現場で使えるコマンドを紹介したいと思います。
dumpsysで確認できる情報のほどんどは、SDKに含まれるAndroidDeviceMonitorや端末の設定画面でも確認することができます。
ただ、AdnroidDeviceMonitorは起動に時間が掛かりますし、常に立ち上げているようなものでもないので、サクッと確認するにはdumpsysのほうが都合がよかったりします。
Android開発に携わるエンジニアは是非試してみてください。
今表示しているActivityを調べる
エミュレータや実機で表示中の画面が、どのActivityによって表示されているか知りたい、というケースがあると思います。 自分が実装に深く関わっていればある程度記憶でなんとかなりますが、まだ中身を理解していない状態では「これどのActivityだっけ?」が非常に多く起こりえます。
そんな場合は下記コマンドが使えます。
コマンド
adb shell dumpsys activity | grep -B 1 "Run #[0-9]*:"
結果
TaskRecord{53441488 #3 A com.example.android.app U 0} Run #3: ActivityRecord{5333cc6c u0 com.example.android.app/com.example.BudgetPlanActivity} Run #2: ActivityRecord{535afc74 u0 com.example.android.app/com.example.BudgetProgressActivity} Run #1: ActivityRecord{5338e558 u0 com.example.android.app/com.example.HomeActivity}
私自身、1ヶ月ほど前にマネーフォワードに入社したのですが、アプリの挙動を把握するのに非常に役立ちました。
FragmentManagerの使用状況を確認する
Activity同様、現在の画面がどのFragmentで表示されているのかを確認することができます。 また同時にFragmentManagerが管理するFragmentの一覧を確認することも可能です。
コマンド
adb shell dumpsys activity top
結果
Local FragmentActivity 5342b114 State: mCreated=truemResumed=true mStopped=false mReallyStopped=false mLoadersStarted=true Active Fragments in 53447b80: #0: AssetHistoryFragment{53422b54 #0 id=0x7f0a0085 android:switcher:2131361925:1412121600000} mFragmentId=#7f0a0085 mContainerId=#7f0a0085 mTag=android:switcher:2131361925:1412121600000 mState=5 mIndex=0 mWho=android:fragment:0 mBackStackNesting=0 mAdded=true mRemoving=false mResumed=true mFromLayout=false mInLayout=false mHidden=false mDetached=false mMenuVisible=true mHasMenu=false mRetainInstance=false mRetaining=false mUserVisibleHint=true mFragmentManager=FragmentManager{53447b80 in AssetHistoryActivity{5342b114}} mActivity=com.example.AssetHistoryActivity@5342b114 mArguments=Bundle[{time=1412121600000}] mContainer=android.support.v4.view.ViewPager{533f7cec VFED.... ........ 0,0-768,1022 #7f0a0085 app:id/pager} mView=android.support.v4.app.NoSaveStateFrameLayout{532d68d8 V.E..... ........ 0,96-768,1022} mInnerView=android.support.v4.app.NoSaveStateFrameLayout{532d68d8 V.E..... ........ 0,96-768,1022} Child FragmentManager{53463bf8 in AssetHistoryFragment{53422b54}}: FragmentManager misc state: mActivity=com.example.AssetHistoryActivity@5342b114 mContainer=android.support.v4.app.Fragment$1@53463c34 mParent=AssetHistoryFragment{53422b54 #0 id=0x7f0a0085 android:switcher:2131361925:1412121600000} mCurState=5 mStateSaved=false mDestroyed=false #1: AssetHistoryFragment{533ee4c0 #1 id=0x7f0a0085 android:switcher:2131361925:1404172800000} mFragmentId=#7f0a0085 mContainerId=#7f0a0085 mTag=android:switcher:2131361925:1404172800000 mState=5 mIndex=1 mWho=android:fragment:1 mBackStackNesting=0 mAdded=true mRemoving=false mResumed=true mFromLayout=false mInLayout=false mHidden=false mDetached=false mMenuVisible=false mHasMenu=false mRetainInstance=false mRetaining=false mUserVisibleHint=true mFragmentManager=FragmentManager{53447b80 in AssetHistoryActivity{5342b114}} mActivity=com.example.AssetHistoryActivity@5342b114 mArguments=Bundle[{time=1404172800000}] mContainer=android.support.v4.view.ViewPager{533f7cec VFED.... ........ 0,0-768,1022 #7f0a0085 app:id/pager} mView=android.support.v4.app.NoSaveStateFrameLayout{534b2628 V.E..... ........ -768,96-0,1022} mInnerView=android.support.v4.app.NoSaveStateFrameLayout{534b2628 V.E..... ........ -768,96-0,1022} Child FragmentManager{532d6ac4 in AssetHistoryFragment{533ee4c0}}: FragmentManager misc state: mActivity=com.example.AssetHistoryActivity@5342b114 mContainer=android.support.v4.app.Fragment$1@532d6b00 mParent=AssetHistoryFragment{533ee4c0 #1 id=0x7f0a0085 android:switcher:2131361925:1404172800000} mCurState=5 mStateSaved=false mDestroyed=false Added Fragments: #0: AssetHistoryFragment{53422b54 #0 id=0x7f0a0085 android:switcher:2131361925:1412121600000} #1: AssetHistoryFragment{533ee4c0 #1 id=0x7f0a0085 android:switcher:2131361925:1404172800000} FragmentManager misc state: mActivity=com.example.AssetHistoryActivity@5342b114 mContainer=android.support.v4.app.FragmentActivity$2@53447bb4 mCurState=5 mStateSaved=false mDestroyed=false
Viewの階層を確認する
上記「FragmentManagerの使用状況を確認する」のコマンドを実行した方はわかったと思いますが、表示中の画面を構成しているViewの階層構造を確認することもできます。
AndroidDeviceMonitorでのDDMSやHierarchyViewでも確認できますが、とりあえずサクッと確認したい、という方におすすめです。
コマンド
adb shell dumpsys activity top
出力
ACTIVITY com.example.timersample/.MyActivity 53395f54 pid=1745 Local FragmentActivity 531efc98 State: mCreated=truemResumed=true mStopped=false mReallyStopped=false mLoadersStarted=true FragmentManager misc state: mActivity=com.example.timersample.MyActivity@531efc98 mContainer=android.support.v4.app.FragmentActivity$2@531f09a8 mCurState=5 mStateSaved=false mDestroyed=false View Hierarchy: com.android.internal.policy.impl.PhoneWindow$DecorView{531f3274 V.E..... ... 0,0-768,1184} android.widget.LinearLayout{531f3854 V.E..... ... 0,0-768,1184} android.view.ViewStub{531f4490 G.E..... ... 0,0-0,0 #1020354} android.widget.FrameLayout{531f466c V.E..... ... 0,50-768,1184} android.support.v7.internal.widget.ActionBarOverlayLayout{531f92d4 V.ED.... ... 0,0-768,1134 #7f080031 app:id/decor_content_parent} android.support.v7.internal.widget.NativeActionModeAwareLayout{531fa058 V.ED.... ... 0,112-768,1134 #1020002 android:id/content} android.widget.RelativeLayout{53207044 V.E..... ... 0,0-768,1022} android.widget.TextView{53207348 V.ED.... ... 32,32-181,70} android.support.v7.internal.widget.ActionBarContainer{531fa304 V.ED.... ... 0,0-768,112 #7f080032 app:id/action_bar_container} android.support.v7.widget.Toolbar{531fa874 V.E..... ... 0,0-768,112 #7f080033 app:id/action_bar} android.widget.TextView{53210768 V.ED.... ... 32,29-272,83} android.support.v7.widget.ActionMenuView{5321697c V.E..... ... 696,0-768,112} android.support.v7.widget.ActionMenuPresenter$OverflowMenuButton{531eb2e0 VFED..C. ... 0,8-72,104} android.support.v7.internal.widget.ActionBarContextView{53200c60 G.E..... ... 0,0-0,0 #7f080034 app:id/action_context_bar}
タイマーが正しくセットされているかを確認する
AlarmManagerでタイマーを設定した場合に正しくセットされたかどうかを確認することができます。 タイマーを設定したものの実際に起動するまでにじっと待っていたり、 結局タイマー設定が間違っていたりして何も起きずガッカリする、といった手間が防げます。
コマンド
adb shell dumpsys alarm
結果
RTC_WAKEUP #0: Alarm{53547c40 type 0 com.example.timersample} type=0 when=+2m55s486ms repeatInterval=0 count=0 operation=PendingIntent{535be2e0: PendingIntentRecord{53553a2c com.example.timersample broadcastIntent}}
あと2分55秒後にPendingIntentが起動することがわかります。
DBのクエリを確認する
アプリケーションでDatabaseを使用する場合、実際に発行したクエリを確認したい場合があります。 ログを出力して確認することもありますが、dumpsysコマンドで簡単に確認することができます。
コマンド
adb shell dumpsys dbinfo
Connection pool for /data/data/com.android.providers.settings/databases/settings.db: Open: true Max connections: 4 Available primary connection: Connection #0: isPrimaryConnection: true onlyAllowReadOnlyOperations: false Most recently executed operations: 0: [2014-11-12 17:01:35.860] executeForLastInsertedRowId took 6ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["1", "mtp_running_status"] 1: [2014-11-12 17:01:35.860] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 2: [2014-11-12 17:01:20.907] executeForLastInsertedRowId took 5ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["0", "mtp_running_status"] 3: [2014-11-12 17:01:20.907] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 4: [2014-11-12 17:01:10.940] executeForLastInsertedRowId took 4ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["1", "mtp_running_status"] 5: [2014-11-12 17:01:10.940] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 6: [2014-11-12 13:19:04.766] executeForLastInsertedRowId took 6ms - succeeded, sql="INSERT INTO secure(value,name) VALUES (?,?)", bindArgs=["0", "wifi_on"] 7: [2014-11-12 13:19:04.766] prepare took 0ms - succeeded, sql="INSERT INTO secure(value,name) VALUES (?,?)" 8: [2014-11-12 11:59:57.958] executeForLastInsertedRowId took 54ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["0", "mtp_running_status"] 9: [2014-11-12 11:59:57.958] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 10: [2014-11-12 10:57:40.342] executeForLastInsertedRowId took 4ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["1", "mtp_running_status"] 11: [2014-11-12 10:57:40.342] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 12: [2014-11-12 10:53:05.932] executeForLastInsertedRowId took 110ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["1", "mtp_drive_display"] 13: [2014-11-12 10:53:05.932] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 14: [2014-11-12 10:53:05.796] executeForLastInsertedRowId took 127ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["0", "mtp_running_status"] 15: [2014-11-12 10:53:05.796] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)" 16: [2014-11-12 10:52:23.219] executeForLastInsertedRowId took 2ms - succeeded, sql="INSERT INTO secure(value,name) VALUES (?,?)", bindArgs=["1", "wifi_on"] 17: [2014-11-12 10:52:23.219] prepare took 0ms - succeeded, sql="INSERT INTO secure(value,name) VALUES (?,?)" 18: [2014-11-12 10:23:27.139] executeForLastInsertedRowId took 1ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)", bindArgs=["1", "mtp_running_status"] 19: [2014-11-12 10:23:27.139] prepare took 0ms - succeeded, sql="INSERT INTO system(value,name) VALUES (?,?)"
SyncAdapterの確認
SyncAdapterはアカウントに紐付いてデータの同期を任意のタイミングで行うことのできる非常に便利な機能です。 ただし、Timerと同様、実際に登録されているのか、あとどのくらい後に実行されるのか、 実際に起動したのか、など状況が見えづらかったりします。
こちらもdumpsysコマンドで確認することができます。
コマンド
adb shell dumpsys content
細かいデータの説明は割愛しますが、 ・アカウント毎に紐付いているSyncAdapterの一覧 ・最近のSync履歴 などが確認できます。
他にも色々
取れる情報が多いので今回は主要なところだけをPickUPしました。 アプリケーションの内容によっては、位置情報など他にも有益な情報が沢山あると思います。 この機会に是非dumpsysコマンドを使ってみてください。
最後に
マネーフォワードではiOSアプリが機能先行している状況ですが、Androidの開発体制も強化して積極的にバージョンアップしていく予定です。 Androidをこよなく愛するイケてるAndroidエンジニアを募集しております。
マネーフォワード採用サイト https://recruit.moneyforward.com/
また、会社間での交流も大歓迎です。交流会をご希望の方は、こちらまでどうぞ。 corp@moneyforward.com