Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

環境

  • 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からも受け取れることを確認済み

以上

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away