LoginSignup
1
0

More than 5 years have passed since last update.

protocol buffersでAndroidからnode.jsサーバーへの疎通確認

Posted at

環境

  • Mac OS X 10.12 or 10.11.6
  • libprotoc 3.2.0

Android側
* Android Studio 2.2
* JDK jdk1.8.0_111

サーバー側
* node v4.5.0(npm 2.15.9)
* protocol-buffers@3.2.1
* connect@3.6.0

やること

protobuf-gradle-pluginを使ってサーバーと通信
Androidでシリアライズ、デシリアライズができることとサーバーとの疎通を確認する

クラス生成手順等は以前の記事と一緒なので省略

Androidとnodeだけなら上記のprotocコマンドがなくても動かせるはず

参考

ほぼこちらそのまま
http://qiita.com/trairia/items/45488e9eee197d58ed7d
公式
https://github.com/google/protobuf-gradle-plugin

.protoファイル

内容はなんでも良かったので以前と同じ

search_request.proto

syntax = "proto3";

package test;

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

使いたいAndroidプロジェクトのbuild.gradleにProtocolBuffers用の記述を追加

build.gradle

buildscript {
    repositories {
        //...
        mavenCentral()
    }
    dependencies {
        //...
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
    }
}

//...

app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'

android {
    //省略
}
// protobuf プラグインの設定
protobuf {
    // protoc の設定
    protoc {
        artifact = "com.google.protobuf:protoc:3.0.0"
    }
    // javalite プラグインを使う
    plugins {
        lite {
            artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
        }
    }
    // タスク関連
    generateProtoTasks {
        all().each { task ->
            task.plugins {
                lite {}
            }
        }
    }
}

dependencies {
    //...
    compile 'com.google.protobuf:protobuf-lite:3.0.0'
    compile 'com.squareup.okhttp:okhttp:2.7.5'
}

.protoファイルを{プロジェクト}/app/src/main/proto/ に追加する

app/src/main
 ├proto
 │   └search_request.proto
 └java
    └com/example/testapp
        └MainActivity.java

上記をするだけでパッケージ内にSearchRequestOuterClass.SearchRequestクラスが自動生成され、プロジェクトのjavaファイルからアクセスできるようになるはずです
オブジェクトを作る際は下記のような感じ

SearchRequestOuterClass.SearchRequest.newBuilder()
                .setQuery("xxxx")
                .setPageNumber(1)
                .setResultPerPage(1)
                .build();

通信はOKHttpでテストしました
node.jsでローカルサーバーをたててシリアライズしたデータを送信、かつレスポンスでサーバー側もシリアライズしたデータを送り、相互に疎通とシリアライズ、デシリアライズの確認

クライアント側

通信先をhttp://127.0.0.1:3000/ にしていますが、例えばローカルPCでnodeサーバーたてて実機Androidでアクセスするなら「127.0.0.1」の部分を書き換える必要があり、同じwifiにつないでMac側のifconfig等で取得したアクセス可能なIPにしてください

MainActivity.java
public class MainActivity extends AppCompatActivity {
    private String TAG = getClass().getName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... params) {
                request();
                return null;
            }
        }.execute();

    }
    //適当なデータ生成
    public static SearchRequestOuterClass.SearchRequest getData(){
        return SearchRequestOuterClass.SearchRequest.newBuilder()
                .setQuery("huga")
                .setPageNumber(1)
                .setResultPerPage(1)
                .build();
    }

    private void request(){
        postBinary("http://127.0.0.1:3000/", new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.d(TAG, "request --- onFailure:"+e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                Log.d(TAG, "request --- onResponse:"+response);
                SearchRequestOuterClass.SearchRequest data = SearchRequestOuterClass.SearchRequest.parseFrom(response.body().bytes());
                Log.d(TAG, "response --- data.getQuery:"+data.getQuery());
            }
        });
    }

    private void postBinary(String url, final Callback callback){
        OkHttpClient client = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        builder.url(url).post(RequestBody.create(MediaType.parse("application/octet-stream"), getData().toByteArray()));
        Request request = builder.build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request call, IOException e){
                Log.d(TAG, "onFailure --- e:"+e);
                if(callback != null) callback.onFailure(call, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {

                if(callback != null) callback.onResponse(response);
            }
        });
    }
}

サーバー側

server.js

var fs = require('fs')
var protobuf = require('protocol-buffers')
var messages = protobuf(fs.readFileSync('search_request.proto'))
//適当なメッセージ生成
var buf = messages.SearchRequest.encode({
  query: "hoge",
  page_number: 1234
})

require('connect')().use(function(req, res, next) {
    next();
}).use(function(req, res) {
    //Android Javaからのリクエストをシリアライズ
    var body = [];
    req.on('data', function (data) {
        // body +=data;
        body.push(data);
    });
    req.on('end',function(){
        var buf = Buffer.concat( body );

        try {
            console.log(body);
            console.log(buf);
            var obj = messages.SearchRequest.decode(buf)
            console.log(obj)
        } catch (e) {
            console.log(e)
        }
        res.writeHead(200, {'Content-Type': 'application/octet-stream'});
        //メッセージをレスポンス
        res.write(buf, 'binary');
        res.end();
    });
  }).listen(3000);

サーバー起動しておく(Ctl+Cで切れるので、起動しながらiOS側実行する)

node server.js

server.jsがObjective-Cから受け取る時と違い、byteをvar body = [];にpushしていくように書かないとデシリアライズに失敗しました。上記のコードでObjective-Cからも受け取れることを確認済み

以上

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0