CLIでAndroidアプリのデバッグをするためにStetho Custom dumpapp Pluginsを作る

  • 14
    いいね
  • 0
    コメント

Stethoが便利なので、より活用するべく、Custom dumpapp Pluginsの作り方を調べてみました。

スクリーンショット 2015-05-28 21.54.46.png

Stethoについて

まず、Stethoについてですが、Chrome DevTools上でのAndroidアプリのデバッグを可能としてくれるFacebook製ソフトウェアです。
Stetho

Stethoを使うと以下のようなことができるようになる。

Network Inspection

スクリーンショット 2015-05-28 21.55.28.png
Chrome DevToolsのNetwork Inspection(JSONレスポンスhelperや画像プレビュー、HARフォーマットでのエクスポートなど)をAndroidアプリの通信においても使えるようになる。

Database Inspection

スクリーンショット 2015-05-28 22.07.17.png
SQLiteやPreferenceの値を、Chrome DevToolsの使い慣れたUIで、確認したり書き換えたりできるようになる。

View Hierarchy

スクリーンショット 2015-05-28 21.54.33.png

Chrome DevToolsの見慣れた画面で、アプリのView hierarchyを確認することができる。
「Copy XPATH」とかも使えるので、Appiumとかでテストするときも便利そう。

dumpapp

スクリーンショット 2015-05-28 22.16.54.png
これは他と少し毛色の違う機能で、dumpappはChrome DevToolsを使ってではなく、コマンドライン上からのAndroidアプリのデバッグを可能としてくれる。
さらにdumpappは拡張することができる(Custom dumpapp Plugins)。
そのCustom dumpapp Pluginsの作リ方に関しては、「See the stetho-sample project for more details.」と書いてあるのみで、他に説明は無いっぽいので、サンプルを参照しながら、実際に作ってみることにする。

Stethoの導入

build.gradle

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つ。

Stetho.java
plugins.add(new HprofDumperPlugin(context));
plugins.add(new SharedPreferencesDumperPlugin(context));
plugins.add(new CrashDumperPlugin());

Stetho.java

SharedPreferencesDumperPlugin

SharedPreferenceの値を出力したり書き換えたりできる。
SharedPreferencesDumperPlugin.java

write

# dumpapp prefs write <path> <key> <boolean|int|long|float|string|set> <value>
dumpapp prefs write shikato shikatokey string shikatoval

print

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。

HelloWorldDumperPlugin.java
// 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>");
  }
}

HelloWorldDumperPlugin.java

作ったCoustom dumpapp Pluginを有効にするには以下の様に実装する。

SampleDebugApplication.java
  @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;
    }
  }

SampleDebugApplication.java

HelloWorldDumperPluginを実行すると、こんな感じに出力される。
スクリーンショット 2015-05-28 22.32.16.png

InfoDumperPlugin

簡単に作れそうなので、実際に作ってみる。

作ってみたのは、dpiやBuildConfigの値など、コマンドライン上から確認できると便利そうな値を出力するプラグイン。

info-dumper

screen gif

今のところ以下のような情報を表示できる。

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