AndroidのメソッドをHTTP経由で無理やり叩く方法


GPSや加速度、電波強度、Beacon、NFCといったセンサー類を使った開発をしていると、イベントの発火させないと動作確認が取れないものが多々あります。想定通りに動作しないとき、イベントが発火していないのか、イベントの発火後の処理の実装がそもそもおかしいのかの、切り分けに悩むことになります。そんなときイベントの発火後のメソッドだけ直接実行できたらと思うことがあります。
ここではそれを実現するためのライブラリをご紹介します。

## MethodHttpExporterライブラリ
今回紹介するライブラリはMethodHttpExporterというもので、APT(annotation processing tool)を使って開発用のコードを生成するものです。生成されたコードは小さなHTTPサーバーとなり、デバイスの外からメソッドを直接実行できる口を作成します。もちろん開発用のコードなのでリリースバージョンでは必ず外してください。

## サンプルアプリ

今回の例で使用しているサンプルアプリケーションはこちらからダウンロードできます。

[MethodHttpExporterSample](https://github.com/cattaka/MethodHttpExporterSample)

## MethodHttpExporterの組込み

Android Studioでの組込み手順は次のとおりです。

* APTを使用できるようにする
* build.gradleのdependanciesに追加する
* 直接メソッドを叩きたいクラスとメソッドにアノテーションを付加する
* 生成されたHTTPサーバーを開始させる


### APTを使用できるようにする

Android Studioを使用している場合はAPTを使用できるようにしてください。筆者は [android-apt](https://bitbucket.org/hvisser/android-apt) を使用しています。


### build.gradleのdependanciesに追加する

build.gradleのdependanciesに次の２つを追加します。

```groovy
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    apt 'net.cattaka:methodhttpexporter-apt:0.4.7'        // <- 追加した行
    compile 'net.cattaka:methodhttpexporter-core:0.4.7'   // <- 追加した行
}
```


* 直接メソッドを叩きたいクラスとメソッドにアノテーションを付加する

対象となるクラスには@ExportMethodHttpを付加します。次にメソッドに@ExportMethodHttpAttrを付加します。

次の例では単純なToastを表示するクラスで、以降ではこれらのメソッドを例に説明します。アノテーションを付加した状態で一度ビルドしましょう。問題なくビルドが終わると<元のクラス名>+HttpServerというクラスが生成されます。この例ではSimpleToastHttpServerというクラスが生成されます。

```java
@ExportMethodHttp
public class SimpleToast {
    private Context mContext;

    public SimpleToast(Context mContext) {
        this.mContext = mContext;
    }

    @ExportMethodHttpAttr
    public String doToast(String text) {
        Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
        return "Succeed";
    }
    @ExportMethodHttpAttr
    public String doNumberToast(Integer number) {
        Toast.makeText(mContext, "Number:"+number, Toast.LENGTH_SHORT).show();
        return "Succeed";
    }

    @ExportMethodHttpAttr
    public void doVoid(Integer number) {
        Toast.makeText(mContext, "void:"+number, Toast.LENGTH_SHORT).show();
    }
}
```

### 生成されたHTTPサーバーを開始させる

生成されたHTTPサーバーを開始させます。次の例ではサーバーはアプリケーションの実行中にずっと動作しても良い物なので、Applicationの中でサーバーを開始させます。もしポート番号などが衝突して開始できなければRuntimeExceptionをスローするようにしていますがこの辺りは適宜実装してください。

```java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        {   // Create Development tool and run
            SimpleToast simpleToast = new SimpleToast(this);
            SimpleToastHttpServer server = new SimpleToastHttpServer(simpleToast);
            try {
                server.run(8090);   // port number of HttpServer
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
```

## HTTPサーバー経由で実際にメソッドを叩く

アプリを起動し、WebブラウザでデバイスのHTTPサーバーを叩いてみましょう。今回の例では8090番ポートを叩きます。巧く叩けると次のようなページが表示されます。メソッド名とそれぞれの引数が表示されているのがわかります。

![20141005_1.png](https://qiita-image-store.s3.amazonaws.com/0/55138/75f96dee-098c-36b1-ddd3-3b9a21a8bf25.png)

doToastのtextに"Hello from browser"と入力し、送信を押してみましょう。doToastメソッドの戻り値である"Succeed"が表示されました。

![20141005_2.png](https://qiita-image-store.s3.amazonaws.com/0/55138/78ae139d-31f0-4d1d-a9a0-0f3ee1f86ac9.png)

そしてデバイスの画面には"Hello from browser"というToastが表示されました。

![20141005_3.png](https://qiita-image-store.s3.amazonaws.com/0/55138/1c0e7f6c-5fe3-28de-8af2-00d6f08c0537.png)

## Activityのメソッドメソッドを叩く場合

前の例ではアプリ動作中はずっと動作してもよいものだったので、HTTPサーバーを起動したままにしていました。しかしActivityの場合は何度か表示と非表示を繰り返したり、異なるインスタンスで動作する場合があるため、起動したままにしておくと不都合があります。このためActivityの場合はonStartとonStopで起動だけでなく、停止もしてあげないといけません。次の例ではonStartで起動を、onStopで停止をするようにしています。またポート番号が衝突しないように8091番を指定しています。

```java
@ExportMethodHttp
public class MyActivity extends Activity {
    MyActivityHttpServer myActivityHttpServer = new MyActivityHttpServer(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
    }

    @Override
    protected void onStart() {
        super.onStart();
        {   // Run Development tool
            try {
                myActivityHttpServer.run(8091);     // <- HTTPサーバーを起動
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        {   // Terminate Development tool
            try {
                myActivityHttpServer.terminate();    // <- HTTPサーバーを停止
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    /* 以下省略 */
}
```

## Eclipseの場合

Eclipseの場合、以下のURLよりmethodhttpexporter-aptとmethodhttpexporter-coreの最新のjarを取得してください。

* Mavenリポジトリ： http://central.maven.org/maven2/net/cattaka/

methodhttpexporter-coreはプロジェクトのlibsディレクトリに保存してください。methodhttpexporter-aptは適当なディレクトリ(factoryなど)に保存し、プロジェクトプロパティの「Java Compiler → Annotation Processing → Factory Path」に指定してください。

## まとめ

センサー類を使ったアプリ開発では、しばしば特定の状態を作り出さないと動作確認ができないことがあります。しかしその特定の状態を創りだすのが面倒な場合、動作確認が不十分となり、品質が下がってしまうことがあります。幸いAndroidは標準のフレームワークが多機能であり、HTTPサーバーのような機能が容易に使えます。これらを使って動作確認がしにくいところは別の口を使ってでも確認するようにしましょう。