環境
- 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ファイル
内容はなんでも良かったので以前と同じ
syntax = "proto3";
package test;
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
使いたいAndroidプロジェクトのbuild.gradleにProtocolBuffers用の記述を追加
buildscript {
repositories {
//...
mavenCentral()
}
dependencies {
//...
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
}
}
//...
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にしてください
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);
}
});
}
}
サーバー側
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からも受け取れることを確認済み
以上