Más contenido relacionado La actualidad más candente (20) Similar a Google Cloud Endpointsによる API構築 (20) Más de Keiji Ariyama (10) Google Cloud Endpointsによる API構築3. Who am I?
•
有山 圭二(ありやまけいじ)
有限会社シーリス代表
•
•
Androidアプリ開発者
日本Androidの会 関西支部
13. 準備
• Google App Engine Java SDK (1.7.5 or newer)
• Java 7
• Apache Maven
•
You need to be familiar with development using Google
App Engine and the Java runtime.
14. 手順
•
Create your backend API project (using Maven to do this is the
easiest method), then write your API backend code.
•
Annotate your API backend code, so classes and client libraries
can be generated from it. (Alternatively, use the Google Plugin for
Eclipse, which can annotate for you.)
•
Generate the client library using Maven, or alternatively, the
endpoints.sh command line tool. (Another alternative is to use
the Google Plugin for Eclipse to generate the client library.)
•
Write your client app, using the client library when making calls
to the API backend.
27. AppEngineプロジェクト作成
$ cd coconeco
$ mvn archetype:generate
Choose a number or apply filter,
com.google.appengine.archetypes:skeleton-archetype
!
Choose com.google.appengine.archetypes:skeleton-archetype version:
2: 1.7.5
!
groupId: io.keiji.coconeco
!
artifactId: coconeco
!
version:
!
padkage:
28. appengine-web.xmlを編集
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>coconeco</application>
<version>${appengine.app.version}</version>
<threadsafe>true</threadsafe>
!
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
coconeco/src/main/java/webapp/WEB-INF/appengine-web.xml
29. pom.xmlを編集
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
!
!
!
!
!
!
!
!
!
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<groupId>io.keiji.coconeco</groupId>
<artifactId>coconeco</artifactId>
<properties>
<appengine.app.version>1</appengine.app.version>
<appengine.target.version>1.7.5</appengine.target.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Compile/runtime dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.target.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-stubs</artifactId>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<version>2.5.1</version>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<archiveClasses>true</archiveClasses>
<webResources>
<!-- in order to interpolate version from pom into appengine-web.xml -->
<resource>
<directory>${basedir}/src/main/webapp/WEB-INF</directory>
<filtering>true</filtering>
<targetPath>WEB-INF</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>${appengine.target.version}</version>
</plugin>
</plugins>
</build>
</project>
coconeco/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
!
!
!
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<groupId>io.keiji.coconeco</groupId>
<artifactId>coconeco</artifactId>
<properties>
<appengine.app.version>1</appengine.app.version>
<appengine.target.version>1.8.9</
appengine.target.version>
<project.build.sourceEncoding>UTF-8</
project.build.sourceEncoding>
</properties>
!
30. endpointsの依存を追加
<dependencies>
<!-- Compile/runtime dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.target.version}</version>
</dependency>
…
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-endpoints</artifactId>
<version>${appengine.target.version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
…
coconeco/pom.xml
32. 初期処理
$ mvn install
JDK 7でないとエラーが発生
Unsupported major.minor version 51.0
http://www.coderanch.com/t/624933/tools/Maven-dependency-mystery-Google-App
34. package io.keiji.coconeco;
!
public class Entry {
public Double latitude;
public Double longitude;
public String geoHash;
public String url;
public String description;
!
!
!
!
!
!
!
!
!
!
public Entry() {
}
public Entry(String url) {
this.url = url;
}
public Double getLatitude() {
return latitude;
}
public Double getLongitude() {
return longitude;
}
public String getGeoHash() {
return geoHash;
}
public void setLocation(Double lat, Double lon) {
this.latitude = lat;
this.longitude = lon;
this.geoHash = Geohash.encode(lat, lon);
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
};
}
coconeco/src/main/java/io/keiji/coconeco/Entry.java
35. package io.keiji.coconeco;
!
import java.util.ArrayList;
!
public class EntryList {
public static ArrayList<Entry> entries = new ArrayList<Entry>();
!
static {
entries.add(
new Entry("http://i.gzn.jp/img/2009/03/13/swimming_cat/
swimming_cat.jpg"));
entries.add(
new Entry("http://upload.wikimedia.org/wikipedia/ja/
1/16/%E6%97%A5%E6%9C%AC%E7%8C%AB_2008-4.jpg"));
}
!
public Entry getEntryList(Integer id) {
return entries.get(id);
}
}
coconeco/src/main/java/io/keiji/coconeco/EntryList.java
36. package io.keiji.coconeco;
!
import java.util.ArrayList;
!
import javax.inject.Named;
!
import com.google.api.server.spi.config.Api;
!
@Api(name = "coconeco", version = "v1")
public class EntryList {
public static ArrayList<Entry> entries = new ArrayList<Entry>();
!
static {
entries.add(
new Entry("http://i.gzn.jp/img/2009/03/13/swimming_cat/
swimming_cat.jpg"));
entries.add(
new Entry("http://upload.wikimedia.org/wikipedia/ja/
1/16/%E6%97%A5%E6%9C%AC%E7%8C%AB_2008-4.jpg"));
}
!
public Entry getEntryList(@Named("id") Integer id) {
return entries.get(id);
}
}
coconeco/src/main/java/io/keiji/coconeco/EntryList.java
38. テストサーバーの起動
$ mvn appengine:devserver
!
…
!
[INFO] INFO: Started SelectChannelConnector@127.0.0.1:8080
[INFO] Feb 14, 2014 9:15:02 PM
com.google.appengine.tools.development.JettyContainerService startHotDeployScanner
[INFO] INFO: Full scan of the web app in place every 5s.
[INFO] Feb 14, 2014 9:15:02 PM com.google.appengine.tools.development.AbstractModule
startup
[INFO] INFO: Module instance default is running at http://localhost:8080/
[INFO] Feb 14, 2014 9:15:02 PM com.google.appengine.tools.development.AbstractModule
startup
[INFO] INFO: The admin console is running at http://localhost:8080/_ah/admin
[INFO] Feb 14, 2014 9:15:02 PM com.google.appengine.tools.development.DevAppServerImpl
doStart
[INFO] INFO: Dev App Server is now running
51. package io.keiji.coconeco;
!
import java.util.ArrayList;
!
import javax.inject.Named;
!
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
!
@Api(name = "coconeco", version = "v1")
public class EntryList {
public static ArrayList<Entry> entries = new ArrayList<Entry>();
!
static {
entries.add(new Entry("http://i.gzn.jp/img/2009/03/13/swimming_cat/
swimming_cat.jpg"));
entries.add(new Entry("http://upload.wikimedia.org/wikipedia/ja/
1/16/%E6%97%A5%E6%9C%AC%E7%8C%AB_2008-4.jpg"));
}
!
!
!
public ArrayList<Entry> getEntryList() {
return entries;
}
@ApiMethod(name = "get", httpMethod = "get")
public Entry getEntry(@Named("id") Integer id) {
return entries.get(id);
}
}
coconeco/src/main/java/io/keiji/coconeco/EntryList.java
52. package io.keiji.coconeco;
!
import java.util.ArrayList;
!
import javax.inject.Named;
!
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
!
@Api(name = "coconeco", version = "v1")
public class EntryList {
public static ArrayList<Entry> entries = new ArrayList<Entry>();
!
static {
entries.add(new Entry("http://i.gzn.jp/img/2009/03/13/swimming_cat/
swimming_cat.jpg"));
entries.add(new Entry("http://upload.wikimedia.org/wikipedia/ja/
1/16/%E6%97%A5%E6%9C%AC%E7%8C%AB_2008-4.jpg"));
}
!
// 略
!
!
@ApiMethod(name = "insert", httpMethod = "post")
public Entry insertEntry(
@Named("lat") Double lat,
@Named("lng") Double lng,
@Named("url") String url,
@Named("desc") String description) {
Entry entry = new Entry();
entry.setLocation(lat, lng);
entry.setUrl(url);
entry.setDescription(description);
!
// 保存
entries.add(entry);
!
!
return entry;
}
}
coconeco/src/main/java/io/keiji/coconeco/EntryList.java
57. !
@ApiMethod(name = "insert", httpMethod = "post")
public Entry insertEntry(
@Named("lat") Double lat,
@Named("lng") Double lng,
@Named("url") String url,
@Named("desc") String description) {
Entry entry = new Entry();
entry.setLocation(lat, lng);
entry.setUrl(url);
entry.setDescription(description);
!
public void setLocation(Double lat, Double lon) {
this.latitude = lat;
this.longitude = lon;
this.geoHash = Geohash.encode(lat, lon);
}
coconeco/src/main/java/io/keiji/coconeco/Entry.java
// 保存
entries.add(entry);
!
return entry;
}
coconeco/src/main/java/io/keiji/coconeco/EntryList.java
{
"latitude": 34.697418,
"longitude": 135.516559,
"geoHash": "xn0m7u2phew2",
"url": "http%3A%2F%2Fi.gzn.jp%2Fimg%2F2009%2F03%2F13%2Fswimming_cat%2Fswimming_cat.jpg",
"description": "%E3%82%B7%E3%83%BC%E3%83%AA%E3%82%B9%E6%9C%AC%E7%A4%BE"
}
58. package io.keiji.coconeco;
!
import java.util.ArrayList;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
!
import javax.inject.Named;
!
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
@ApiMethod(name = "nyaaby", httpMethod = "get")
public ArrayList<Entry> getEntry(
@Named("lat") Double lat,
@Named("lng") Double lng) {
!
!
!
ArrayList<Entry> result = new ArrayList<Entry>();
String geoHash = Geohash.encode(lat, lng);
// 検索条件
!
@Api(name = "coconeco", version = "v1")
public class EntryList {
public static ArrayList<Entry> entries = new ArrayList<Entry>();
!
private static final int GEO_THRESHOLD = 7;
!
static {
Entry entry1 = new Entry();
entry1.setLocation(34.697418, 135.516559);
entry1.setUrl("http://i.gzn.jp/img/2009/03/13/swimming_cat/
swimming_cat.jpg");
try {
entry1.setDescription(
URLEncoder.encode("シーリス本社", "UTF-8"));
!
entries.add(entry2);
} catch (UnsupportedEncodingException e) {}
}
!
// 検索処理
search(entries, result, term);
!
!
entries.add(entry1);
} catch (UnsupportedEncodingException e) {}
Entry entry2 = new Entry();
entry2.setLocation(35.675595, 139.767504);
entry2.setUrl("http://upload.wikimedia.org/wikipedia/ja/
1/16/%E6%97%A5%E6%9C%AC%E7%8C%AB_2008-4.jpg");
try {
entry2.setDescription(
URLEncoder.encode(”シーリス東京オフィス”, ”UTF-8”));
String term = geoHash.substring(
0, geoHash.length() - GEO_THRESHOLD);
return result;
}
private static void search(ArrayList<Entry> from,
ArrayList<Entry> to, String term) {
for (Entry entry : from) {
if (entry.getGeoHash().startsWith(term)) {
to.add(entry);
}
}
}
}
coconeco/src/main/java/io/keiji/coconeco/EntryList.java
61. {
"items" : [ {
"latitude" : 35.675595,
"longitude" : 139.767504,
"geoHash" : "xn76uqx96uut",
"url" : "http://upload.wikimedia.org/wikipedia/ja/1/16/%E6%97%A5%E6%9C%AC%E7%8C%AB_2008-4.jpg",
"description" : "%E3%82%B7%E3%83%BC%E3%83%AA%E3%82%B9%E6%9D%B1%E4%BA%AC%E3%82%AA
%E3%83%95%E3%82%A3%E3%82%B9"
} ]
}
62. サーバーへのupload準備
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>stable-apogee-488</application>
<version>1</version>
<threadsafe>true</threadsafe>
!
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/
logging.properties"/>
</system-properties>
</appengine-web-app>
coconeco/src/main/java/webapp/WEB-INF/appengine-web.xml
66. Beginning interaction for module default...
0% Created staging directory at: '/var/folders/tv/yr_x0jn163s4yr1gv77bk1mr0000gn/T/
appcfg4387737080071537979.tmp'
5% Scanning for jsp files.
20% Scanning files on local disk.
25% Initiating update.
28% Cloning 10 application files.
40% Uploading 6 files.
52% Uploaded 1 files.
61% Uploaded 2 files.
68% Uploaded 3 files.
73% Uploaded 4 files.
77% Uploaded 5 files.
80% Uploaded 6 files.
82% Initializing precompilation...
84% Sending batch containing 6 file(s) totaling 21KB.
90% Deploying new version.
99% Closing update: new version is ready to start serving.
99% Uploading index definitions.
!
Update for module default completed successfully.
Success.
Cleaning up temporary files for module default...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:00.795s
[INFO] Finished at: Sat Feb 15 06:07:07 JST 2014
[INFO] Final Memory: 13M/245M
[INFO] ------------------------------------------------------------------------
76. 表示する画面
package io.keiji.coconeco.android;
!
public class MainActivity extends Activity {
!
private ImageView mImage;
private TextView mDescription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImage = (ImageView) findViewById(R.id.iv_picture);
mDescription = (TextView) findViewById(R.id.tv_description);
// 付近の情報を取得
}
NyaaByTask task = new NyaaByTask();
task.execute(new Void[0]);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
!
!
!
!
!
MainActivity.java
77. 付近の情報を取得する処理
private class NyaaByTask extends AsyncTask<Void, Void, List<CoconecoEntry>> {
@Override
protected List<CoconecoEntry> doInBackground(Void... params) {
}
@Override
protected void onPostExecute(List<CoconecoEntry> result) {
super.onPostExecute(result);
if (result.size() > 0) {
CoconecoEntry entry = result.get(0);
try {
String desc = URLDecoder.decode(entry.getDescription(),
"UTF-8");
mDescription.setText(desc);
} catch (UnsupportedEncodingException e) {
}
}
}
!
!
!
!
Coconeco.Builder builder = new Coconeco.Builder(
AndroidHttp.newCompatibleTransport(), new GsonFactory(),
null);
Coconeco service = builder.build();
try {
EntryCollection result = service.nyaaby(34.697211, 135.513215)
.execute();
return result.getItems();
} catch (IOException e) {
}
return null;
}
ImageLoadTask task = new ImageLoadTask();
task.execute(new String[] { entry.getUrl() });
MainActivity.java
78. 画像を取得する処理
private class ImageLoadTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
HttpGet request = new HttpGet(url);
AndroidHttpClient client = AndroidHttpClient
.newInstance("Coconeco");
}
Bitmap bmp = null;
InputStream is = null;
try {
HttpResponse response = client.execute(request);
is = response.getEntity().getContent();
bmp = BitmapFactory.decodeStream(is);
} catch (IOException e) {
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
client.getConnectionManager().shutdown();
}
return bmp;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
mImage.setImageBitmap(result);
}
!
!
!
!
MainActivity.java