艦隊シミュレーター@androidで外部DBを使ってみたいので、DBを使用する方法としてGoogle App Engine を試してみる。
多くのサイトではEclipseを使って開発しているみたいだが、Android Studioでも出来るみたいなので調べてみる。
私はあまり従いませんが、一応Googleさんがこれをどこに書いているのかぐらいは示しておきます。
Quickstart _ Cloud Tools for Android Studio _ Google Cloud Platform
How-to Guides _ Cloud Tools for Android Studio _ Google Cloud Platform
gradle-appengine-templates_HelloEndpoints at master · GoogleCloudPlatform_gradle-appengine-templates · GitHub
Android Studio + Google App Engine でお手軽バックエンドサーバ構築 – Qiita
まずこの記事の通りにやってみたが、新規作成したモジュールを実行したら以下のエラーがでた。
“C:\Program Files\android-studio_2_Preview4\jre\bin\java” -javaagent:C:\Users\aillice\.gradle\appengine-sdk\appengine-java-sdk-1.9.34\lib\agent\appengine-agent.jar -Xbootclasspath/p:C:\Users\aillice\.gradle\appengine-sdk\appengine-java-sdk-1.9.34\lib\override\appengine-dev-jdk-overrides.jar -Didea.launcher.port=7533 “-Didea.launcher.bin.path=C:\Program Files\android-studio_2_Preview4\bin” -Dfile.encoding=windows-31j -classpath “C:\Users\aillice\.gradle\appengine-sdk\appengine-java-sdk-1.9.34\lib\appengine-tools-api.jar;C:\Program Files\android-studio_2_Preview4\lib\idea_rt.jar” com.intellij.rt.execution.application.AppMain com.google.appengine.tools.development.DevAppServerMain –address=localhost –port=8080 D:\android_studio_project\KanColleOrg\backend\build\exploded-app
8 15, 2016 6:06:38 午後 java.util.prefs.WindowsPreferences <init>
警告: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(…) returned error code 5.
********************************************************
There is a new version of the SDK available.
———–
Latest SDK:
Release: 1.9.42
Timestamp: Sat Jul 16 03:45:47 JST 2016
API versions: [1.0]
———–
Your SDK:
Release: 1.9.34
Timestamp: Fri Feb 12 04:36:15 JST 2016
API versions: [1.0]
———–
Please visit https://developers.google.com/appengine/downloads for the latest SDK.
********************************************************
8 15, 2016 6:06:40 午後 com.google.appengine.tools.development.SystemPropertiesManager setSystemProperties
情報: Overwriting system property key ‘java.util.logging.config.file’, value ‘C:\Users\aillice\.gradle\appengine-sdk\appengine-java-sdk-1.9.34\config\sdk\logging.properties’ with value ‘WEB-INF/logging.properties’ from ‘D:\android_studio_project\KanColleOrg\backend\build\exploded-app\WEB-INF\appengine-web.xml’
************************************************
Could not open the requested socket: Address already in use: bind
Try overriding –address and/or –port.
Process finished with exit code 2
新規作成したモジュールのbuild.gradleのdependenciesの
appengineSdk ‘com.google.appengine:appengine-java-sdk:1.9.42’
compile ‘com.google.appengine:appengine-endpoints:1.9.42’
compile ‘com.google.appengine:appengine-endpoints-deps:1.9.42’
この辺を全部1.9.34から1.9.42に変更したら解決した。
その後もなんだかエラーが出て実行できなかったが、他のソフトで8080ポートを使用していてアクセス出来ないみたいだった。ちょうど以下の様なエラーメッセージが出てた。
android – Google Cloud Endpoint – Error when project runs backend configuration – Stack Overflow
この辺を乗り越えたらちゃんと実行できた。実行してhttp://localhost:8080/にアクセスすると、
ヽ(=´▽`=)ノあーよかった。ここまで気づくのに結構時間がかかってしまった…。
では気を取り直して次はこっちの記事にしたがってみます。
Android Studioに追加されたGoogle App Engineテンプレートを試そう 導入編 – クックパッド開発者ブログ
自動生成されたjavaファイルは、MyEndpoint.javaとMyBean.javaの2つ。
MyEndpointはエンドポイントを定義するクラスで、アノテーションで宣言しているらしい。(ん?エンドポイントって何?)
@ApiではAPI名、バージョンなどを定義する。
@ApiMethodではname属性にエンドポイント名を定義するらしい。
コード見たほうが早いよねって話でほい↓。
MyEndpoint.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package com.example.aillice.myapplication.backend; import com.google.api.server.spi.config.Api; import com.google.api.server.spi.config.ApiMethod; import com.google.api.server.spi.config.ApiNamespace; import javax.inject.Named; /** * An endpoint class we are exposing */ @Api( name = "myApi", version = "v1", namespace = @ApiNamespace( ownerDomain = "backend.myapplication.aillice.example.com", ownerName = "backend.myapplication.aillice.example.com", packagePath = "" ) ) public class MyEndpoint { /** * A simple endpoint method that takes a name and says Hi back */ @ApiMethod(name = "sayHi") public MyBean sayHi(@Named("name") String name) { MyBean response = new MyBean(); response.setData("Hi, " + name); return response; } } |
MyBean.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.example.aillice.myapplication.backend; /** * The object model for the data we are sending through endpoints */ public class MyBean { private String myData; public String getData() { return myData; } public void setData(String data) { myData = data; } } |
MyBeanクラスはデータを持たせるための簡単なクラスで。MyEndpointクラスのsayHi()メソッドは、フォームに入力された値をMyBeanクラスにセットして返すだけのメソッド。
えーと、書き忘れてましたけどlocalhost:8080ではじめに表示されるページでは、フォームに文字を入力して右のボタンを押すと、ページの上の方に「Hi, (入力した文字)」と表示されます。はい。
そんでsayHi()メソッドがどのようにして使われているのかわからんので、自動生成されたsrc/main/webapp/index.htmlを見て、「sayHi」で検索かけると見つけました。javascriptで実行してるんですかね。↓これ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<script type="text/javascript">// <![CDATA[ // A function that attaches a "Say Hello" button click handler function enableClick() { document.getElementById('helloButton').onclick = function() { var name = document.getElementById('nameInput').value; gapi.client.myApi.sayHi({'name': name}).execute( function(response) { var outputAlertDiv = document.getElementById('outputAlert'); outputAlertDiv.style.visibility = 'visible'; if (!response.error) { outputAlertDiv.className = 'alert alert-success'; outputAlertDiv.innerHTML = ' <h2>' + response.result.data + '</h2> '; } else if (response.error) { outputAlertDiv.className = 'alert alert-danger'; outputAlertDiv.innerHTML = '<b>Error Code: </b>' + response.error.code + ' [' + response.error.message + ']'; } } ); return false; } } // This is called initially function init() { var apiName = 'myApi'; var apiVersion = 'v1'; var apiRoot = 'https://' + window.location.host + '/_ah/api'; if (window.location.hostname == 'localhost' || window.location.hostname == '127.0.0.1' || ((window.location.port != "") && (window.location.port > 1023))) { // We're probably running against the DevAppServer apiRoot = 'http://' + window.location.host + '/_ah/api'; } var callback = function() { enableClick(); } gapi.client.load(apiName, apiVersion, callback, apiRoot); } // ]]></script> |
↑これの6行目にgapi.client.myApi.sayHi({‘name’: name})というのがありますね。
これ以上のことはjavascriptをやっている人間ではないのでわかりません( ;∀;)
続いて同じサイトの「アプリケーションでAPIを利用する」の辺りからならわかりそうなのでざっと見ていきます。
クライアントライブラリは@Apiのnameとnamespaceに従うので、この場合は「com.example.aillice.myapplication.backend.myApi.MyApi」がエンドポイント用のクライアントになるそうです?
通信処理は非同期処理で行うので、以下の処理はAsyncTaskクラスのサブクラス内に書きます。(そういえばAsyncTaskもいじったことありませんでした(´・ω・`))
app(MainActivity.javaがあるパッケージ)にasynctaskというパッケージを作ってSayHiAsyncTaskクラスを作りました。
SayHiAsyncTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.aillice_android.kancolleorg.asynctask; import android.os.AsyncTask; import com.example.aillice.myapplication.backend.myApi.MyApi; import com.example.aillice.myapplication.backend.myApi.model.MyBean; import com.google.api.client.extensions.android.http.AndroidHttp; import com.google.api.client.extensions.android.json.AndroidJsonFactory; import java.io.IOException; /** * Created by aillice on 2016/08/15. */ public class SayHiAsyncTask extends AsyncTask<String, Void, String> { public static final String TAG = SayHiAsyncTask.class.getSimpleName(); private MyApi getApiClient() { return new MyApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null) //if you use android emulator, you should replace to "10.0.2.2". .setRootUrl("http://10.0.2.2:8080") // .setRootUrl("http://localhost:8080/") .build(); } @Override protected String doInBackground(String... strings) { try { MyBean response = getApiClient().sayHi(strings[0]).execute(); return response.getData(); } catch (IOException e) { return e.getMessage(); } } } |
こんな感じで書いたは良いけど、何パターンもserRootUrl()の中身を書き換えてもローカルホストに接続できなかった。
実機でもエミュレータでもダメでした。時間取られすぎて死にそうなのでとりあえず諦めておく。
この辺はネットワークの知識が足りてないんだろうなあと実感しました。
ローカルじゃなくてサンプル用に適当やつをデプロイしてみればうまくいくのかね。
(´・ω・`)外部DB扱えるようになるまでが遠いなあ。