Stethoが便利なので、より活用するべく、Custom dumpapp Pluginsの作り方を調べてみました。
Stethoについて
まず、Stethoについてですが、Chrome DevTools上でのAndroidアプリのデバッグを可能としてくれるFacebook製ソフトウェアです。
Stetho
Stethoを使うと以下のようなことができるようになる。
Network Inspection
Chrome DevToolsのNetwork Inspection(JSONレスポンスhelperや画像プレビュー、HARフォーマットでのエクスポートなど)をAndroidアプリの通信においても使えるようになる。
Database Inspection
SQLiteやPreferenceの値を、Chrome DevToolsの使い慣れたUIで、確認したり書き換えたりできるようになる。
View Hierarchy
Chrome DevToolsの見慣れた画面で、アプリのView hierarchyを確認することができる。
「Copy XPATH」とかも使えるので、Appiumとかでテストするときも便利そう。
dumpapp
これは他と少し毛色の違う機能で、dumpappはChrome DevToolsを使ってではなく、コマンドライン上からのAndroidアプリのデバッグを可能としてくれる。
さらにdumpappは拡張することができる(Custom dumpapp Plugins)。
そのCustom dumpapp Pluginsの作リ方に関しては、「See the stetho-sample project for more details.」と書いてあるのみで、他に説明は無いっぽいので、サンプルを参照しながら、実際に作ってみることにする。
Stethoの導入
build.gradle
compile 'com.facebook.stetho:stetho:1.1.1'
// 以下はNetwork inspectionする場合に必要
// OkHttpを使う場合
compile 'com.facebook.stetho:stetho-okhttp:1.1.1'
// UrlConnectionを使う場合
compile 'com.facebook.stetho:stetho-urlconnection:1.1.1'
Applicatioクラス
public class MyApplication extends Application {
public void onCreate() {
super.onCreate();
Stetho.initialize(
Stetho.newInitializerBuilder(this)
// dumpappを有効にする
.enableDumpapp(
Stetho.defaultDumperPluginsProvider(this))
// Chrome DevToolsでのデバッグを有効にする
.enableWebKitInspector(
Stetho.defaultInspectorModulesProvider(this))
.build());
}
}
Network Inspection機能を使う場合
OkHttp
OkHttpのNetworkInterceptorsにStethoInterceptorを追加するだけでOK。
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());
HttpURLConnection
HttpURLConnectionの場合は、ちょっと面倒。
以下のサンプルのように実装する必要がある。
Networker.java
dumpappを使う場合
dumpappコマンド(stethoのリポジトリに含まれている)のパスを環境変数に追加
export PATH=”$PATH:/path/to/stetho/scripts”
※ Python 3がインストールされてない場合はインストールが必要。
Stethoが提供するdumpapp Plugins
最初から、いくつかのdumpapp Pluginsが提供されているので、調査がてら見てみる。
Stetho.defaultDumperPluginsProvider(this)で使えるようになるのは、以下の3つ。
plugins.add(new HprofDumperPlugin(context));
plugins.add(new SharedPreferencesDumperPlugin(context));
plugins.add(new CrashDumperPlugin());
SharedPreferencesDumperPlugin
SharedPreferenceの値を出力したり書き換えたりできる。
SharedPreferencesDumperPlugin.java
write
# dumpapp prefs write <path> <key> <boolean|int|long|float|string|set> <value>
dumpapp prefs write shikato shikatokey string shikatoval
dumpapp prefs print
# 以下のように出力される
# shikato:
# shikatokey = shikatoval
CrashDumperPlugin
クラッシュを意図的に発生させることができる。
CrashDumperPlugin.java
引数で指定(デフォルトはjava.lang.Error)した例外を発生させる。(simulates a program crash)
dumpapp crash throw java.lang.NullPointerException
System.exitを使う。(simulates an abnormal Android exit strategy)
dumpapp crash exit
ProcessBuilderを使ってkillコマンドを実行。(simulates the low memory killer)
dumpapp crash kill
HprofDumperPlugin
Heap dump(HPROF)を、stdoutに出力したり、Android端末内のファイルに書き込んだりできる。
HprofDumperPlugin.java
dumpapp hprof -
Custom dumpapp Pluginsの作り方
HelloWorldなサンプル dumpapp Plugin。
// DumperPluginをimplementsする
public class HelloWorldDumperPlugin implements DumperPlugin {
private static final String NAME = "hello";
// getNameで返す値がdumpappp実行時のコマンドとなる
// ex.) appdump {{NAME}}
@Override
public String getName() {
return NAME;
}
// getNameで指定したコマンドを実行したときに呼ばれるメソッド
@Override
public void dump(DumperContext dumpContext) throws DumpException {
PrintStream writer = dumpContext.getStdout();
// {{NAME}}以降の引数が返ってくる
// appdump {{NAME}} hoge huga
Iterator<String> args = dumpContext.getArgsAsList().iterator();
String helloToWhom = args.hasNext() ? args.next() : null;
if (helloToWhom != null) {
doHello(writer, helloToWhom);
} else {
doUsage(writer);
}
}
private void doHello(PrintStream writer, String name) throws DumpUsageException {
if (TextUtils.isEmpty(name)) {
// This will print an error to the dumpapp user and cause a non-zero exit of the
// script.
throw new DumpUsageException("Name is empty");
}
writer.println("Hello " + name + "!");
}
private void doUsage(PrintStream writer) {
writer.println("Usage: dumpapp " + NAME + " <name>");
}
}
作ったCoustom dumpapp Pluginを有効にするには以下の様に実装する。
@Override
public void onCreate() {
super.onCreate();
long startTime = SystemClock.elapsedRealtime();
final Context context = this;
Stetho.initialize(
Stetho.newInitializerBuilder(context)
// DumperPluginsProviderをimplementsしたクラスを渡す
.enableDumpapp(new SampleDumperPluginsProvider(context))
.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
.build());
long elapsed = SystemClock.elapsedRealtime() - startTime;
Log.i(TAG, "Stetho initialized in " + elapsed + " ms");
}
// DumperPluginsProviderをimplementsしたクラスを用意する
private static class SampleDumperPluginsProvider implements DumperPluginsProvider {
private final Context mContext;
public SampleDumperPluginsProvider(Context context) {
mContext = context;
}
@Override
public Iterable<DumperPlugin> get() {
ArrayList<DumperPlugin> plugins = new ArrayList<DumperPlugin>();
for (DumperPlugin defaultPlugin : Stetho.defaultDumperPluginsProvider(mContext).get()) {
plugins.add(defaultPlugin);
}
// 有効にしたいPluginを追加する
plugins.add(new HelloWorldDumperPlugin());
plugins.add(new APODDumperPlugin(mContext.getContentResolver()));
return plugins;
}
}
HelloWorldDumperPluginを実行すると、こんな感じに出力される。
InfoDumperPlugin
簡単に作れそうなので、実際に作ってみる。
作ってみたのは、dpiやBuildConfigの値など、コマンドライン上から確認できると便利そうな値を出力するプラグイン。
今のところ以下のような情報を表示できる。
Command | Action |
---|---|
buildconf | BuildConfig fields. |
id | AndroidID, UUID, Advertising ID. |
dpi | dpi info. |
memory | Memory info |
network | Network info. |
permission | Required permissions. |
lastupdate | Lastupdate time. |
error | Error state info. |
tel | TelephonyManager info. |
appinfo | android.content.pm.ApplicationInfo fields. |
osbuild | android.os.Build fields. |
all | all. |
最後に
Stethoかなり便利。dumpappもっと活用していきたい。
せっかく作ったInfoDumperPluginも、もうちょっと便利にしたい。
メモ
DevToolsにdumpappが組み込まれるかも?
lynfogeek commented on 23 Feb
The DumperPlugins are great, but it would be even better to embed them into the DevTools.
One solution could be to create a new tab (like this extension does) , and list there all the enable plugins. Whenever the user click on one of them, it pulls the data and display it.
(commented on 23 Febとなってるので望み薄かもだけど)
https://github.com/facebook/stetho/issues/68