App Engine 

i Play Services
Jacek Kwiecień
Schibsted Tech Polska
Zastosowania praktyczne
App Engine
https://cloud.google.com/appengine/docs
Platforma developerska hostująca aplikacje webowe w
centrach danych Google
Po co mi App Engine?
Dodawanie modułu Google Cloud do naszej aplikacji
Inne zalety zastosowania App Engine
• Automatycznie skalowalny
• Automatycznie generowane api dla aplikacji klienckiej
Android (obsługa również dla iOS)
• Możliwość wykorzystania końcówek jako klasyczne rest api
• Brak konieczności posiadania lub wynajmowania osobnego
serwera - zerowe koszty wejścia
• Do niewielkiego wykorzystania zupełnie darmowy, później
koszt jest zależny od zużycia
• Możliwość tworzenia w Python, Java, PHP i Go
Słabe strony App Engine
• Datastore jest bardzo ograniczony w stosunku do SQL
• Pierwszy deploy potrafi nastręczyć sporo problemów

(Stackoverflow na ratunek)
• Społeczność nie jest tak liczna jak dla innych popularnych
rozwiązań backendowych
• Źle zaprojektowana aplikacja o dużym zapotrzebowaniu

może pochłonąć majątek
• Brak możliwości zwracania podstawowych obiektów
odzwierciedlających typy proste
Tworzenie końcówek (endpoints)
Backend
Android Client
private class UserInfo {

public String nameAndAge;

}



@ApiMethod(name = "userInfo")

public UserInfo userInfo(@Named("name") String name, @Named("age") Integer age) {

UserInfo info = new UserInfo();

info.nameAndAge = String.format("%s, age: %d", name, age);

return info;

}
private void getUserInfo() throws IOException {

Publicapi.Builder builder = new Publicapi.Builder(

AndroidHttp.newCompatibleTransport(),

new JacksonFactory(),

null)

.setApplicationName("Test app");

builder = CloudEndpointUtils.updateBuilder(builder);

Publicapi endpoint = builder.build();



UserInfo info = endpoint.userInfo("Jacek", 29).execute();

Log.i("User", info.getNameAndAge());

}
Datastore
https://cloud.google.com/datastore/docs
Zintegrowana z App Engine baza danych typu no-SQL
Objectify
Biblioteka znacznie ułatwiająca pracę z Datastore.
@Entity

public class Invitation {



@Id

protected String id;

@Index

private String inviterId;

@Index

private String inviteeId;

@Index

private boolean pending;

@Index

private boolean accepted;

@Index

private Date createdAt;

@Index

private Date answeredAt;

}
Encja - klasa definiująca „tabelę”
Kwerendy
@ApiMethod(name = "invitations")

public RInvitations invitations(@Named("inviteeId") String inviteeId, @Named("from")
Date from) {

List<Invitation> invitations = ofy().load().type(Invitation.class)

.filter("inviteeId", inviteeId).filter("pending", true)

.filter("createdAt >", from).list();

RInvitations response = RInvitations.newInstance(invitations);

return response;

}
in, =, ==
Operatory równości:
>, >=, <, <=, in, !=
Operatory nierówności:
Bulkloader
Narzędzie pozwalające na upload dużych paczek
danych bezpośrednio z plików CSV do Datastore.
https://cloud.google.com/appengine/docs/adminconsole/datastoreadmin
appcfg.py upload_data --config_file bulkloader.yaml --url=https://quiz-
fight.appspot.com/remote_api --filename question.csv --kind=Question -e
jacek.kwiecien@gmail.com
Najlepiej do tego celu używać Google Docs!
python_preamble:
- import: base64
- import: re
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.ext.bulkload.bulkloader_wizard
- import: google.appengine.ext.db
- import: google.appengine.api.datastore
transformers:
- kind: Question
connector: csv
connector_options:
encoding: utf-8
property_map:
- property: __key__
external_name: id
export_transform: transform.key_id_or_name_as_string
import_transform: transform.none_if_empty(int)
- property: subcategoryId
external_name: subcategoryId
export_transform: transform.key_id_or_name_as_string
import_transform: transform.none_if_empty(int)
- property: question
external_name: question
Konsola developerska
• Wiele przydatnych i nieprzydatnych informacji (w tym
statystyki użycia i logi)
• Możliwość przeglądania i edycji Datastore
• Aktywacja poszczególnych API Google (np do celów
integracji z Google+ lub Youtube)
• Wgląd w kod źródłowy aplikacji oraz push-to-deploy
• Kreator okna dostępu
• Wiele, wiele innych…
Udzielanie dostępu aplikacjom klienckim
Wyświetlenie danych o kluczach w pliku keystore: keytool -list -v -keystore <sciezka_pliku>
Wyświetlenie danych o pliku apk: keytool -list -printcert -jarfile <sciezka_apk>
Przykłady zastosowania Google API
Logowanie z Google+
https://developers.google.com/+/mobile/android/sign-in
GoogleApiClient.Builder(context).addConnectionCallbacks(connectionCallbacksListener)
.addOnConnectionFailedListener(connectionFailedListener)
.addApi(Plus.API).addScope(Plus.SCOPE_PLUS_LOGIN).build();
Benefity korzystania z API Google+
Play Games
https://developers.google.com/games/services/
GoogleApiClient.Builder(context).addConnectionCallbacks(connectionCallbacksListener)
.addOnConnectionFailedListener(connectionFailedListener)
.addApi(Games.API).addScope(Games.SCOPE_GAMES).build();
Play Games
Tabele wyników
Osiągnięcia
Questy i Eventy
Multiplayer
(turowy i w czasie
rzeczywistym)
Zapis gry
Zabezpieczenie 

antypirackie
Tabele wyników
https://developers.google.com/games/services/common/concepts/leaderboards
Aktualizowanie wyniku gracza
Games.Leaderboards.loadCurrentPlayerLeaderboardScore(

googleApiClient,

LEADERBOARD_ID,

LeaderboardVariant.TIME_SPAN_ALL_TIME,

LeaderboardVariant.COLLECTION_PUBLIC)

.setResultCallback(new ResultCallback<Leaderboards.LoadPlayerScoreResult>() {

@Override

public void onResult(final Leaderboards.LoadPlayerScoreResult scoreResult) {

if (isScoreResultValid(scoreResult)) {

long currentPoints = scoreResult.getScore().getRawScore();

long pointsToAdd = 1337;

Games.Leaderboards.submitScore(googleApiClient, LEADERBOARD_ID, currentPoints +
pointsToAdd);

}

}

});
Tabele wyników
https://developers.google.com/games/services/common/concepts/leaderboards
Wyświetlanie tabeli
startActivityForResult(

Games.Leaderboards.getLeaderboardIntent(googleApiClient, LEADERBOARD_ID), 

REQUEST_LEADERBOARD);
Osiągnięcia
https://developers.google.com/games/services/common/concepts/achievements
Odblokowywanie osiągnięć:
Games.Achievements.unlock(
googleApiClient,
"my_achievement_id");
Games.Achievements.increment(

googleApiClient, 

"my_incremental_achievment_id", 

1);
Wyświetlanie osiągnięć:
startActivityForResult(

Games.Achievements

.getAchievementsIntent(googleApiClient),

REQUEST_ACHIEVEMENTS);
Multiplayer (turowy i w czasie rzeczywistym)
https://developers.google.com/games/services/common/concepts/realtimeMultiplayer
• System pokojów
• Szereg callbacków obsługujących zdarzenia w grze
• UI do zapraszania znajomych oraz oczekiwania na start gry
• Przesyłanie informacji pomiędzy uczestnikami 

(reliable i non-reliable)
• Brak konieczności posiadania backendu dla prostych 

gier
Przykłady
Game of Words Quiz Fight
Sorry for the long presentation… here is the
potato
http://tnijurl.com/mtcae/
Prezentacja dostępna pod

Google App Engine i Google Play Services w Twoich aplikacjach