You are on page 1of 271

Backendless API for Android

2015 Backendless Corp.

Backendless API for Android


2015 Backendless Corp.
All rights reserved. No parts of this work may be reproduced in any form or by any means - graphic, electronic, or
mechanical, including photocopying, recording, taping, or information storage and retrieval systems - without the
written permission of the publisher.
Products that are referred to in this document may be either trademarks and/or registered trademarks of the
respective owners. The publisher and the author make no claim to these trademarks.
While every precaution has been taken in the preparation of this document, the publisher and the author assume no
responsibility for errors or omissions, or for damages resulting from the use of information contained in this
document or from the use of programs and source code that may accompany it. In no event shall the publisher and
the author be liable for any loss of profit or any other commercial damage caused or alleged to have been caused
directly or indirectly by this document.
Generated on: December 2015

Special thanks to:


All the people who contributed to this document, to everyone who
has helped us out with the vision for the product, feature
suggestions and ideas for improvements. Special thank s to our
families for your support, encouragement and patience.

Contents

Table of Contents
User Service

1 Overview
................................................................................................................................... 6
2 Setup ................................................................................................................................... 6
3 Core Classes
................................................................................................................................... 9
4 Error ...................................................................................................................................
Handling
11
5 User Properties
................................................................................................................................... 11
6 Retrieve
...................................................................................................................................
User Entity Properties
13
7 User Registration
................................................................................................................................... 14
8 Login................................................................................................................................... 17
9 Facebook
...................................................................................................................................
Login
21
10 Update
...................................................................................................................................
User Properties
25
11 Get Current
...................................................................................................................................
User
28
12 Logout
................................................................................................................................... 28
13 Password
...................................................................................................................................
Recovery
29
14 Security
................................................................................................................................... 31
15 Role To
...................................................................................................................................
User Mapping
35

Data Service

37

1 Overview
................................................................................................................................... 37
2 Setup................................................................................................................................... 38
3 Sync ...................................................................................................................................
and Async Calls
41
4 Error ...................................................................................................................................
Handling
42
5 Native...................................................................................................................................
vs External Databases
42
6 Using...................................................................................................................................
External Databases
43
7 Data Object
................................................................................................................................... 47
8 Saving
...................................................................................................................................
Data Objects
48
9 Updating
...................................................................................................................................
Data Objects
52
10 Deleting
...................................................................................................................................
Data Objects
59
11 Retrieving
...................................................................................................................................
Schema Definition
65
12 Basic...................................................................................................................................
Search
67
13 Advanced
...................................................................................................................................
Search
73
14 Using...................................................................................................................................
Dates in Search
80
15 Relations
...................................................................................................................................
Overview
81
16 Relations
...................................................................................................................................
(Save/Update)
87
17 Relations
...................................................................................................................................
(Delete)
97
2015 Backendless Corp.

Backendless API for Android


18 Relations
...................................................................................................................................
(Retrieve)
104
19 Relations
...................................................................................................................................
with Geo Points
114
20 Security
................................................................................................................................... 122
21 Permissions
...................................................................................................................................
API
128

Messaging Service

131

1 Overview
................................................................................................................................... 131
2 Setup
................................................................................................................................... 131
3 Core...................................................................................................................................
Classes
135
4 Sync...................................................................................................................................
and Async Calls
138
5 Error...................................................................................................................................
Handling
139
6 Push...................................................................................................................................
Notification Setup (Android)
139
7 Push...................................................................................................................................
Notification Setup (iOS)
143
8 Device
...................................................................................................................................
Registration
153
9 Retrieve
...................................................................................................................................
Device Registration
154
10 Managing
...................................................................................................................................
Registrations
155
11 Cancel
...................................................................................................................................
Device Registration
156
12 Message
...................................................................................................................................
Publishing
157
13 Publish
...................................................................................................................................
Push Notifications
164
14 Cancel
...................................................................................................................................
Scheduled Message
165
15 Message
...................................................................................................................................
Subscription
166
16 Cancel
...................................................................................................................................
Subscription
172
17 Sending
...................................................................................................................................
Email
173

File Service

175

1 Overview
................................................................................................................................... 175
2 Setup
................................................................................................................................... 175
3 Sync...................................................................................................................................
and Async Calls
179
4 Error...................................................................................................................................
Handling
180
5 Handling
...................................................................................................................................
Files via Console
180
6 File ...................................................................................................................................
Upload
184
7 Save...................................................................................................................................
Files From Byte Arrays
189
8 File ...................................................................................................................................
Download
191
9 File ...................................................................................................................................
Deletion
192
10 Directory
...................................................................................................................................
Deletion
193
11 Git Integration
................................................................................................................................... 193
12 Web...................................................................................................................................
Hosting
195
13 Custom
...................................................................................................................................
Domain Name
196
14 Custom
...................................................................................................................................
Web Template Hosting
197
15 Files...................................................................................................................................
Security
200
2015 Backendless Corp.

Contents

Geo Service

5
201

1 Overview
................................................................................................................................... 201
2 Setup
................................................................................................................................... 203
3 Sync...................................................................................................................................
and Async Calls
206
4 Error...................................................................................................................................
Handling
207
5 Adding
...................................................................................................................................
a Geo Category
207
6 Deleting
...................................................................................................................................
a Geo Category
211
7 Retrieving
...................................................................................................................................
Geo Categories
213
8 Adding
...................................................................................................................................
a GeoPoint
215
9 Updating
...................................................................................................................................
a GeoPoint
217
10 Deleting
...................................................................................................................................
a GeoPoint
217
11 Importing
...................................................................................................................................
Geo Data
219
12 Search
...................................................................................................................................
in Category
221
13 Search
...................................................................................................................................
in Radius
224
14 Search
...................................................................................................................................
in Rectangular Area
232
15 Geo...................................................................................................................................
Point Clustering
239
16 Relations
...................................................................................................................................
with Data Objects
248
17 Geofence
...................................................................................................................................
Designer
252
18 Geofence
...................................................................................................................................
API
263

Index

269

2015 Backendless Corp.

Backendless API for Android

User Service

1.1

Overview
The Backendless User Service empowers applications with the functionality related to the user accounts
such as user registrations, logins, password recovery and logouts. The core concept which the User
Service relies on is the User entity. The structure of the entity is configurable, that is a developer can
decide which properties "describe" a user in the context of a given version of the application. Typically,
properties describing a user are the ones collected during the user registration process. The User
Service provides the API enabling the following functionality for the applications built on top of
Backendless:
User Registration - Applications use the Backendless' registration API to let the users register
and create accounts for subsequent logins. Application developers can restrict access to the
server-side resources for specific user accounts or based on roles.
User Login - the API lets the registered users login to establish their identity within the
application.
Password Recovery - Backendless supports a complete workflow allowing users to recover lost or
forgotten passwords.
User Logout - the API lets the logged in users terminate their session and disassociate their
identity from the application.
Updating User Registration - the API supports the operation of updating user information.

1.2

Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.

Download SDK
The SDK can be downloaded from the Backendless website.

Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );

Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
2015 Backendless Corp.

User Service

-dontwarn
-keep class weborb.** {*;}

Apps with Google Maps and Geolocation


Android applications using Google Maps, for instance, the Geo service sample app generated by
Backendless code generator, require additional configuration in order for Google maps to be displayed.
The process of configuring an app to support Google maps consists of the following tasks:
1.
Display the debug certificate fingerprint
1. Locate your debug keystore file. The file name is debug.keystore , and is created the first time
you build your project. By default, it is stored in the same directory as your Android Virtual Device
(AVD) files:
OS X and Linux: ~/.android/
Windows Vista and Windows 7: C:\Users\your_user_name\.android\
2. If you are using Eclipse with ADT, and you are not sure where your debug keystore is located, you
can select Windows > Prefs > Android >Build to check the full path, which you can then paste
into a file explorer to locate the directory containing the keystore.
3. List the SHA-1 fingerprint.
For Linux or OS X, open a terminal window and enter the following:
keytool -list -v -keystore ~/.android/debug.keystore -alias
androiddebugkey -storepass android -keypass android
For Windows Vista and Windows 7, run:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.
keystore" -alias androiddebugkey -storepass android -keypass
android
4. You should see output similar to this:
Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01
18:04:04 PST 2033
Certificate fingerprints:
MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:
A1:66:6E:44:5D:75
Signature algorithm name: SHA1withRSA
Version: 3
5. The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the
sequence of 20 two-digit hexadecimal numbers separated by colons.
2.
Create an API project in the Google APIs Console
1. In a browser, navigate to the Google APIs Console.

2015 Backendless Corp.

Backendless API for Android

If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.
4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.

Add the API key to your application


Follow the steps below to include the API key in your application's manifest, contained in the file
AndroidManifest.xml. From there, the Maps API reads the key value and passes it to the Google
Maps server, which then confirms that you have access to Google Maps data.
1. In AndroidManifest.xml, add the following element as a child of the <application> element, by
inserting it just before the closing tag </application> :
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
2. Substitute your API key for API_KEY in the value attribute. This element sets the key com.google.
android.maps.v2.API_KEY to the value of your API key, and makes the API key visible to any
MapFragment in your application.
3. Save AndroidManifest.xml and re-build your application.

Application ID and Secret Key


Values for the application-id and secret-key headers must be obtained through the Backendless
Console:
1. Login to your account and select the application.
2. Click the Manage icon from the vertical icon-menu on the left.
2015 Backendless Corp.

User Service

3. The "App Settings" section is selected by default. The interface contains the text fields for
"Application ID" and secret keys for each supported client-side environment.
4. Use the "Copy" button to copy the value into the system clipboard.

Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".

1.3

Core Classes
The Backendless User Service uses the following core classes:
Backendless.UserService - provides access to the user service API. All user-related operations,
such as user registration, user update, login, logout and password recovery are available through this
class.
BackendlessUser - represents a user registered with the application. The class is used in:
User Registration - an instance of the class contains a list of user properties
User Registration/Properties Update - an instance of the class contains a list of properties to be
updated
The Login method returns an instance of the class upon successful login.
BackendlessUser Class Definition
The BackendlessUser class is defined as:

2015 Backendless Corp.

10

Backendless API for Android

package com.backendless;
import java.util.HashMap;
import java.util.Map;
public class BackendlessUser
{
// Returns all properties of the user object
public Map<String, Object> getProperties()
// sets the properties of the user to the values from the
specified map
public void setProperties( Map<String, Object> properties )
// adds properties from the specified map to the existing
properties collection
public void putProperties( Map<String, Object> properties )
// retrieves the value of the property
public Object getProperty( String key )
// sets the value of the property
public void setProperty( String key, Object value )
// returns internal id of the user object assigned by
Backendless.
// The value is available only for registered user accounts.
public String getUserId()
// sets the password for the account
public void setPassword( String password )
// retrieves the password for the user account
public String getPassword()
}
AsyncCallBack<T> - an interface used in the asynchronous API calls. An implementation of the
interface must provide two methods. One accepting a result from the server (with the generic type T as
the argument) and the other for handling errors reported by the server.
AsyncCallback Class Definition
The AsyncCallback<T> interface is defined as:
package com.backendless.async;
import com.backendless.exceptions.BackendlessFault;
public interface AsyncCallback<T>
{
public void handleResponse( T response );
public void handleFault( BackendlessFault fault );
}

2015 Backendless Corp.

User Service

11

BackendlessFault and BackendlessException - Used in error handling, asynchronous and


synchronous respectfully.

1.4

Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. Currently all the error codes are numbers, however the method
returning the error code returns the String type. This is done for future expansion of the error code
system which may include characters. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional
information about the error
public String getMessage();
//
public String getDetail();
public String toString();
}
The asynchronous calls receive the fault through the following method in the callback object:
public void handleFault( BackendlessFault fault )
For the synchronous calls the fault object can be obtained through the BackendlessException
exception which the synchronous method may throw. The exception class provides access to a
BackendlessFault object through the following method:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}

1.5

User Properties
When a user registers with an application, he provides information which establishes the person's
identity. For one application these properties may include name, phone number and email address, for
another it could be user id, age and gender. Backendless User Service allows each application to have a
custom set of properties associated with the user entity. There are two ways to define what properties
the User entity should have in a Backendless application:

Defining properties with Console


User property management is available on the User Properties screen of the console. To get to the
2015 Backendless Corp.

12

Backendless API for Android

screen:
1. Login to the console
2. Select the desired application and version
3. Click the Users icon on the left side of the interface. The "User Properties" panel is selected by
default.
The interface consists of two lists: Available Properties and Selected Properties. The Selected
Properties list contains the properties assigned to the User entity - these are the effective properties for
the selected version of the application. The Available Properties list is simply a storage for the noneffective properties which can be moved to the Selected list if needed. A property can be moved between
the lists by clicking its name.
Identity Property
Among the Selected Properties, one must be marked as identity. This is the property Backendless
uses for the Login and Restore Password operations.. As users register, Backendless ensures the
provided value for the property selected as identity is unique in the context of a specific version for
an application.
Password Property
"password" is a special property. Backendless automatically adds the property when an application
is created. The following rules apply to the password property:
Password cannot be moved out of the Available Properties list.
Password cannot be marked as Identity.
Password is always a required property

To add a property, click the "Add Custom Property" button. New properties automatically added to the
Selected Properties list. To move a property to the other list, simply click its name.

Defining properties with API


User properties can be defined with the user registration API call. For any property specified in the
2015 Backendless Corp.

User Service

13

object with the user registration information Backendless creates a user property. This behavior can be
turned off/on using the Dynamic User Definition configuration setting in the console (select the Users >>
User Properties). The setting is turned on by default:

1.6

Retrieve User Entity Properties


Application developers can get a list of the properties associated with the user entity using the following
API:
Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
public void Backendless.UserService.describeUserClass
( AsyncCallback<List<UserProperty>> callback );
where:
- an object which receives either a return value or an error from the server. The return
value from the server is a collection of the UserProperty objects. The class of the
callback object must implement the AsyncCallback<List<UserProperty>>
interface .
The UserProperty class is defined as:
callback

public class UserProperty


{
// The method returns true if the property is marked as
'identity'
public boolean isIdentity();
// Returns the name of the property
public String getName();
// Returns true if the property is required during user
registration
public boolean isRequired();
// Returns the data type of the property
public DateTypeEnum getType();
}
Synchronous Call Method Signature:
public List<UserProperty> Backendless.UserService.describeUserClass
();
Asynchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
Backendless.UserService.describeUserClass( new
AsyncCallback<List<UserProperty>>()
{
public void handleResponse( List<UserProperty> properties )

2015 Backendless Corp.

14

Backendless API for Android

{
for( UserProperty userProp : properties )
{
System.out.println( "Property name - " + userProp.getName();
System.out.println( "\trequired - " + userProp.isRequired();
System.out.println( "\tidentity - " + userProp.isIdentity();
System.out.println( "\tdata type - " + userProp.getType();
}
}
public void handleFault( BackendlessFault fault )
{
}
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
List<UserProperty> properties = Backendless.UserService.
describeUserClass();
for( UserProperty userProp : properties )
{
System.out.println( "Property name - " + userProp.getName();
System.out.println( "\trequired - " + userProp.isRequired();
System.out.println( "\tidentity - " + userProp.isIdentity();
System.out.println( "\tdata type - " + userProp.getType();
}

1.7

User Registration
The user registration API can be used to create user accounts in the application. The registration
request must provide a user object as a collection of key/value properties. The collection must contain
all the required properties which must include a property marked as identity as well as the "password"
property. Unless the properties are modified in the console, the default property marked as identity is
"email" . Additionally, the "email" property is required if the application is configured to confirm email
addresses for the registered users.
Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
Backendless.UserService.register( user, new
AsyncCallback<BackendlessUser>() );
where:
user
callback

- an instance of the BackendlessUser class which contains property values for the
account registration.
- an object which receives either a return value or an error from the server. The return
value from the server is an instance of the BackendlessUser class with the ID assigned
by the server-side. The class of the callback object must implement the
AsyncCallback<BackendlessUser> interface .

2015 Backendless Corp.

User Service

15

Synchronous Call Method Signature:


public BackendlessUser Backendless.UserService.register
( BackendlessUser userObj ) throws BackendlessException;
Error Codes:
The following errors may occur during User Registration API calls. See the Error Handling section for
details on how to retrieve the error code when the server returns an error.
Error
Code

Description

2002

Version is disabled or provided wrong application info (application id or


secret key)

3009

User registration is disabled for the version of the application

3010

User registration has an unknown property and dynamic properties are


disabled for this version of the application

3011

Missing "password" property

3012

Required property is missing

3013

Missing value for the identity property

3014

External registration failed with an error.

3021

General user registration error. Details included with the error message.

3033

User with the same identity already exists

3038

Missing application-id, version name or collection of properties for the


registering user

3039

Property "id" cannot be used in the registration call

3040

Email address is in the wrong format

3041

A value for a required property is missing

3043

Duplicate properties in the registration request

8000

Property value exceeds the length limit

Asynchronous Call Example:


Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
BackendlessUser user = new BackendlessUser();
user.setProperty( "email", "james.bond@mi6.co.uk" );
user.setPassword( "iAmWatchingU" );
Backendless.UserService.register( user, new
AsyncCallback<BackendlessUser>()
{
public void handleResponse( BackendlessUser registeredUser )
{
// user has been registered and now can login
}

2015 Backendless Corp.

16

Backendless API for Android

public void handleFault( BackendlessFault fault )


{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
} );
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
BackendlessUser user = new BackendlessUser();
user.setProperty( "email", "james.bond@mi6.co.uk" );
user.setPassword( "iAmWatchingU" );
try
{
user = Backendless.UserService.register( user );
}
catch( BackendlessException exception )
{
BackendlessFault fault = exception.getFault();
// an error has occurred, the error code can be retrieved with
fault.getCode()
}

Turning Registration Off


User registration can be disabled for a particular version of the application using the Backendless
Console:
1. Login to the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Registration".
The "Registration" toggle turns the registration API on or off. When the registration is turned off and a
user attempts to register, the system returns error 3009 .

Email Confirmations
Backendless can send out an email requesting new registered users to confirm their email address. This
feature can be configured in the Backendless Console:
1. Log into the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Registration".

2015 Backendless Corp.

User Service

17

When email confirmations are required (the feature is enabled by default), the "email" user property is
required and must contain a value formatted as an email address. To configure the text of the email
message, select "Communication & Email Templates" from the Users menu in the console and select
the "User registers" event.

External Registration
User registrations can be duplicated in an external system through the External Registration Callback.
Developer can specify a URL where Backendless sends a POST request to with the user registration
data as a JSON object. The external registration callback is synchronous and takes place in the same
transaction as the Backendless registration call. As a result, the external system must return result as
fast as possible. The format of the request and response for the external registration is the same as the
request/response body of the Backendless registration API.
To configure the callback:
1. Login to the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Registration".
4. Turn on the "Execute registration callback" toggle.
5. Enter the URL of the script into the "Callback URL" text field.

1.8

Login
Registered users can login to establish their identity with the application using the API below. The login
operation requires two properties: one marked as user identity and the second is password .
Backendless automatically assigns the "AuthenticatedUser" role to all successfully logged in users.
The role can be used to differentiate access to various resources (persistent objects, messaging
channels, media streams) between authenticated users and guests.
Asynchronous Methods:
The method call does not block - it returns immediately. The AsyncCallback argument receives
either the response or the fault returned by the Backendless servers.
public void Backendless.UserService.login( String login,
String password,
AsyncCallback<BackendlessUser>
callback );
public void Backendless.UserService.login( String login,
String password,
boolean stayLoggedIn,
AsyncCallback<BackendlessUser>
callback );

where:
2015 Backendless Corp.

18

Backendless API for Android

login
password
stayLoggedIn

- a value for the property marked as identity.


- user's password
- requests to store the user's login information so the login form can be
skipped next time the user launches the app. Use the following API to check
if the application has user login information from previous runs:
// UserTokenStorageFactory is available in the com.
backendless.persistence.local package
String userToken = UserTokenStorageFactory.instance().
getStorage().get();
if( userToken != null && !userToken.equals( "" ) )
{ // user login is available, skip the login activity/login
form }

callback

- an object which receives either a return value or an error from the server.
The class must implement the AsyncCallback<BackendlessUser>
interface .

Synchronous Method:
public BackendlessUser Backendless.UserService.login( String login, String
password, boolean stayLoggedIn ) throws BackendlessException ;

where:
login
password
stayLoggedIn

- a value for the property marked as identity.


- user's password
- requests to store the user's login information so the login form can be
skipped next time the user launches the app. Use the following API to check
if the application has user login information from previous runs:
// UserTokenStorageFactory is available in the com.
backendless.persistence.local package
String userToken = UserTokenStorageFactory.instance().
getStorage().get();
if( userToken != null && !userToken.equals( "" ) )
{ // user login is available, skip the login activity/login
form }

Error Codes:
The following errors may occur during the Login API call. See the Error Handling section for details
on how to retrieve the error code when the server returns an error.
Error Description
Code
2002

Version is disabled or provided wrong application info (application id


or secret key)

3000

Login has been disabled for the user account.

3001

Missing login settings, possibly invalid application id or version.

3002

User cannot login because Multiple Logins disabled and there is a


logged in user for the account.

3003

Invalid login or password.

2015 Backendless Corp.

User Service

3006

Either login or password is an empty string value.

3034

User logins are disabled for the version of the application.

3036

Account locked out due to too many failed logins.

3038

One of the required parameters (application id, version, login or


password) is null

3044

Multiple login limit for the same user account has been reached.

8000

Property value exceeds the length limit

19

Asynchronous Call Example:


Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
Backendless.UserService.login( username, password, new
AsyncCallback<BackendlessUser>()
{
public void handleResponse( BackendlessUser user )
{
// user has been logged in
}
public void handleFault( BackendlessFault fault )
{
// login failed, to get the error code call fault.getCode()
}
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
BackendlessUser user;
try
{
user = Backendless.UserService.login( username, password );
}
catch( BackendlessException exception )
{
// login failed, to get the error code, call exception.getFault
().getCode()
}

External Authentication
Similar to external registration, Backendless supports external authentication. When configured,
Backendless delegates the authentication process to an external system by sending the provided
user credentials to a URL. The URL of the external authentication system can be configured in
Backendless Console:
1. Log into the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Login".
4. The "External authentication" section contains the configuration settings.

2015 Backendless Corp.

20

Backendless API for Android

When the external authentication is enabled and user attempts to login, Backendless sends the
following request to the specified URL:
POST http://external-authentication-url
Authorization: Basic base64-encoded-login:password
Where base64-encoded-login:password is constructed as follows:
1. login and password are combined into a string "login:password"
2. The resulting string literal is then encoded using Base64

Multiple Logins
The Multiple Logins feature enables login using the same account from different computers or
devices. Multiple logins can be configured in the Backendless Console:
1. Log into the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Login".
4. The "Multiple Logins" section contains the configuration settings.
When the feature is turned on (multiple logins allowed), the configuration setting may include the
maximum number of simultaneous logins for the selected version of the application. When the
feature is disabled (multiple logins are not allowed), the configuration must indicate whether which
login should be invalidated (first or second for the account):

Session Timeout
Backendless supports session expiration which can be configured in console. Along with the
session timeout interval, the configuration can also include a forwarding URL which is used to
redirect requests to for the expired sessions.

2015 Backendless Corp.

User Service

21

Account Lockout
An application powered by Backendless can be configured to lock out accounts with failed logins.
The console has two configuration options: number of failed logins before the account is locked and
a time interval to wait before the account is available for logins again.

1.9

Facebook Login
Backendless integrates with Facebook to support user authentication and login into a Backendless
application with a Facebook account. Using the integration application developer can provide a way to
skip the application registration step and allow users to use their Facebook identity to enter and
experience the application. Backendless provides two ways to handle Facebook logins:
Easy Facebook Login - The Facebook SDK is not required on the client. The client application
makes a Backendless API call to initiate the login and the user authenticates in a Facebook
popup window.
Login with Facebook SDK - The client application uses the Facebook SDK to authenticate the
user and then delegates to a Backendless API call to link the Facebook identity to a
BackendlessUser instance.
Both approaches - Easy Login and Login with SDK, rely on the concepts and a configuration change
described below:

Property Mapping
Since a Backendless application has its own set of user properties, they need to be mapped to the
corresponding Facebook Graph API user fields. A mapping references a Facebook user field and the
name of a Backendless property. The mapping is a required argument in the Backendless API call to
login. Once the user is authenticated, Backendless obtains the field values from the Facebook user
graph object and populates the mapped properties in the BackendlessUser object. For the very first
login, Backendless also registers the user. In this case the returned BackendlessUser object will have
2015 Backendless Corp.

22

Backendless API for Android

the "user-registered " property set to true .

Permissions
The client application can request the permissions from the user to access additional user information or
perform various actions. Requested permissions is a collection of string objects passed as an argument
in the Backendless API call to login a Facebook user.

Backendless Configuration
Backendless backend must be configured with Facebook's application App ID and secret key. See the "
Social Settings" chapter of the user guide (the Manage section) for detailed instructions for configuring
Backendless with Facebook.

Easy Facebook Login


Backendless client SDK and the server-side implementation follows the following process to support the
easy login option:
1. The client application initiates the Facebook login sequence by calling a method from the
Backendless SDK.
2. The invocation results in a UI dialog where the user enters their Facebook credentials and
submits the form.
3. The form submission is sent to Facebook.
4. Upon successful login, Facebook executes a redirect to the Backendless servers.
5. Backendless receives the user information from Facebook and returns it back to the client.
The diagram below illustrates the process:

API
Description:
Perform Facebook user login with the default permission. Backendless will obtain the Facebook
fields which are available without access token - see the complete list of available fields.
2015 Backendless Corp.

User Service

23

Backendless SDK constructs a UI dialog to show the Facebook login screen.


public void Backendless.UserService.loginWithFacebook(
android.app.Activity
context,
Map<String, String>
facebookFieldsMappings,
AsyncCallback<BackendlessU
ser> responder )
where:
context
- must be the current activity context.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user

responder

properties. Keys must be the names of the Facebook fields,


values - the names of the Backendless properties. See the
Property Mapping section for additional details.
- an object which receives either a return value or an error from
the server. The class must implement the
AsyncCallback<BackendlessUser> interface .

Description:
Perform Facebook user login and request the specified permissions. Backendless SDK
constructs a UI dialog to show the Facebook login screen.
public void Backendless.UserService.loginWithFacebook(
android.app.Activity
context,
Map<String, String>
facebookFieldsMappings,
List<String> permissions,
AsyncCallback<BackendlessU
ser> responder )
where:
context
- must be the current activity context.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user

permissions
responder

properties. Keys must be the names of the Facebook fields,


values - the names of the Backendless properties. See the
Property Mapping section for additional details.
- a collection of the Facebook permissions/scopes which the
application requests access to.
- an object which receives either a return value or an error from
the server. The class must implement the
AsyncCallback<BackendlessUser> interface .

Description:
Perform Facebook user login and request the specified permissions. Backendless SDK uses
the provided WebView to host the Facebook login screen.
public void Backendless.UserService.loginWithFacebook(
android.app.Activity
context,
android.webkit.WebView
webView,
Map<String, String>
facebookFieldsMappings,

2015 Backendless Corp.

24

Backendless API for Android

List<String> permissions,
AsyncCallback<BackendlessU
ser> responder )
where:
- must be the current activity context.
- a view component where the Facebook login dialog will be
rendered.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user
properties. Keys must be the names of the Facebook fields,
values - the names of the Backendless properties. See the
Property Mapping section for additional details.
permissions
- a collection of the Facebook permissions/scopes which the
application requests access to.
responder
- an object which receives either a return value or an error from
the server. The class must implement the
AsyncCallback<BackendlessUser> interface .
context
webView

Login With Facebook SDK


As the name suggests this option uses the Facebook SDK to handle the login to a Backendless
application. The end result is an instance of the BackendlessUser class which contains the property
values initialized from the Facebook user account. Using the Facebook SDK with Backendless consists
of four steps:
1. Configure the environment with the Facebook SDK. Complete instructions are available at:
https://developers.facebook.com/docs/getting-started/facebook-sdk-for-android/3.0/
2. Configure Backendless application with the Facebook App ID/API Key.
3. Add onActivityResult() method to the activity class (Android Activity).
4. Use an API call described below.
Adding onActivityResult() method
Before using API for logging in, you need to add onActivityResult() method to the activity
class (Android Activity) of your application as follows:
@Override
public void onActivityResult( int requestCode, int resultCode, Intent
data )
{
super.onActivityResult( requestCode, resultCode, data );
Session.getActiveSession().onActivityResult( this, requestCode,
resultCode, data );
}

If onActivityResult() method is already defined, add the following line to the activity class
(Android Activity) of your application:
Session.getActiveSession().onActivityResult( this, requestCode, resultCode,
data );

Description
Logs in a Facebook user into a Backendless application. Uses the SDK to display the login
dialog with the specified permissions.
public void Backendless.UserService.loginWithFacebookSdk(
android.app.Activity

2015 Backendless Corp.

User Service

25

context,
Map<String, String>
facebookFieldsMappings,
List<String> permissions,
AsyncCallback<BackendlessU
ser> responder )
where:
context
- must be the current activity context.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user

permissions
responder

properties. Keys must be the names of the Facebook fields,


values - the names of the Backendless properties. See the
Property Mapping section for additional details.
- a collection of the Facebook permissions/scopes which the
application requests access to.
- an object which receives either a return value or an error from
the server. The class must implement the
AsyncCallback<BackendlessUser> interface .

Description:
Associates a Facebook user with BackendlessUser. Can be used when the Facebook login
must be handled independently.
public void Backendless.UserService.loginWithFacebookSession(
com.facebook.Session
facebookSession,
com.facebook.model.
GraphUser facebookUser,
Map<String, String>
facebookFieldsMappings,
AsyncCallback<BackendlessU
ser> responder )
where:
facebookSession

- established and current Facebook session. Provided Facebook


session should use NonCachingTokenCachingStrategy . You
may use com.backendless.helpers.
NonCachingTokenFacebookSession.openActiveSession
( Activity activity, List<String> permissions,
Session.StatusCallback callback ) , which wrappes the

creation of the non-caching session.


facebookUser
- currently logged in Facebook user.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user

responder

1.10

properties. Keys must be the names of the Facebook fields,


values - the names of the Backendless properties. See the
Property Mapping section for additional details.
- an object which receives either a return value or an error from
the server. The class must implement the
AsyncCallback<BackendlessUser> interface .

Update User Properties


Backendless supports the operation of user properties update for the logged in users. This operation is
useful if an application needs to provide to the users the functionality for updating user profiles and
registration properties. User must be logged in in order to update his registration properties.

2015 Backendless Corp.

26

Backendless API for Android

Asynchronous Call Method Signature:


The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
public void Backendless.UserService.update( BackendlessUser user,
AsyncCallback<Backendle
ssUser> );
where:
user
callback

- an instance of the BackendlessUser class which contains property values to update


the user account with.
- an object which receives either a return value or an error from the server. The return
value from the server is an updated instance of the BackendlessUser class. The class of
the callback object must implement the AsyncCallback<BackendlessUser>
interface .

Synchronous Call Method Signature:


public BackendlessUser Backendless.UserService.update
( BackendlessUser user );
where:
- an instance of the BackendlessUser class which contains property values to update
the user account with.
user

Error Codes:
The following errors may occur during the Update User Properties API call. See the Error Handling
section for details on how to retrieve the error code when the server returns an error.
Error
Code

Description

2002

Version is disabled or provided wrong application info (application id or


secret key)

3018

The property marked as "identity" is being updated and another user


already has the specified value which must be unique.

3024

General "update registration" error. Error message should contain


additional details.

3028

User is not logged in.

3029

Cannot modify properties of another user. Returned when one user is


logged and the call attempts to modify properties of another user.

3030

Unable to locate user account - invalid user id.

3031

A new "dynamic" property is being added, but dynamic property


definition is disabled.

3045

Required properties in the provided object do not contain values.

Asynchronous Call Example:


Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
Backendless.UserService.login( username, password, new
AsyncCallback<BackendlessUser>()
{

2015 Backendless Corp.

User Service

27

public void handleResponse( BackendlessUser user )


{
// user has been logged in, now user properties can be updated
user.setProperty( "phoneNumber", "5551212" );
Backendless.UserService.update( user, new
AsyncCallback<BackendlessUser>()
{
public void handleResponse( BackendlessUser user )
{
// user has been updated
}
public void handleFault( BackendlessFault fault )
{
// user update failed, to get the error code call fault.
getCode()
}
});
}
public void handleFault( BackendlessFault fault )
{
// login failed, to get the error code call fault.getCode()
}
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
BackendlessUser user;
try
{
user = Backendless.UserService.login( username, password );
}
catch( BackendlessException exception )
{
// login failed, to get the error code, call exception.getFault
().getCode()
}
try
{
user.setProperty( "phoneNumber", "5551212" );
user = Backendless.UserService.update( user );
}
catch( BackendlessException exception )
{
// update failed, to get the error code, call exception.getFault
().getCode()
}

2015 Backendless Corp.

28
1.11

Backendless API for Android

Get Current User


An Android application can retrieve an instance of BackendlessUser representing the currently logged in
user using the following API call:
public BackendlessUser Backendless.UserService.CurrentUser();
If a user is not logged in, the method returns null .

1.12

Logout
The Logout operation terminates user session and disassociates the AuthenticatedUser role from the
subsequent requests made by the client application.
Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
public void Backendless.UserService.logout( AsyncCallback<Void>
callback )
where:
- an object which is notified when the server completes the logout operation or returns
an error. The class must implement the AsyncCallback<Void> interface .
callback

Synchronous Call Method Signature:


public void Backendless.UserService.logout() throws
BackendlessException;
Error Codes:
The following errors may occur during the Logout API call. See the Error Handling section for details on
how to retrieve the error code when the server returns an error.
Error
Code

Description

2002

Version is disabled or provided wrong application info (application id or


secret key)

3007

Invalid application-id or version.

3023

General error while executing logout. Error details should be available in


the message property.

Asynchronous Call Example:


Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
Backendless.UserService.login( username, password, new
AsyncCallback<BackendlessUser>()
{
public void handleResponse( BackendlessUser user )
{
// user has been logged in
// now, let's logout
Backendless.UserService.logout( new AsyncCallback<Void>()

2015 Backendless Corp.

User Service

29

{
public void handleResponse( Void response )
{
// user has been logged out.
}
public void handleFault( BackendlessFault fault )
{
// something went wrong and logout failed, to get the error
code call fault.getCode()
}
});
}
public void handleFault( BackendlessFault fault )
{
// login failed, to get the error code call fault.getCode()
}
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
BackendlessUser user;
try
{
user = Backendless.UserService.login( username, password );
}
catch( BackendlessException exception )
{
// login failed, to get the error code, call exception.getFault
().getCode()
}
try
{
// now log out:
Backendless.UserService.logout();
}
catch( BackendlessException exception )
{
// logout failed, to get the error code, call exception.getFault
().getCode()
}

1.13

Password Recovery
Password recovery sends an email to the user's email address with a link where the user can change
the password.
Asynchronous Call Method Signature:

2015 Backendless Corp.

30

Backendless API for Android

The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
public void Backendless.UserService.restorePassword( String
identityValue, AsyncCallback<Void> callback )
where:
identityValue
callback

- a value for the property marked as identity which uniquely identifies the user
within the application.
- an object which is notified when the server completes the logout operation or
returns an error. The class must implement the AsyncCallback<Void>
interface .

Synchronous Call Method Signature:


public void Backendless.UserService.restorePassword( String
identityValue ) throws BackendlessException;
where:
- a value for the property marked as identity which uniquely identifies the user
within the application.
identityValue

Error Codes:
The following errors may occur during the Password Recovery API call. See the Error Handling section
for details on how to retrieve the error code when the server returns an error.
Error
Code

Description

2002

Version is disabled or provided wrong application info (application id or


secret key)

3020

Unable to find user with the specified login (invalid user identity).

3025

General password recovery error. Additional details should be available


in the "message" property of the response.

3038

One of the requirement arguments (application id, version or user


identity) is missing.

Asynchronous Call Example:


Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
Backendless.UserService.restorePassword( "james.bond", new
AsyncCallback<Void>()
{
public void handleResponse( Void response )
{
// Backendless has completed the operation - an email has been
sent to the user
}
public void handleFault( BackendlessFault fault )
{
// password revovery failed, to get the error code call fault.
getCode()
}

2015 Backendless Corp.

User Service

31

});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
try
{
Backendless.UserService.restorePassword( "james.bond" );
}
catch( BackendlessException exception )
{
// password recovery failed, to get the error code, call
exception.getFault().getCode()
}

1.14

Security
All Backendless API operations can be restricted either for specific user accounts or for roles. A user
account may be associated with one or more roles. Backendless supports several built-in system roles
as well as developer-defined roles. The system roles include:
NotAuthenticatedUser - any user who has not authenticated to a Backendless application.
AuthenticatedUser - any user who has successfully logged in.
SocialUser - any user who has logged in through a social network.
FacebookUser - any user who has logged in with a Facebook account.
TwitterUser - any user who has logged in with a Twitter account.
Developer-defined roles can be added using the Backendless Console. Roles are defined at the
application version level, that is a particular version of an application may have its own set of developerdefined roles.
Backendless manages permissions as tuples consisting of:
Operation - Users interact with Backendless either via console or the API. Any request to
Backendless is an operation which may have a permission associated with it.
Resource - Some operations target specific resources. These can be data tables, messaging
channels or media tubes.
Principal - Either a specific user identity or a role associated with the user initiating the operation.
There are two levels of permissions for the API operations - global and resource-specific. The resourcespecific permissions guard access to the specific resources (data tables, messaging channels, etc).
They have higher priority and are checked first. When Backendless receives an API call from a client, it
determines the user associated with the request and obtains a list of roles associated with the user
account (for the users who have not authenticated, the NotAuthenticatedUser role is used). The
resource-specific permissions can be set to either inherit the permission from the global matrix or
explicitly grant or deny access to the resource for the given user or role. The diagram below illustrates
the process when the resource-specific permission is set to inherit:

2015 Backendless Corp.

32

Backendless API for Android

When the resource-specific permission is explicitly set to either grant or deny access, the global
permissions are bypassed:

Global Permissions
The global service permissions apply to all resources managed by a particular service. For example,
global Data Service permissions for a particular role apply to all data tables. These permissions can be
viewed and modified by clicking a role on the Users > Security and Restrictions screen in Backendless
Console:

2015 Backendless Corp.

User Service

The table below shows a global permissions matrix for a role:

2015 Backendless Corp.

33

34

Backendless API for Android

Each global permission has two states:


Grant - represented by a green check mark - grants the rights to execute an operation
Deny - represented by a red X - denies the rights to execute an operation.
To change a permission click the icon of the current state. The change will be effective immediately.
To register a new role use the "Add Role" button on the "Security and Restrictions" screen. Once a role
is added, you can configure it's global permission matrix as well as resource-specific permissions.

Resource-Specific Permissions
To view, assign or modify resource-specific permissions, use a corresponding screen in the
Backendless Console. For example, to restrict access to a data table, switch to the Data view, select a
table and click the "Schema and Permissions" button. The user interface has two views - one is for
managing permissions for user accounts and the other for roles. To modify permissions for a user
account:
1. Click the "User Permissions" list item.
2. Enter the user name in the search field.
3. Select the user and click the "Add" button.
4. The table displays the permissions for various operations for the selected user and the resource.
5. Click an icon representing the permission state to modify the permission.

2015 Backendless Corp.

User Service

35

Similarly permissions can be assigned or modified for specific roles - use the "Role Permissions" list
item.
To modify the permissions for Messaging channels, click the "Messaging" icon, select a channel and
switch to the "Permissions" view.

1.15

Role To User Mapping


User accounts can be mapped to roles using the API documented below. Once a role is assigned to a
user account, any permissions assigned to the role (both grant and deny) automatically apply to all API
operations executed by the application on behalf of the user. Roles can be assigned to users in Custom
Business Logic. This can be accomplished using the custom events.

Retrieving Available User Roles


Before you assign a user to a role, you might need to figure out what roles are available in the system.
Please note that the system will return the list of the roles associated with your user account in
Backendless. If you send the request without logging in, the system will return only one role NotAuthenticatedUser .
To get the list of the available user roles:
Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives
either the response or the fault returned by the Backendless servers.
Backendless.UserService.getUserRoles( new AsyncCallback<List<String>>());

Synchronous Call Method Signature:


BackendlessUser Backendless.UserService.getUserRoles() throws
BackendlessException;

Asynchronous Call Example:


Before retrieving the user roles, log in as described in the Login section. Then, retrieve the user
roles as follows:
//login user
Backendless.UserService.login( "username@foo.com", "secretpassword", new
AsyncCallback<BackendlessUser>()
{
@Override
public void handleResponse( BackendlessUser backendlessUser )
{
// user has been logged in. Get user roles.
Backendless.UserService.getUserRoles( new AsyncCallback<List<String>>()
{
@Override
public void handleResponse( List<String> userRoles )

2015 Backendless Corp.

36

Backendless API for Android

{
//here we get all user roles - "userRoles".
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
// get user roles, to get the error code call backendlessFault.
getCode()
}
} );
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
// login failed, to get the error code call backendlessFault.getCode()
}
} );

Assigning a Role to User:


Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives
either the response or the fault returned by the Backendless servers.
public void Backendless.UserService.assignRole( String identity,
String roleName,
AsyncCallback<Void> callback
);

where:
identity
roleName
callback

- user identification. A value for the user property marked as


identity.
- the name of the role to assign to the user account.
- an object which receives either a return value or an error from
the server. The class must implement the AsyncCallback<Void>
interface.

Synchronous Call Method Signature:


public BackendlessUser Backendless.UserService.assignRole( String
identity, String roleName ) throws BackendlessException;

where:
identity
roleName

- user identification. A value for the property marked as identity.


- name of the role to assign to the user account.

Unassigning a Role from User:


Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives
either the response or the fault returned by the Backendless servers.
public void Backendless.UserService.unassignRole( String identity,

String roleName,
AsyncCallback<Voi

2015 Backendless Corp.

User Service

37

d> callback );
where:
identity
roleName
callback

- user identification. A value for the user property marked as


identity.
- name of the role to remove from the user account.
- an object which receives either a return value or an error from
the server. The class must implement the AsyncCallback<Void>
interface.

Synchronous Call Method Signature:


public BackendlessUser Backendless.UserService.unassignRole( String
identity, String roleName ) throws BackendlessException;

where:
identity
roleName

- user identification. A value for the user property marked as


identity.
- name of the role to remove from the user account.

Error Codes:
The following errors may occur during the Login API call. See the Error Handling section for
details on how to retrieve the error code when the server returns an error.
Error
Code

Description

2002

Version is disabled or provided wrong application info (application id or


secret key)

2005

Could not find role.

3038

One of the required parameters (user identity or roleName) is null.

3057

Could not find user by id or identity.

3058

Could not assign role to user.

3059

Could not unassign role to user.

Data Service

2.1

Overview
The Backendless Data Service is a highly scalable, object storage system. It is available via intuitive API
which supports all basic data persistence operations - Create, Retrieve, Update and Delete (CRUD). The
Data Service operates with persistent data at the object level, that means applications use the APIs to
save, update, delete or search for objects, rather than traditional database records. Developers using the
Data Service API do not need to know or understand the databases, schema creation rules, stored
procedures or SQL syntax.
The Data Service follows the following rules when working with user objects:
1. Objects persisted by the Data Service must specify the "type" or be an instance of a class (for
client applications written in strongly-typed languages).
2. Backendless automatically creates tables for each persisted type it has not seen before and
saves objects in the corresponding tables. Each table has columns corresponding to the
properties of the persisted objects.
3. Backendless creates three additional system-level columns for each new table:

2015 Backendless Corp.

38

Backendless API for Android

objectId - contains a unique object ID assigned by Backendless to each object.


created - contains a timestamp when the object was first saved by Data Service
updated - contains a timestamp when the object was most recently updated. The value is

null for newly created objects.


4. When an object of a known type/class introduces any new properties throughout the lifetime of
the application, the persistence structure will be modified accordingly. When such an object is
persisted, Backendless analyzes the object's properties and automatically alters the structure of
the corresponding table if any new properties are added.
5. When an object is saved or updated, it may reference another related object (a one-to-one
relationship) or objects (one-to-many). Backendless creates the underlying tables for the main
and the referenced types and persists the hierarchy accordingly.

2.2

Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.

Download SDK
The SDK can be downloaded from the Backendless website.

Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );

Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}

Apps with Google Maps and Geolocation


Android applications using Google Maps, for instance, the Geo service sample app generated by
Backendless code generator, require additional configuration in order for Google maps to be displayed.
The process of configuring an app to support Google maps consists of the following tasks:
1.
Display the debug certificate fingerprint
1. Locate your debug keystore file. The file name is debug.keystore , and is created the first time
you build your project. By default, it is stored in the same directory as your Android Virtual Device
2015 Backendless Corp.

Data Service

39

(AVD) files:
OS X and Linux: ~/.android/
Windows Vista and Windows 7: C:\Users\your_user_name\.android\
2. If you are using Eclipse with ADT, and you are not sure where your debug keystore is located, you
can select Windows > Prefs > Android >Build to check the full path, which you can then paste
into a file explorer to locate the directory containing the keystore.
3. List the SHA-1 fingerprint.
For Linux or OS X, open a terminal window and enter the following:
keytool -list -v -keystore ~/.android/debug.keystore -alias
androiddebugkey -storepass android -keypass android
For Windows Vista and Windows 7, run:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.
keystore" -alias androiddebugkey -storepass android -keypass
android
4. You should see output similar to this:
Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01
18:04:04 PST 2033
Certificate fingerprints:
MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:
A1:66:6E:44:5D:75
Signature algorithm name: SHA1withRSA
Version: 3
5. The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the
sequence of 20 two-digit hexadecimal numbers separated by colons.
2.
Create an API project in the Google APIs Console
1. In a browser, navigate to the Google APIs Console.
If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.

2015 Backendless Corp.

40

Backendless API for Android

4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.

Add the API key to your application


Follow the steps below to include the API key in your application's manifest, contained in the file
AndroidManifest.xml. From there, the Maps API reads the key value and passes it to the Google
Maps server, which then confirms that you have access to Google Maps data.
1. In AndroidManifest.xml, add the following element as a child of the <application> element, by
inserting it just before the closing tag </application> :
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
2. Substitute your API key for API_KEY in the value attribute. This element sets the key com.google.
android.maps.v2.API_KEY to the value of your API key, and makes the API key visible to any
MapFragment in your application.
3. Save AndroidManifest.xml and re-build your application.

Application ID and Secret Key


Values for the application-id and secret-key headers must be obtained through the Backendless
Console:
1. Login to your account and select the application.
2. Click the Manage icon from the vertical icon-menu on the left.
3. The "App Settings" section is selected by default. The interface contains the text fields for
"Application ID" and secret keys for each supported client-side environment.
4. Use the "Copy" button to copy the value into the system clipboard.

2015 Backendless Corp.

Data Service

41

Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".

2.3

Sync and Async Calls


Most of Backendless API for Android and plain Java is available in the synchronous and asynchronous
formats. The difference in the method signatures is the asynchronous methods accept the
AsyncCallback< T> argument:
package com.backendless.async.callback;
import com.backendless.exceptions.BackendlessFault;
public interface AsyncCallback<T>
{
void handleResponse( T response );
void handleFault( BackendlessFault fault );
}

The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;

2015 Backendless Corp.

42

Backendless API for Android

import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}

When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.

2.4

Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}

The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )

The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}

2.5

Native vs External Databases


A Backendless backend supports two alternative mechanisms of data persistence. There is a built-in
storage, referred to as native and an external storage. The native mechanism is enabled by default and a

2015 Backendless Corp.

Data Service

43

developer working with a Backendless backend does not need to do anything special to configure it. All
the data persisted through the Data Service API and Backendless console is stored internally, in the
database maintained by the Backendless service.
The external storage can be any database located elsewhere which the Backendless service connects
to with the connection parameters provided by the developer. Currently MySQL/MariaDB databases is
the only type of the supported external storage.
An application must be configured to use either the native or an external storage mechanism, they
cannot be combined into the hybrid mode. However, regardless of which mechanism is enabled, the
Data Service API can be used to work with the data. Additionally, if the application is configured to use
an external database, Backendless console can be used the manage the external data the same way it
does so with native. The only exception is the management of an external schema - Backendless
Console displays it in the view only mode.
To learn more about external data storage, see the Using External Databases chapter.

2.6

Using External Databases


Backendless data storage is an ultra-scalable system for storing objects hierarchies ranging from simple
to very deep and possibly recursive in nature. The default implementation uses an internal persistent
system which functions as a black box from the client-side developer's perspective. The internal
implementation is referred to as native. There are use-cases when the native storage system cannot be
used in an application built with Backendless. For instance, a new mobile application must built on top
of an existing MySQL database which is used by an existing desktop application. To support this
scenario, Backendless provides integration with External Databases. The integration enables a client
application to use the Data Service API as it would with the native storage, however the backend
operates on the data stored externally.

Adding an External Database in Backendless


Console
To link the external database to your Backendless backend:
1. Log in to the Backendless Console, select your app and click the Data icon.
2. Click the External radio-button as shown in the image below:

2015 Backendless Corp.

44

Backendless API for Android

3. In the Database Connection pop-up window, specify the connection details. To test the
connection, click the Test link. To proceed with the connection, click the Next button.

4. Once a connection is established (it may take several minutes), console displays a list of the
databases in the Database drop-down menu. Select a database and click the Ok button to
confirm your choice.

2015 Backendless Corp.

Data Service

45

5. Backendless will inspect the selected database and an email is delivered to the application
developer email address with the status of the inspection.
6. Upon successful database inspection the Data Management screen displays a list of the
tables from the external database.

The user interfaces is updated to reflect the new available functions:


Re-inspect the external database by clicking the Refresh icon:

Connect to another external database by clicking the add button:

2015 Backendless Corp.

46

Backendless API for Android

Re-connect (or edit the connection details) to current external database by clicking the
connect button:

Handling Data Objects in an External Database


All of the Data Service APIs can be used with external databases. The only caveat is with the data
tables which have composite primary keys (a PK consisting of more than one column). In this case, the
Data Service API provides special methods where object identity is established by sending in entire
object rather than a specific identity value.
saving an object
updating an object
deleting an object
basic search
advanced search
saving and updating relations
2015 Backendless Corp.

Data Service

47

retrieving relations

Supported Data Types


Currently Backendless supports only MySQL and MariaDB as external databases. The table below
provides a mapping between the MySQL data types and the corresponding types in Backendless:
MySQL Data Type

Backendless Data Type

varchar( x )

String value with maxLength = x

int

Integer

long

Long integer

double, float, decimal


Double
tinyint( 1 )

Boolean

datetime

Datetime

text

Text

other types

String

Limitations
There are several limitations for working with external databases:

1. The number of tables allowed in a database is limited by your billing plan


(which is the same as for the native Backendless storage).
2. There must be a permission for the MySQL information_schema table used
to inspect the external database.
3. Some of the system-level columns (and thus data object properties) will not
be available with the external databases, specifically: updated, created, ownerId
4.
5.
6.
7.
8.

2.7

, objectId .
You can not use the native and external databases simultaneously; once you establish a
connection with an external database, a native one will not be available any longer and vice
versa.
Only one external database can be used in an application (cannot connect to two different
external databases).
The schema of an external database cannot be edited in Backendless Console.
You can not import data to or export data from an external database using Backendless
Console.
If the database schema changes, it is important to re-inspect the database to avoid errors.

Data Object
Backendless supports persistence of arbitrary Java objects. Object's class does not need to implement
any special Backendless interfaces or extend classes. There are however a few requirements imposed
on the classes of the persisted objects:
Class must contain the default, public no-argument constructor.
Class must contain either at least one public field or a pair of getter and setter methods
conforming to the Java Bean naming convention.
Optional requirement. Backendless automatically assigns a unique ID to every persisted

2015 Backendless Corp.

48

Backendless API for Android

object. If the application needs to have access to the assigned ID, the class must implement
the following methods:
private String objectId;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
Optional requirement. In addition to objectId, Backendless maintains two additional properties
for every persisted object - created and updated . The former contains the timestamp when
the object was initially created in the Backendless data store. The latter is updated every time
the object is updated. To get access to these properties, the class must implement the
following methods:
private Date created;
private Date updated;
public Date getCreated()
{
return created;
}
public void setCreated( Date created )
{
this.created = created;
}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}

2.8

Saving Data Objects


The API to save an object can be used for two separate scenarios: if an object has been previously
saved, it is updated in the data store, otherwise it is saved (created). The save operation checks if the
object has objectId assigned by the server. in that case, the object is updated, otherwise it is created
in the Backendless data store. In case when there is no table for the persisted object, Backendless
creates one and maps table's columns to the object's properties.
The objectId property is automatically assigned to all persisted objects when they are initially saved.
See the Data Object section for details on objectId .

2015 Backendless Corp.

Data Service

49

Synchronous Method:
public <E> E Backendless.Persistence.of( E ).save( E entity ) throws
BackendlessException

Asynchronous Method:
public <E> void Backendless.Persistence.of( E ).save( E entity,
AsyncCallback<E> responder )

where:
E
entity
responder

- Java class of the data object to save.


- Java object to persist, must be of type E.
- a responder object which will receive a callback when the
method successfully saves the object or if an error occurs.
Applies to the asynchronous method only.

Return Value:
The synchronous method returns the saved object. The asynchronous call receives the return
value through a callback executed on the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
public String getObjectId() {
return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge( int age ) {
this.age = age;
}
public String getPhone() {
return phone;

2015 Backendless Corp.

50

Backendless API for Android

}
public void setPhone( String phone ) {
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
}

The following code saves a new instance of the Contact class:


public void saveNewContact()
{
String applicationID = "YOUR-APPLICATION-ID";
String secretKey = "YOUR-JAVA/ANDROID-SECRET-KEY";
String version = "v1";
Backendless.initApp( applicationID, secretKey, version );
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
// save object synchronously
Contact savedContact = Backendless.Persistence.save( contact );
// save object asynchronously
Backendless.Persistence.save( contact, new AsyncCallback<Contact>() {
public void handleResponse( Contact response )
{
// new Contact instance has been saved
}
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with fault.
getCode()
}
});
}

Saving a Data Object in an External Database


When saving a new data object to an external database, you need to take into consideration that such
object does not have some properties characteristic for the native data objects. Thus, these properties
will be omitted in the queries when saving a data object to an external database.

The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly

2015 Backendless Corp.

Data Service

51

initialized. Assuming you have the following Customer class


public class Customer
{
private String lastName;
private Integer discountRatePercent;
private String firstName;
public String getLastName()
{
return lastName;
}
public void setLastName( String lastName )
{
this.lastName = lastName;
}
public Integer getDiscountRatePercent()
{
return discountRatePercent;
}
public void setDiscountRatePercent( Integer discountRatePercent )
{
this.discountRatePercent = discountRatePercent;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName( String firstName )
{
this.firstName = firstName;
}
}

You can identify a customer object by unique combination of name and surname and save this object to
an external database as follows:
private void saveAlreadyExistingObject()
{
// get a customer entity from DB, modify its properties and update it. Get
it again to make sure update is correct
// Notice that values of the primary keys cannot be changed.
Customer melinda = new Customer();
melinda.setFirstName( "Melinda" );
melinda.setLastName( "Grey" );
melinda = Backendless.Persistence.of( Customer.class ).findById( melinda );
System.out.println("Melinda's rate is "+melinda.getDiscountRatePercent()+"
percent");
melinda.setDiscountRatePercent( melinda.getDiscountRatePercent() + 2 );
Backendless.Persistence.of(Customer.class).save(melinda);
Customer updatedMelinda = new Customer();
updatedMelinda.setFirstName( "Melinda" );
updatedMelinda.setLastName( "Grey" );

2015 Backendless Corp.

52

Backendless API for Android

updatedMelinda = Backendless.Persistence.of( Customer.class ).findById


( updatedMelinda );
System.out.println("Now Melinda's discount rate is "+updatedMelinda.
getDiscountRatePercent() +" percent.");
// The asynchronous call will look as follows:
Backendless.Persistence.of(Customer.class).save( melinda, new
AsyncCallback<Customer>(){
public void handleResponse(Customer c) { }
public void handleFault (BackendlessFault f) { }
});
}

2.9

Updating Data Objects


Updating a data object which has been previously stored in the Backendless data store can be done
using the same API as for saving the initial object version. Once an object has objectId assigned, the
call updates the persisted copy of the object:
Please note: updating an object in a native database differs from updating an object in an external
database.

Updating an Object Individually in a Native Database


Method:
PUT

URL:
https://api.backendless.com/<version>/data/<table-name>/<object-id>
where:
<version>

<table-name>
<object-id>

- name of the application's version. Application versions can be


managed using Backendless Console. Login to the console,
select an application, click Manage, then Versioning.
Backendless automatically creates version "v1" for any new
application.
- name of the table where the object needs to be updated.
- ID of the object to update assigned by Backendless in the
create object operation.

Request Headers:
application-id: app-id-value
secret-key: secret-key-value
Content-Type:application/json
application-type: REST
where:
application-id

- the ID of your application generated upon its creation. You can


find this header in the Manage > App Settings section of the
2015 Backendless Corp.

Data Service

secret-key

Content-Type
application-type

53

Backendless Console. This header is mandatory. Please refer to


the Setup section for information on how to obtain the values for
the header.
- the key of your application generated upon its creation. You
can find this header in the Manage > App Settings section of
the Backendless Console. This header is mandatory. Please
refer to the Setup section for information on how to obtain the
values for the header.
- the static value, should be set to application/json. This header
is mandatory.
- the static value, should be set to REST. This header is
mandatory.

Sample Request Body:


{
"objectId" : "28325E9F-2DED-D3CA-FFC6-C76911AFBB00"
"name" : "James Bond",
"age" : 33,
"phoneNumber" : "+44123456789",
}
Sample Response Body:
{
"name" : "James Bond",
"age" : 33,
"phoneNumber" : "+44123456789",
"updated" : "02/05/2014 12:47:10 GMT+0000",
"created" : "02/04/2014 19:40:10 GMT+0000",
"ownerId" : null | <user-id>,
"objectId" : "28325E9F-2DED-D3CA-FFC6-C76911AFBB00",
"___class" : "Person"
}
where:
updated
created
ownerId
objectId
___class

- special property generated by Backendless which contains the


timestamp when the object was last updated.
- similar to 'updated', but contains the timestamp showing when
the object was initially created.
- when the object is created by an authenticated user, this
property contains the id of the currently logged in user object.
- unique id (GUID) assigned to the object.
- contains the name of the table where the object is stored. This
is the same value as the <table-name> in the URL.

Example:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-H Content-Type:application/json
-X PUT
-d "{\"name\":\"Bob\", \"age\":20 }"

2015 Backendless Corp.

54

Backendless API for Android

-v https://api.backendless.com/v1/data/Person/6C77C11B-E9B3-EB14FFA2-69F38CF48800
Notice the objectId value is put directly into the URL. The same value is optional in the JSON
body of the request.

Updating Several Objects at a Time (Bulk Update) in


a Native Database
Method:
PUT

URL:
https://api.backendless.com/<app version>/data/bulk/<table-name>?
where=<where clause>
where:
<app version>

<table-name>
<where clause>

- name of the application's version. Application versions can be


managed using Backendless Console. Login to the console,
select an application, click Manage, then Versioning.
Backendless automatically creates version "v1" for any new
application.
- name of the table where the objects need to be updated.
- condition for selecting the objects for the bulk update (please
refer to the examples below in this section). Should be URL
encoded.

Request Headers:
application-id: app-id-value
secret-key: secret-key-value
Content-Type:application/json
application-type: REST
where:
application-id

secret-key

Content-Type
application-type

- the ID of your application generated upon its creation. You can


find this header in the Manage > App Settings section of the
Backendless Console. This header is mandatory. Please refer to
the Setup section for information on how to obtain the values for
the header.
- the key of your application generated upon its creation. You
can find this header in the Manage > App Settings section of
the Backendless Console. This header is mandatory. Please
refer to the Setup section for information on how to obtain the
values for the header.
- the static value, should be set to application/json. This header
is mandatory.
- the static value, should be set to REST. This header is
mandatory.

Sample Request:
Bulk update allows changing several data objects selected by the specified criteria. For
instance, if you have the list of employees and need to update their salary info depending on
their time of employment, you can do it by using the bulk update feature.
2015 Backendless Corp.

Data Service

55

To test the feature:


1. Create two persons/employees: one employed for 15 days curl -H application-id:
application-id-value-from-console-H secret-key:secret-key-valuefrom-console -H Content-Type:application/json -X POST -d
"{\"name\":\"Tom\", \"age\":35, \"salary\":0, \"workDays\":\"15\"}"
-v https://api.backendless.com/v1/data/Person , another one employed for
20 days curl -H application-id:application-id-value-from-console-H
secret-key:secret-key-value-from-console -H Content-Type:
application/json -X POST -d "{\"name\":\"Bob\", \"age\":20,
\"salary\":0, \"workDays\":\"20\"}" -v https://api.backendless.com/
v1/data/Person.
Please note: salaries of both employees are zero.
2. To set new salary (1000) to all staff members that are employed for more than 10 days:
specify the new salary in the -d argument as follows: -d "{\"salary\":1000}"
set the value of <where clause> argument in URL to workDays>10 , that is:
where=workDays>10 . The URL encoded query will look as follows:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-H Content-Type:application/json
-X PUT
-d "{\"salary\":1000}"
-v https://api.backendless.com/v1/data/bulk/Person?where=workDays%
3E10
As a result you will receive the response displaying the number of data object updated (2
objects). The salary of both employees will be changed to 1000.
Sample Response:
Server will return the number of the updated objects.
Synchronous Method:
public <E> E Backendless.Persistence.save( E entity ) throws
BackendlessException

Asynchronous Method:
public <E> void Backendless.Persistence.save( E entity, AsyncCallback<E>
responder )

where:
entity
responder

- Java object to update.


- a responder object which will receive a callback when the
method successfully updates the object or if an error occurs.
Applies to the asynchronous method only.

Return Value:
The synchronous method returns the updated object. The asynchronous call receives the return
value through a callback executed on the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{

2015 Backendless Corp.

56

Backendless API for Android

private
private
private
private
private

String objectId;
String name;
int age;
String phone;
String title;

public String getObjectId() {


return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge( int age ) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone( String phone ) {
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
}

The following code saves a new instance of the Contact class and subsequently updates it:
Synchronous API:
public void updateContact()
{
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationID, secretKey, version);
Contact contact = new Contact();

2015 Backendless Corp.

Data Service

57

contact.setName( "Jack Daniels" );


contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Contact savedContact = Backendless.Persistence.save( contact );
// now update the saved object
savedContact.setTitle( "Most favorite" );
savedContact.setPhone( "666-666-666" );
Backendless.Persistence.save( savedContact );
}

Asynchronous API:
public void updateContact()
{
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationID, secretKey, version);
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Backendless.Persistence.save( contact, new AsyncCallback<Contact>() {
public void handleResponse( Contact savedContact )
{
savedContact.setTitle( "Most favorite" );
savedContact.setPhone( "666-666-666" );
Backendless.Persistence.save( savedContact, new
AsyncCallback<Contact>() {
@Override
public void handleResponse( Contact response )
{
// Contact instance has been updated
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with fault.
getCode()
}
});
}

Updating a Data Object in an External Database


2015 Backendless Corp.

58

Backendless API for Android

When updating a data object in an external database, you need to take into consideration that such
object does not have some properties characteristic for the native data objects. Thus, these properties
will be omitted in the queries when saving a data object to an external database.

The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have the following Customer class
public class Customer
{
private String lastName;
private Integer discountRatePercent;
private String firstName;
public String getLastName()
{
return lastName;
}
public void setLastName( String lastName )
{
this.lastName = lastName;
}
public Integer getDiscountRatePercent()
{
return discountRatePercent;
}
public void setDiscountRatePercent( Integer discountRatePercent )
{
this.discountRatePercent = discountRatePercent;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName( String firstName )
{
this.firstName = firstName;
}
}

You can identify a customer object by unique combination of name and surname and update this object
in an external database as follows:
private void saveAlreadyExistingObject()
{
// get a customer entity from DB, modify its properties and update it. Get
it again to make sure update is correct
// Notice that values of the primary keys cannot be changed.
Customer melinda = new Customer();
melinda.setFirstName( "Melinda" );
melinda.setLastName( "Grey" );
melinda = Backendless.Persistence.of( Customer.class ).findById( melinda );
System.out.println("Melinda's rate is "+melinda.getDiscountRatePercent()+"

2015 Backendless Corp.

Data Service

59

percent");
melinda.setDiscountRatePercent( melinda.getDiscountRatePercent() + 2 );
Backendless.Persistence.of(Customer.class).save(melinda);
Customer updatedMelinda = new Customer();
updatedMelinda.setFirstName( "Melinda" );
updatedMelinda.setLastName( "Grey" );
updatedMelinda = Backendless.Persistence.of( Customer.class ).findById
( updatedMelinda );
System.out.println("Now Melinda's discount rate is "+updatedMelinda.
getDiscountRatePercent() +" percent.");
// The asynchronous call will look as follows:
Backendless.Persistence.of(Customer.class).save( melinda, new
AsyncCallback<Customer>(){
public void handleResponse(Customer c) { }
public void handleFault (BackendlessFault f) { }
});
}

2.10

Deleting Data Objects


The API completely removes an object from the persistent store. If the object is successfully delete, the
API returns the timestamp of the exact deletion time in milliseconds.
Please note: deleting an object from a native database differs from deleting an object from an external
database.

Deleting an Object Individually from a Native


Database
Method:
DELETE

URL:
https://api.backendless.com/<version>/data/<table-name>/<object-id>
where:
<version>

<table-name>
<object-id>

- name of the application's version. Application versions can be


managed using Backendless Console. Login to the console,
select an application, click Manage, then Versioning.
Backendless automatically creates version "v1" for any new
application.
- name of the table where the object needs to be deleted.
- ID of the object to delete. The ID assigned by Backendless in
the create object operation.

Request Headers:
application-id: app-id-value

2015 Backendless Corp.

60

Backendless API for Android

secret-key: secret-key-value
application-type: REST
where:
application-id

secret-key

application-type

- the ID of your application generated upon its creation. You can


find this header in the Manage > App Settings section of the
Backendless Console. This header is mandatory. Please refer to
the Setup section for information on how to obtain the values for
the header.
- the key of your application generated upon its creation. You
can find this header in the Manage > App Settings section of
the Backendless Console. This header is mandatory. Please
refer to the Setup section for information on how to obtain the
values for the header.
- the static value, should be set to REST. This header is
mandatory.

Request Body:
None
Sample Response Body:
{
"deletionTime" : timestamp in milliseconds
}
Example:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-X DELETE
-v https://api.backendless.com/v1/data/Orders/6C77C11B-E9B3-EB14FFA2-69F38CF48800

Deleting Several Objects at a Time (Bulk Delete)


from a Native Database
Method:
DELETE

URL:
https://api.backendless.com/<app version>/data/bulk/<table-name>?
where=<where clause>
where:
<app version>

<table-name>
<where clause>

- name of the application's version. Application versions can be


managed using Backendless Console. Login to the console,
select an application, click Manage, then Versioning.
Backendless automatically creates version "v1" for any new
application.
- name of the table where the objects need to be deleted.
- condition for selecting the objects for the bulk delete (please
2015 Backendless Corp.

Data Service

61

refer to the examples below in this section). Should be URL


encoded.
Request Headers:
application-id: app-id-value
secret-key: secret-key-value
application-type: REST
where:
application-id

secret-key

application-type

- the ID of your application generated upon its creation. You can


find this header in the Manage > App Settings section of the
Backendless Console. This header is mandatory. Please refer to
the Setup section for information on how to obtain the values for
the header.
- the key of your application generated upon its creation. You
can find this header in the Manage > App Settings section of
the Backendless Console. This header is mandatory. Please
refer to the Setup section for information on how to obtain the
values for the header.
- the static value, should be set to REST . This header is

mandatory.
Sample Request:
Bulk delete allows removing several data objects selected by the specified criteria. For instance,
if you have the list of employees and need to delete some of them depending on their time of
employment, you can do it by using the bulk delete feature.
To test the feature:
1. Create three persons/employees:
employed for 15 days curl -H application-id:application-id-value-fromconsole-H secret-key:secret-key-value-from-console -H ContentType:application/json -X POST -d "{\"name\":\"Tom\", \"age\":35,
\"salary\":0, \"workDays\":\"15\"}" -v https://api.backendless.
com/v1/data/Person
employed for 0 days curl -H application-id:application-id-value-fromconsole-H secret-key:secret-key-value-from-console -H ContentType:application/json -X POST -d "{\"name\":\"Bob\", \"age\":20,
\"salary\":0, \"workDays\":\"0\"}" -v https://api.backendless.
com/v1/data/Person
employed for 0 days curl -H application-id:application-id-value-fromconsole-H secret-key:secret-key-value-from-console -H ContentType:application/json -X POST -d "{\"name\":\"Brad\", \"age\":22,
\"salary\":10, \"workDays\":\"0\"}" -v https://api.backendless.
com/v1/data/Person
2. To delete all staff members that are employed for 0 days, set the value of <where clause>
argument in URL to workDays=0 , that is: where=workDays=0 . The URL encoded query will
look as follows:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-X DELETE
-v https://api.backendless.com/v1/data/bulk/Person?where=workDays%

2015 Backendless Corp.

62

Backendless API for Android

3D0
As a result you will receive the response displaying the number of data object deleted (2
objects). The second and third employees (Bob and Brad) will be deleted from the database.
Sample Response:
Server will return the number of the deleted objects.
Synchronous Method:
Long result = Backendless.Persistence.of( E ).remove( E entity );
Asynchronous Method:
Backendless.Persistence.of( E ).remove( E entity, AsyncCallback<Long>
responder );

where:
entity
responder

- Java object to delete.


- a responder object which will receive a callback when the object
is deleted or if an error occurs. Applies to the asynchronous
method only.

Return Value:
The synchronous method returns the time stamp when the server-side removed the object from
the data store. The asynchronous call receives the return value through a callback executed on
the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
public String getObjectId() {
return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}

2015 Backendless Corp.

Data Service

63

public int getAge() {


return age;
}
public void setAge( int age ) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone( String phone ) {
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
}
The following code saves a new instance of the Contact class and subsequently deletes it:
Synchronous API:
public void deleteContact()
{
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationID, secretKey, version);
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Contact savedContact = Backendless.Persistence.save( contact );
// now delete the saved object
Long result = Backendless.Persistence.of( Contact.class ).remove(
savedContact );
}

Asynchronous API:
public void deleteContact()
{
String applicationID = "";
String secretKey = "";
String version = "";

2015 Backendless Corp.

64

Backendless API for Android

Backendless.initApp( applicationID, secretKey, version);


Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Backendless.Persistence.save( contact, new AsyncCallback<Contact>()
{
public void handleResponse( Contact savedContact )
{
Backendless.Persistence.of( Contact.class ).remove( savedContact, new
AsyncCallback<Contact>()
{
public void handleResponse( Long response )
{
// Contact has been deleted. The response is a time in
milliseconds when the object was deleted
}
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with fault.
getCode()
}
});
}

Deleting a Data Object from an External Database


When deleting a data object from an external database, you need to take into consideration that such
object does not have some properties characteristic for the native data objects. Thus, these properties
will be omitted in the queries when deleting a data object from an external database.
The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have the following Customer class
public class Customer
{
private String lastName;
private Integer discountRatePercent;
private String firstName;
public String getLastName()
{
return lastName;
}
public void setLastName( String lastName )
{

2015 Backendless Corp.

Data Service

65

this.lastName = lastName;
}
public Integer getDiscountRatePercent()
{
return discountRatePercent;
}
public void setDiscountRatePercent( Integer discountRatePercent )
{
this.discountRatePercent = discountRatePercent;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName( String firstName )
{
this.firstName = firstName;
}
}

You can identify a customer object by unique combination of name and surname and delete this object
from an external database as follows:
private void deleteExistingObject()

Customer mia = new Customer();


mia.setFirstName( "Mia" );
mia.setLastName("Yinuo");
mia.setDiscountRatePercent( 0 );
Backendless.Persistence.of(Customer.class).save( mia );
Backendless.Persistence.of(Customer.class).remove( mia );
}

2.11

Retrieving Schema Definition


Backendless supports API for data table schema introspection. The API provides information about
table's columns and their data types, whether a value for a column is required or if there is a default
value.
Asynchronous Method:
Backendless.Persistence.describe( String tableName,
AsyncCallback<List<ObjectProperty>> callback )

where:
tableName
callback

- name of the table to get the schema definition for.


- a responder with references to the methods called back upon
successful invocation or an error.

Synchronous Method:
public List<ObjectProperty> Backendless.Persistence.describe( String
tableName )

where:
2015 Backendless Corp.

66

Backendless API for Android

tableName

- name of the table to get the schema definition for.

Return value:
Method returns a collection of the ObjectProperty instances. The ObjectProperty class includes
the following Java bean properties:
autoLoad:boolean
- applies only to relations. If true, the property is set to autoload related data for the data retrieval queries.
customRegex:String
- a regular expression assigned to the column as a validator.
The validator applies when a new object is saved in the table
or an existing one is updated.
defaultValue:Object
- a default value assigned to any object saved/updated in the
table where the column does not have a value.
isPrimaryKey:boolean
- true if the column is or is a part of a primary key.
name:String
- contains the name of a property
relatedTable:String
- contains the name of the related table(s)
required:boolean
- defines whether a property is optional or required for the
requests which save the initial object or update an existing
one.
type:String
- defines the property type
Example:
The example below describes how to retrieve the properties of the user object. Consider the
following class:
public class Person
{
private String name;
private int age;
public Person ()
{
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge( int age )
{
this.age = age;
}
}

Save an instance of the Person class in the persistent storage (Backendless create the Person
table if it does not exist):

2015 Backendless Corp.

Data Service

67

Person person = new Person();


person.setName( "Bob" );
person.setAge( 20 );
// asynchronous call
Backendless.Persistence.of( Person.class ).save( person, new
AsyncCallback<Person>()
{
@Override
public void handleResponse( Person person )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
} );
// synchronous call
Backendless.Persistence.of( Person.class ).save( person );

Run the following request to retrieve table schema:


// asynchronous call
Backendless.Persistence.describe( "Person", new
AsyncCallback<List<ObjectProperty>>()
{
@Override
public void handleResponse( List<ObjectProperty>
objectProperties )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
} );
// synchronous call
Backendless.Persistence.describe( "Person" );

2.12

Basic Search
Backendless supports multiple basic search operations. These include finding an object by ID, finding
first or last object in the collection or retrieving the entire persisted collection. Each method is available
in both synchronous and asynchronous versions:
Please note: searching for an object in a native database differs from searching for an object in an
external database.

Searching for a Data Object in a Native Database


Synchronous Methods:

2015 Backendless Corp.

68

Backendless API for Android

Retrieve all data objects of class E.


public void Backendless.Persistence.of( E ).find() throws
BackendlessException;

Find first data object of class E. The first data object is the first one saved in the data store:
public void Backendless.Persistence.of( E ).findFirst() throws
BackendlessException;

Find last data object of type E. The last data object is the last one saved in the data store:
public <E> E Backendless.Persistence.of( E ).findLast() throws
BackendlessException;

Find a data object by its ID:


public <E> E Backendless.Persistence.of( E ).findById() throws
BackendlessException;

Asynchronous Methods:
Retrieve all data objects of type E:
public void Backendless.Persistence.of( E ).find
( AsyncCallback<BackendlessCollection<E>> responder );

Find first data object of type E. The first data object is the first one saved in the data store:
public void Backendless.Persistence.of( E ).findFirst( AsyncCallback<E>
responder )

Find last data object of type E. The last data object is the last one saved in the data store:
public void Backendless.Persistence.of( E ).findLast( AsyncCallback<E>
responder )

Find a data object by its ID:


public void Backendless.Persistence.of( E ).findById( AsyncCallback<E>
responder )

where:
E

responder

- class object for the data type to search data objects for. For
example, if the class is "Person", the argument should Person.
class.
- a responder object which will receive a callback when the
method successfully returns the result or if an error occurs.
Applies to the asynchronous methods only.

Example:
Consider the following class:
package com.sample;
public class Contact
{

2015 Backendless Corp.

Data Service

private
private
private
private
private

69

String objectId;
String name;
int age;
String phone;
String title;

public String getObjectId() {


return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge( int age ) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone( String phone ) {
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
}
The following code demonstrates various search queries:
Find all contacts:
Synchronous call:
BackendlessCollection<Contact> result = Backendless.Persistence.of(

2015 Backendless Corp.

70

Backendless API for Android

Contact.class ).find();
Asynchronous call:
Backendless.Persistence.of( Contact.class).find( new
AsyncCallback<BackendlessCollection<Contact>>(){
@Override
public void handleResponse( BackendlessCollection<Contact>
foundContacts )
{
// all Contact instances have been found
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
});
Find first contact:
Synchronous call:
Contact firstContact = Backendless.Persistence.of( Contact.class ).
findFirst();
Asynchronous call:
Backendless.Persistence.of( Contact.class).findFirst( new
AsyncCallback<Contact>(){
@Override
public void handleResponse( Contact contact )
{
// first contact instance has been found
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
});
Find last contact:
Synchronous call:
Contact lastContact = Backendless.Persistence.of( Contact.class ).
findLast();
Asynchronous call:
Backendless.Persistence.of( Contact.class).findLast( new
AsyncCallback<Contact>(){
@Override
public void handleResponse( Contact contact )

2015 Backendless Corp.

Data Service

71

{
// last contact instance has been found
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
});
Find contact by ID:
Synchronous call:
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
// save object synchronously
Contact savedContact = Backendless.Persistence.save( contact );
Contact lastContact = Backendless.Persistence.of( Contact.class ).
findById( savedContact.getObjectId() );
Asynchronous call:
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Backendless.Persistence.save( contact, new AsyncCallback<Contact>()
{
@Override
public void handleResponse( Contact savedContact )
{
Backendless.Persistence.of( Contact.class ).findById(
savedContact.getObjectId(), new AsyncCallback<Contact>() {
@Override
public void handleResponse( Contact response )
{
// a Contact instance has been found by ObjectId
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved
with fault.getCode()
}
} );
}

2015 Backendless Corp.

72

Backendless API for Android

@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
} );

Searching for a Data Object in an External Database


When searching for a data object in an external database, it is important to keep in mind that external
objects do not have all the same properties as the native data objects. See the complete list of
differences for more details.
The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have the following Customer class
public class Customer
{
private String lastName;
private Integer discountRatePercent;
private String firstName;
public String getLastName()
{
return lastName;
}
public void setLastName( String lastName )
{
this.lastName = lastName;
}
public Integer getDiscountRatePercent()
{
return discountRatePercent;
}
public void setDiscountRatePercent( Integer discountRatePercent )
{
this.discountRatePercent = discountRatePercent;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName( String firstName )
{
this.firstName = firstName;
}
}

You can identify a customer object by unique combination of name and surname and search for this
object in an external database as follows:
2015 Backendless Corp.

Data Service

73

private void findByIdWithObject()


{
Customer elle = new Customer();
elle.setFirstName( "Elle" );
elle.setLastName( "Woods" );
//synchonous call
Customer elleInDb = Backendless.Persistence.of( Customer.class ).findById
( elle );
System.out.println( "Elle's discount rate is "+ elleInDb.
getDiscountRatePercent()+" percent.");
//asynchronous call
Backendless.Persistence.of( Customer.class ).findById( elle, new
AsyncCallback<Customer>(){
public void handleResponse(Customer c){
System.out.println("okay, got Elle with discount rate %"+ c.
getDiscountRatePercent());
}
public void handleFault(BackendlessFault fault){
System.out.println("Shucks, could not find Elle");
}
});
}

2.13

Advanced Search
Advanced search use-cases supported by Backendless include:
Search with query - a query is an SQL-92 expression (the where clause) referencing data object
properties.
Paged search - sets the page size and index (offset) to search from.
Sorted search - lists the data object properties to sort the result collection by.
Additionally, search requests may include special modifiers:
Request for specific properties of the data objects returned in the result collection.
Request to return related data objects attached to the 'parent' objects in the result collection.

Searching for a Data Object


Backendless supports the use-cases and modifiers listed above with a special class BackendlessDataQuery :
package com.backendless.persistence;
public class BackendlessDataQuery
{
public BackendlessDataQuery();
public BackendlessDataQuery( List<String> properties );
public BackendlessDataQuery( String whereClause );
public BackendlessDataQuery( QueryOptions queryOptions );
public BackendlessDataQuery( List<String> properties, String
whereClause, QueryOptions queryOptions );

2015 Backendless Corp.

74

Backendless API for Android

// Methods working with the data object property names.


// When BackendlessDataQuery contains a collection of properties,
// the query response will contain data objects only with the
// requested property names

public void setProperties( List<String> properties )


public void addProperty( String property );
public List<String> getProperties();
// Gets/sets the where clause - a query to search for data objects with

public String getWhereClause();


public void setWhereClause( String whereClause );
// Gets/sets QueryOptions - the options are used for sorting data,
// paging and loading related data

public QueryOptions getQueryOptions();


public void setQueryOptions( QueryOptions queryOptions );
// Convenience getters and setters facilitating paged data access.
// This is an alternative to setting page size and offset in QueryOptions
// associated with this BackendlessDataQuery.

public
public
public
public

int getPageSize();
void setPageSize( int pageSize );
int getOffset();
void setOffset( int offset );

}
The QueryOptions class is used by BackendlessDataQuery and is defined as:
package com.backendless.persistence;
public class QueryOptions
{
public QueryOptions()
public QueryOptions( int pageSize, int offset )
public QueryOptions( int pageSize, int offset, String sortBy )
public QueryOptions( String sortBy )
public
public
public
public

void setPageSize( Integer pageSize )


int getPageSize()
void setOffset( Integer offset )
int getOffset()

public void addRelated( String related )


public void setRelated( List<String> related )
public List<String> getRelated()
public void setSortBy( List<String> sortBy )
public void addSortByOption( String sortBy )
public List<String> getSortBy()
}

2015 Backendless Corp.

Data Service

75

To run a query-based search, use the following API methods:


Method Signature:
Synchronous Method:
BackendlessCollection<E> result = Backendless.Persistence.of( E ).find
( BackendlessDataQuery query );

Asynchronous Method:
Backendless.Persistence.of( E ).find( BackendlessDataQuery query,
AsyncCallback<BackendlessCollection<E>>
);

where:
query
responder

- an instance of BackendlessDataQuery - containing the search


query and other search options.
- a responder object which will receive a callback when the
search operation returns the result. Applies to the asynchronous
method only.

Return Value:
The synchronous method returns a collection of strongly typed objects found as a result of the
query execution. The asynchronous call receives the return value through a callback executed
on the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
public String getObjectId() {
return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}

2015 Backendless Corp.

76

Backendless API for Android

public int getAge() {


return age;
}
public void setAge( int age ) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone( String phone ) {
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
}
The code below stores an instance of the Contact class in the Backendless data store:
String applicationID = "YOUR-APPLICATION-ID";
String secretKey = "YOUR-JAVA/ANDROID-SECRET-KEY";
String version = "v1";
Backendless.initApp( applicationID, secretKey, version );
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
// save object synchronously
Contact savedContact = Backendless.Persistence.save( contact );
The following code demonstrates various search queries:
Find all contacts where the value of the "age" property equals 147:
String whereClause = "age = 147";
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause );
BackendlessCollection<Contact> result = Backendless.Persistence.of(
Contact.class ).find( dataQuery );
Find all contacts where the value of the "age" property is greater than 21:
String whereClause = "age > 21";

2015 Backendless Corp.

Data Service

77

BackendlessDataQuery dataQuery = new BackendlessDataQuery();


dataQuery.setWhereClause( whereClause );
BackendlessCollection<Contact> result = Backendless.Persistence.of(
Contact.class ).find( dataQuery );
Find all contacts where the value of the "age" property is between 21 and 30:
String whereClause = "age >= 21 AND age <=30";
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause );
BackendlessCollection<Contact> result = Backendless.Persistence.of(
Contact.class ).find( dataQuery );
Find all contacts by name:
String whereClause = "name = 'Jack Daniels'";
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause );
BackendlessCollection<Contact> result = Backendless.Persistence.of(
Contact.class ).find( dataQuery );
Find all contacts by partial name match:
String whereClause = "name LIKE 'Jack%'";
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause );
BackendlessCollection<Contact> result = Backendless.Persistence.of(
Contact.class ).find( dataQuery );
Find objects within certain distance from a geo point

With the ability to link data objects with geo points, you can search for data
objects by distance. This type of search returns all data objects located within
the specified distance. Distance-based search uses a special function in
whereClause of the search request. The syntax of the function is:
distance(
center point latitude,
center point longitude,
columnname which contains geo point.latitude,
columnname which contains geo point.longitude ) <operator>
units-function(value)
where:
<operator>
units-function

- Possible values are <, >, =, >=, <=


- Defines the units of measure for the distance. Possible values
are:
ft( X ) - the distance value X is expressed in feet
km( X ) - the distance value X is expressed in kilometers
mi( X ) - the distance value X is expressed in miles
yd( X ) - the distance value X is expressed in yards

For example, the following expression searches for data objects located within 200 miles from
2015 Backendless Corp.

78

Backendless API for Android

the point at 30.26715, -97.74306. Each data object must have the "coordinates" property
of type GeoPoint.
distance( 30.26715, -97.74306, coordinates.latitude, coordinates.longitude )
< mi(200)

The following example demonstrates a search-by-distance query. The example


uses three data objects stored in the Friend table: Bob, Jane, and Fred who
respectively live in Austin, Houston, San Antonio. The search query in the
example finds all friends who love within the specified distance. Before running
the search query, create the objects in the data storage with the corresponding
geo points.
The Friend class definition:
import com.backendless.geo.GeoPoint;
public class Friend
{
private String name;
private String phoneNumber;
private GeoPoint coordinates;
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public String getPhoneNumber()
{
return phoneNumber;
}
public void setPhoneNumber( String phoneNumber )
{
this.phoneNumber = phoneNumber;
}
public GeoPoint getCoordinates()
{
return coordinates;
}
public void setCoordinates( GeoPoint coordinated )
{
this.coordinates = coordinated;
}
}

Run the following query/code to store a data object representing Bob with a link to his home in
Austin, TX:
Friend bob = new Friend();

2015 Backendless Corp.

Data Service

79

bob.setName( "Bob" );
bob.setPhoneNumber( "512-555-1212" );
bob.setCoordinates( new GeoPoint( 29.76328, -95.36327 ) );
bob.getCoordinates().addCategory( "Home" );
bob.getCoordinates().addMetadata( "description", "Bob's home" );
Backendless.Data.of( Friend.class ).save( bob );

Run the following query/code to store a data object representing Jane with a link to his home in
Houston, TX:
Friend jane = new Friend();
jane.setName( "Jane" );
jane.setPhoneNumber( "281-555-1212" );
jane.setCoordinates( new GeoPoint( 29.76328, -95.36327 ) );
jane.getCoordinates().addCategory( "Home" );
jane.getCoordinates().addMetadata( "description", "Jane's home" );
Backendless.Data.of( Friend.class ).save( jane );

Run the following query/code to store a data object representing Fred with a link to his home in
San Antonio, TX:
Friend fred = new Friend();
fred.setName( "Fred" );
fred.setPhoneNumber( "210-555-1212" );
fred.setCoordinates( new GeoPoint( 29.42412, -98.49363 ) );
fred.getCoordinates().addCategory( "Home" );
fred.getCoordinates().addMetadata( "description", "Fred's home" );
Backendless.Data.of( Friend.class ).save( fred );

Once the data is in the persistent object and geo location storage, run the following code/query
to perform a distance-based search:
String whereClause = "distance( 30.26715, -97.74306, coordinates.latitude,
coordinates.longitude ) < mi(200)";
BackendlessDataQuery dataQuery = new BackendlessDataQuery( whereClause );
QueryOptions queryOptions = new QueryOptions();
queryOptions.setRelationsDepth( 1 );
dataQuery.setQueryOptions( queryOptions );
BackendlessCollection<Friend> friends = Backendless.Data.of( Friend.class ).
find( dataQuery );
for( Friend friend : friends.getData() )

{
System.out.println( String.format( "%s lives at %f, %f tagged as '%s'",
friend.getName(), friend.getCoordinates().getLatitude(), friend.
getCoordinates().getLongitude(),
(String) friend.getCoordinates().
getMetadata( "description" ) ) );

The search returns all data objects within the specified distance. Each data object has the
coordinates property containing the coordinates of a geo point associated with this data

object.

2015 Backendless Corp.

80
2.14

Backendless API for Android

Using Dates in Search


There is a special consideration for the whereClause-based queries which reference a column of the
DATETIME data type. Typically a DATETIME column is referenced in a comparison against a scalar value
describing a specific date or a timestamp. The scalar value can be a number of milliseconds since the
epoch (UNIX timestamp as milliseconds) or a string. Backendless supports a variety of date formats for
the latter. For example, the queries below will find all the objects which were updated after March 23rd,
2015:
updated > '23-Mar-2015'
updated > '03/23/2015'
updated > 1427068800000

Comparison Operators
Backendless supports the following date comparison operators:
Column's value is after the specified date/time: use either > or the after keyword:
birthDate > '22-Dec-1980'
birthDate after 1427068800000
Column's value is before the specified date/time: use either < or the before keyword:
birthDate < '22-Dec-1980'
birthDate before 1427068800000
Column's value is either at or after the specified date/time: use either => or the at or after
keyword:
birthDate >= '28-10-1988'
birthDate at or after '10/28/1988 00:00:00 GMT-0200'
Column's value is either at or before the specified date/time: use either <= or the at or
before keyword:
birthDate >= '28-10-1988'
birthDate at or after '10/28/1988 00:00:00 GMT-0200'
Note: the whereClause-based queries can be tested in the Backendless Console with the SQL Search
turned on.

Supported Date Formats


Date/time string values may be in any of the following formats. The pattern letters have the same
definition as in Java's SimpleDateFormat:
EEE MMM dd HH:mm:ss zzz yyyy

2015 Backendless Corp.

Data Service

81

MM/dd/yyyy HH:mm:ss 'GMT'z


MM.dd.yyyy HH:mm:ss 'GMT'z
MM-dd-yyyy HH:mm:ss 'GMT'z
MM.dd.yyyy HH:mm:ss
MM-dd-yyyy HH:mm:ss
MM/dd/yyyy HH:mm:ss
MM.dd.yyyy
MM-dd-yyyy
MM/dd/yyyy HH:mm:ss z
MM/dd/yyyy HH:mm:ss 'GMT'Z
MM.dd.yyyy HH:mm:ss z
MM/dd/yyyy HH:mm
MM/dd/yyyy
dd/MMM/yyyy
dd-MMM-yyyy
EEEEE, d MMMMM yyyy
ddMyy
d MMMMM yyyy, HH'h' mm'm' ss's'
yyyy/MM/d/hh:mm:ss
yyyy-MM-dd'T'hh:mm:ss
EEEEE, MMMMM d, yyyy
MMMMM d, yyyy
yyyy-MM-dd
yyyy M d
yyyyMMMd
yyyy-MMM-d
yyyy-M-d, E
yyyyMMdd
'Date' yyyy-MM-dd
yyyy-MM-dd'T'hh:mm:ssZ
yyyy-MM-dd'T'hh:mmZ
yyyy-'W'w-d
yyyy-DDD

Example
2.15

Relations Overview
A data object stored in a Backendless backend may reference other objects. These references are
called relations. There are two types of relations: one-to-one and one-to-many. Relations may be
declared manually in a table schema using the Backendless Console or derived (and added to schema)
from the objects which are being saved. Additionally, Backendless supports bidirectional relations
between the objects stored in the Data Service and other entities in a Backendless backend. For
example, a data object may have a relation with a User object and/or Geo Point objects.

Creating Relations
Relations can be declared manually by using the Backendless Console. Creating a relation between
objects involves two main steps:
1. Declaring a relationship between the tables where the objects are stored.
2. Establishing relations between the objects from the related tables.

2015 Backendless Corp.

82

Backendless API for Android

Declaring a Relation Between Tables


The instructions below describe how to declare a relation between two tables:
1. Select a table where a relation should be declared.
2. Click the Table Schema and Permissions button in the top right corner of the user
interface.
3. Click the Add Column button. The pop-up window will display as shown below:

4. Enter column name that will represent the relation.


5. Click the Type drop-down list and select the Data Object Relationship option.

6. The pop-up window will display new menu options. Select a related table and the cardinality
of the relations from the corresponding drop-down menus. The one-to-one relation means that
a table's object can be linked with only one object from the related table. While the one-tomany relation means that a table's object can be linked with several objects from the related

2015 Backendless Corp.

Data Service

83

table.

7. Click Save to save the changes.


Once a relationship column is declared, it will appear along other columns in the Data Browser view of
the Backendless Console.

Creating Relations Between Objects in Related Tables


Once you have declared a relation between table schemas, you can establish a relation between objects
in these tables. Follow the instructions below to establish a relationship:
1. Click the table name where you declared a relation. Console displays the columns
representing relations slightly different than the other ones. The header for these columns
includes:
- name of the related table;
- relation type (cardinality) visualized as either a single line for one-to-one relations or three
lines for one-to-many relations;
- the "auto-load" checkbox.

2015 Backendless Corp.

84

Backendless API for Android

2. Click the plus icon next to the object, for which you want to create a relation. The Set
Related Object pop-up window will display the list of objects from the related table.

3. Each object in the displayed popup has either a radio button or a checkbox next to the
object's data. Radio buttons are used for one-to-one relations, while checkboxes apply for the
one-to-many relations. Select the object(s) which will be linked to the parent object.
4. Click the Set Related Object button to save the changes.
The Data Service API can be used to link objects from different tables to form a relation. For more
information see the Relations (Save/Update) section of the guide.

Editing Relations
2015 Backendless Corp.

Data Service

85

You can edit the relations between the data objects via the Backendless Console:
1. Click the name of the table containing the object with relations. Click the plus icon next to
the relation you want to edit:

2. The Set Related Object pop-up window will display. If you want to link a data object with
different object(s), click the radio-button or check-box(s) next to the necessary object(s).
3. Click the Set Related Object button to save the changes.
Refer to the Relations (Save/Update) section to learn how to update relations by using the API.

Deleting Relations
You can delete both relations between the objects and between the tables if necessary. Relations
between the tables can be deleted only by using the Backendless Console. While relations between the
objects can be deleted either via the Backendless Console or API.
To delete relations between the tables by using the Backendless Console:
1. Click the name of the table that you want to unlink from another table.
2. Click the Table Schema and Permissions button at the top right. The Table Schema and
Permissions page will display.
3. Click the check-box(s) next to the column associated with the relation(s) between the
tables.

2015 Backendless Corp.

86

Backendless API for Android

4. Click the Delete Selected button.


To delete relations between the data objects via the Backendless Console:
1. Click the name of the table, where you want to delete a relation. Click the plus icon next to
the relation you want to delete.

2. The Set Related Object pop-up window will display. Click the Unlink Related Object
button to delete the relation.
Additionally, a relation between two objects (not tables) can be deleted by using the API. For more
information see the Relations (Delete) section.

2015 Backendless Corp.

Data Service

2.16

87

Relations (Save/Update)
Backendless Data Service supports a complete set of CRUD (create, retrieve, update, delete) operations
for related objects.
Please note: handling relations in a native database differs from handling relations in an external
database.

Handling Relations in a Native Database


Consider the following class diagram:

Notice the PhoneBook entity references Contact through two properties - "owner" (the one to one
relationship) and "contacts" (the one to many relationship). Each contact entity also references
Address. These entities will be used to demonstrate how Backendless handles related entities in all
basic data persistence operations. Consider the following class definitions for these entities:
PhoneBook Class
package com.sample.entity;
import java.util.List;
public class PhoneBook
{
private String objectId;
private Contact owner;
private List<Contact> contacts;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public Contact getOwner()
{
return owner;
}
public void setOwner( Contact owner )

2015 Backendless Corp.

88

Backendless API for Android

{
this.owner = owner;
}
public List<Contact> getContacts()
{
return contacts;
}
public void setContacts( List<Contact> contacts )
{
this.contacts = contacts;
}
}

Contact Class
package com.sample.entity;
import java.util.Date;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
private Address address;
private Date updated;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge( int age )
{
this.age = age;
}

2015 Backendless Corp.

Data Service

public String getPhone()


{
return phone;
}
public void setPhone( String phone )
{
this.phone = phone;
}
public String getTitle()
{
return title;
}
public void setTitle( String title )
{
this.title = title;
}
public Address getAddress()
{
return address;
}
public void setAddress( Address address )
{
this.address = address;
}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}
}

Address Class
package com.sample.entity;
public class Address
{
private String street;
private String city;
private String state;
public String getStreet()
{
return street;
}
public void setStreet( String street )
{
this.street = street;

2015 Backendless Corp.

89

90

Backendless API for Android

}
public String getCity()
{
return city;
}
public void setCity( String city )
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState( String state )
{
this.state = state;
}
}

Consider the following examples of saving objects with related data:


Constructing PhoneBook with the Owner and no Contacts:

Address address = new Address();


address.setStreet( "TN 55" );
address.setCity( "Lynchburg" );
address.setState( "Tennessee" );
Contact owner = new Contact();
owner.setName( "Jack Daniels" );
owner.setAge( 147 );
owner.setPhone( "777-777-777" );
owner.setTitle( "Favorites" );
owner.setAddress( address );
PhoneBook phoneBook = new PhoneBook();
phoneBook.setOwner( owner );
PhoneBook savedPhoneBook = Backendless.Persistence.save( phoneBook );

Constructing PhoneBook with the Owner and two Contacts:

2015 Backendless Corp.

Data Service

Address address = new Address();


address.setStreet( "TN 55" );
address.setCity( "Lynchburg" );
address.setState( "Tennessee" );
Contact owner = new Contact();
owner.setName( "Jack Daniels" );
owner.setAge( 147 );
owner.setPhone( "777-777-777" );
owner.setTitle( "Favorites" );
owner.setAddress( address );
Address contact1Address = new Address();
contact1Address.setStreet( "Main St" );
contact1Address.setCity( "Smallville" );
contact1Address.setState( "Kansas" );
Contact contact1 = new Contact();
contact1.setName( "Clark Kent" );
contact1.setAge( 75 );
contact1.setPhone( "111-111-111" );
contact1.setTitle( "Super heroes" );
contact1.setAddress( contact1Address );
Address contact2Address = new Address();
contact2Address.setStreet( "Cavern-X" );
contact2Address.setCity( "Sedona" );
contact2Address.setState( "Arizona" );
Contact contact2 = new Contact();
contact2.setName( "Wade Winston Wilson" );
contact2.setAge( 22 );
contact2.setPhone( "222-222-222" );
contact2.setTitle( "Super heroes" );
contact2.setAddress( contact2Address );
List<Contact> contacts = new ArrayList<Contact>();
contacts.add( contact1 );

2015 Backendless Corp.

91

92

Backendless API for Android

contacts.add( contact2 );
PhoneBook phoneBook = new PhoneBook();
phoneBook.setOwner( owner );
phoneBook.setContacts( contacts );
PhoneBook savedPhoneBook = Backendless.Persistence.save( phoneBook );

Add Contact to an existing PhoneBook (uses the "savedPhoneBook" object created at the
end of the example above):

// skipped code for brevity..


// use the code from the example above
PhoneBook savedPhoneBook = Backendless.Persistence.save( phoneBook );
Address contact2Address = new Address();
contact2Address.setStreet( "Cavern-X" );
contact2Address.setCity( "Sedona" );
contact2Address.setState( "Arizona" );
Contact contact2 = new Contact();
contact2.setName( "Wade Winston Wilson" );
contact2.setAge( 22 );
contact2.setPhone( "222-222-222" );
contact2.setTitle( "Super heroes" );
contact2.setAddress( contact2Address );

2015 Backendless Corp.

Data Service

93

// Add contact to the saved phone book


savedPhoneBook.getContacts().add( contact2 );
// And save the phone book again - the new object will be
// properly created in the Backendless data store
PhoneBook updatedPhoneBook = Backendless.Persistence.save( savedPhoneBook );

Update a property in the aggregated object (update the phone number in the "owner" of
PhoneBook) and save PhoneBook:

// use any of the samples above to the point when PhoneBook is saved,
// then use the savedPhoneBook instance:
savedPhoneBook.getOwner().setPhone( "555-555-555" );
PhoneBook updatedPhoneBook = Backendless.Persistence.save( savedPhoneBook );

Removing one contact from PhoneBook, adding another and re-saving PhoneBook:

2015 Backendless Corp.

94

Backendless API for Android

// use any of the samples above to the point when PhoneBook is saved,
// then use the savedPhoneBook instance:
Address contact2Address = new Address();
contact2Address.setStreet( "Cavern-X" );
contact2Address.setCity( "Sedona" );
contact2Address.setState( "Arizona" );
Contact contact2 = new Contact();
contact2.setName( "Wade Winston Wilson" );
contact2.setAge( 22 );
contact2.setPhone( "222-222-222" );
contact2.setTitle( "Super heroes" );
contact2.setAddress( contact2Address );
savedPhoneBook.getContacts().remove( 0 );
savedPhoneBook.getContacts().add( contact2 );
PhoneBook updatedPhoneBook = Backendless.Persistence.save( savedPhoneBook );

Handling Relations in an External Database


Saving and updating the relations between the data objects in an external database works generally the
same way as in the native database. Except
the class diagram will include the Contact and Address entities only
when saving/updating relations in an external database, contactId and addressId argument

2015 Backendless Corp.

Data Service

95

will be used instead of objectId argument used in the native database.

The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have OrderItem class and Order class
public class OrderItem
{
private
private
private
private
private
private

Integer itemId;
String itemName;
Integer quantitySold;
String pricePerItem;
Order Order_id;
Manufacturer Manufacturer_id;

public Integer getItemId()


{
return itemId;
}
public void setItemId( Integer id )
{
this.itemId = itemId;
}
public String getItemName()
{
return itemName;
}
public void setItemName( String itemName )
{
this.itemName = itemName;
}
public Integer getQuantitySold()
{
return quantitySold;
}

2015 Backendless Corp.

96

Backendless API for Android

public void setQuantitySold( Integer quantitySold )


{
this.quantitySold = quantitySold;
}
public String getPricePerItem()
{
return pricePerItem;
}
public void setPricePerItem( String pricePerItem )
{
this.pricePerItem = pricePerItem;
}
public Order getOrder_id()
{
return Order_id;
}
public void setOrder_id( Order Order_id )
{
this.Order_id = Order_id;
}
public Manufacturer getManufacturer_id()
{
return Manufacturer_id;
}
public void setManufacturer_id( Manufacturer Manufacturer_id )
{
this.Manufacturer_id = Manufacturer_id;
}
}

public class Order


{
private java.util.Date placed;
private String totalCost;
private Integer id;
public java.util.Date getPlaced()
{
return placed;
}
public void setPlaced( java.util.Date placed )
{
this.placed = placed;
}
public String getTotalCost()
{
return totalCost;
}
public void setTotalCost( String totalCost )

2015 Backendless Corp.

Data Service

97

{
this.totalCost = totalCost;
}
public Integer getId()
{
return id;
}
public void setId( Integer id )
{
this.id = id;
}
}

To find an order item with relation(s) and update it, run the following query:
private void findAnOrderItemWithRelationsAndUpdateARelation()
{
OrderItem item = new OrderItem();
item.setItemId( 1 );
OrderItem foundItem = Backendless.Persistence.of(OrderItem.class).findFirst
( 2 );
Order order = foundItem.getOrder_id();
order.setTotalCost( order.getTotalCost()+100 );
Backendless.Persistence.of( Order.class ).save( order );
}

2.17

Relations (Delete)
Backendless Data Service supports a complete set of CRUD (create, retrieve, update, delete) operations
for related objects.
Please note: handling relations in a native database differs from handling relations in an external
database.

Handling Relations in a Native Database


Consider the following class diagram:

Notice the PhoneBook entity references Contact through two properties - "owner" (the one to one
relationship) and "contacts" (the one to many relationship). Each contact entity also references
2015 Backendless Corp.

98

Backendless API for Android

Address. These entities will be used to demonstrate how Backendless handles related entities in all
basic data persistence operations. Consider the following class definitions for these entities:
PhoneBook Class
package com.sample.entity;
import java.util.List;
public class PhoneBook
{
private String objectId;
private Contact owner;
private List<Contact> contacts;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public Contact getOwner()
{
return owner;
}
public void setOwner( Contact owner )
{
this.owner = owner;
}
public List<Contact> getContacts()
{
return contacts;
}
public void setContacts( List<Contact> contacts )
{
this.contacts = contacts;
}
}

Contact Class
package com.sample.entity;
import java.util.Date;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
private Address address;
private Date updated;

2015 Backendless Corp.

Data Service

public String getObjectId()


{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge( int age )
{
this.age = age;
}
public String getPhone()
{
return phone;
}
public void setPhone( String phone )
{
this.phone = phone;
}
public String getTitle()
{
return title;
}
public void setTitle( String title )
{
this.title = title;
}
public Address getAddress()
{
return address;
}
public void setAddress( Address address )
{
this.address = address;

2015 Backendless Corp.

99

100

Backendless API for Android

}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}
}

Address Class
package com.sample.entity;
public class Address
{
private String street;
private String city;
private String state;
public String getStreet()
{
return street;
}
public void setStreet( String street )
{
this.street = street;
}
public String getCity()
{
return city;
}
public void setCity( String city )
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState( String state )
{
this.state = state;
}
}

Consider the following examples of saving objects with related data:


Delete parent object (deleting a PhoneBook):

2015 Backendless Corp.

Data Service

101

// use any of the samples above to the point when PhoneBook is saved,
// then use the savedPhoneBook instance:
Long result = Backendless.Persistence.of( PhoneBook.class ).remove
( savedPhoneBook );

Remove a contact from PhoneBook and save the change:

// use any of the samples above. Take the "savedPhoneBook" object


// and remove a contact from it:
savedPhoneBook.getContacts().remove( 0 );

2015 Backendless Corp.

102

Backendless API for Android

// now save PhoneBook to reflect the change in the remote data


storage:
PhoneBook updatedPhoneBook = Backendless.Persistence.save
( savedPhoneBook );

Deleting Relations in an External Database


The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have OrderItem class and Order class
public class OrderItem
{
private
private
private
private
private
private

Integer itemId;
String itemName;
Integer quantitySold;
String pricePerItem;
Order Order_id;
Manufacturer Manufacturer_id;

public Integer getItemId()


{
return itemId;
}
public void setItemId( Integer id )
{
this.itemId = itemId;
}
public String getItemName()
{
return itemName;
}
public void setItemName( String itemName )
{
this.itemName = itemName;
}
public Integer getQuantitySold()
{
return quantitySold;
}
public void setQuantitySold( Integer quantitySold )
{
this.quantitySold = quantitySold;
}
public String getPricePerItem()
{
return pricePerItem;

2015 Backendless Corp.

Data Service

}
public void setPricePerItem( String pricePerItem )
{
this.pricePerItem = pricePerItem;
}
public Order getOrder_id()
{
return Order_id;
}
public void setOrder_id( Order Order_id )
{
this.Order_id = Order_id;
}
public Manufacturer getManufacturer_id()
{
return Manufacturer_id;
}
public void setManufacturer_id( Manufacturer Manufacturer_id )
{
this.Manufacturer_id = Manufacturer_id;
}
}

public class Order


{
private java.util.Date placed;
private String totalCost;
private Integer id;
public java.util.Date getPlaced()
{
return placed;
}
public void setPlaced( java.util.Date placed )
{
this.placed = placed;
}
public String getTotalCost()
{
return totalCost;
}
public void setTotalCost( String totalCost )
{
this.totalCost = totalCost;
}
public Integer getId()
{
return id;
}

2015 Backendless Corp.

103

104

Backendless API for Android

public void setId( Integer id )


{
this.id = id;
}
}

To delete relation for an order item object, run the following query:
private void deleteARelation()
{
OrderItem item = new OrderItem();
item.setItemId( 1 );
OrderItem foundItem = Backendless.Persistence.of( OrderItem.class ).
findFirst( 2 );
Order order = foundItem.getOrder_id();
foundItem.setOrder_id( null );
Backendless.Persistence.of( OrderItem.class ).save( foundItem );
}

2.18

Relations (Retrieve)
When a data object or a collection of objects is retrieved using the client API, Backendless does not
automatically include related objects into the returned object hierarchy. The reason for this is it would
make it very easy to load a lot of unnecessary data which could impact application's performance. There
are multiple ways to retrieve related data:
Relations auto-load - a mechanism built-into Backendless Console for configuring specific
relations to be included into responses with the parent objects.
Single-step relations retrieval - initializing and including related objects into the response
when running a find query for parent objects.
Two-step relations retrieval - a process of retrieving relations where the first step is to load the
parent object and the second step is to load specific relations for the given parent.
Loading with relations depth - retrieving related objects by specifying the depth of the object
hierarchy
Loading related child objects by condition applied to parent - load related objects using a
search query where the condition applies to the parent object properties.
Please note: retrieving relations in a native database differs from retrieving relations in an external
database.

Relations Auto Load


By default when an object is retrieved from Backendless using any of the find APIs (basic or advanced),
its related objects are not included into the response, unless explicitly referenced in the request. This
behavior can be easily modified using Backendless Console:

2015 Backendless Corp.

Data Service

105

For any two tables A and B where A has a relationship column linking it to B, the console includes the
"auto load" checkbox for the relationship column. Selecting the checkbox instructs Backendless to
return all related B objects when the parent instance of A is retrieved through an API call. For example,
in the image above, the Order table has the one-to-many "items" relationship with the OrderItem table.
When the "auto load" checkbox in the "items" column is selected, all related OrderItem objects will be
included into the response for a find query for the Order table.

Single Step Retrieval


This approach allows to retrieve related objects along with the parent object in a single find request.
Each relationship property (column) must be uniquely identified by name using the following API:
BackendlessDataQuery query = new BackendlessDataQuery();
QueryOptions queryOptions = new QueryOptions();
queryOptions.addRelated( "RELATED-PROPERTY-NAME" );
queryOptions.addRelated( "RELATED-PROPERTY-NAME.RELATION-OFRELATION" );
query.setQueryOptions( queryOptions );
BackendlessCollection<T> collection = Backendless.Data.of( T.class
).find( query );
where
RELATED-PROPERTY-NAME - name of a related property to load. For example, if table

T.class

Person, has a relation "homeAddress" pointing to an object in


the Address table, the value would be "homeAddress". The
syntax allows to add relations of relations. For example, if the
same Address table has a relation "country" pointing to the
Country table, then "homeAddress.country" would instruct the
related Country object to be loaded as well.
- Reference to a class which identifies the table from which the
data is to be loaded.

Two Step Retrieval


With this approach a parent object is loaded first and then there is a separate API call to load specific
related objects into the given parent object. For example, suppose the Order table has a one-to-many
relationship column "items " pointing to the OrderItem table. The OrderItem table has a one-to-one
relationship to the Manufacturer table, the code below loads first Order object and then loads related
OrderItems and corresponding Manufacturers :
Order firstOrder = Backendless.Data.of( Order.class ).findFirst();
ArrayList<String> relationProps = new ArrayList<String>();

2015 Backendless Corp.

106

Backendless API for Android

relationProps.add( "items" );
relationProps.add( "items.manufacturer" );
Backendless.Data.of( Order.class ).loadRelations( firstOrder,
relationProps );
The result of the second loadRelations call is the firstOrder object is populated with the related
OrderItem objects.

Loading with Relations Depth


The Data Service API supports a mechanism for loading related objects without identifying each by its
name. Instead, the API includes a parameter which specifies the "depth" of the relations to include into
the response. Consider the following diagram:

The diagram shows a hierarchy for class structure - the Order class has two relations: with OrderItem
and Customer classes. Each in turn has a relation to the Manufacturer and Address classes. When an
instance or a collection of Order objects is retrieved from Backendless, the API may include a parameter
specifying the depth of relations to include into the response. If the relation depth is 1, then all related
instances of OrderItem and Customer will be included into each Order object. If the relation depth is 2,
then not only OrderItem and Customer instances will be included, but the corresponding Manufacturer
and Address objects as well.

API methods supporting relations depth


Synchronous methods:
public T Backendless.Data.of(
relationsDepth );
public T Backendless.Data.of(
relationsDepth );
public T Backendless.Data.of(
relationsDepth );
public T Backendless.Data.of(
dataQuery );

T.class ).findFirst( int


T.class ).findLast( int
T.class ).findById( int
T.class ).find( BackendlessDataQuery

2015 Backendless Corp.

Data Service

107

Asynchronous methods:
public void Backendless.Data.of( T.class ).findFirst( int
relationsDepth, AsyncCallback<E> responder );
public void Backendless.Data.of( T.class ).findLast( int
relationsDepth, AsyncCallback<E> responder );
public void Backendless.Data.of( T.class ).findById( int
relationsDepth,
AsyncCallback<E> responder );
public void Backendless.Data.of( T.class ).find(
BackendlessDataQuery dataQuery,
AsyncCallback<BackendlessCollection<E>> responder );
Example:
BackendlessDataQuery query = new BackendlessDataQuery();
QueryOptions queryOptions = new QueryOptions();
queryOptions.relationsDepth = 2;
query.setQueryOptions( queryOptions );
BackendlessCollection<Foo> collection = Backendless.Data.of( Foo.
class ).find( query );

Loading a Subset of Related Child Objects


Backendless supports a special query syntax for loading a subset of child objects for a specific parent.
Consider the following class diagram:

Notice the PhoneBook entity references Contact through two properties - "owner" (the one to one
relationship) and "contacts" (the one to many relationship). Each contact entity also references
Address. These entities will be used to demonstrate how Backendless handles related entities in all
basic data persistence operations. Consider the following class definitions for these entities:
PhoneBook Class
package com.sample.entity;
import java.util.List;
public class PhoneBook
{
private String objectId;
private Contact owner;
private List<Contact> contacts;

2015 Backendless Corp.

108

Backendless API for Android

public String getObjectId()


{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public Contact getOwner()
{
return owner;
}
public void setOwner( Contact owner )
{
this.owner = owner;
}
public List<Contact> getContacts()
{
return contacts;
}
public void setContacts( List<Contact> contacts )
{
this.contacts = contacts;
}
}
Contact Class
package com.sample.entity;
import java.util.Date;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
private Address address;
private Date updated;
public String getObjectId()
{
return objectId;
}

2015 Backendless Corp.

Data Service

public void setObjectId( String objectId )


{
this.objectId = objectId;
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge( int age )
{
this.age = age;
}
public String getPhone()
{
return phone;
}
public void setPhone( String phone )
{
this.phone = phone;
}
public String getTitle()
{
return title;
}
public void setTitle( String title )
{
this.title = title;
}
public Address getAddress()
{
return address;
}
public void setAddress( Address address )
{

2015 Backendless Corp.

109

110

Backendless API for Android

this.address = address;
}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}
}
Address Class
package com.sample.entity;
public class Address
{
private String street;
private String city;
private String state;
public String getStreet()
{
return street;
}
public void setStreet( String street )
{
this.street = street;
}
public String getCity()
{
return city;
}
public void setCity( String city )
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState( String state )
{
this.state = state;
}

2015 Backendless Corp.

Data Service

111

}
The general structure of the query to load a collection of child objects for a specific parent object is:
ParentTableName[ relatedPropertyForChildrenCollection ].
parentColumnName COLUMN-VALUE-CONDITION
When a query in this format is used to fetch a collection of child object, the table addresses in the
request must be one which the "relatedPropertyForChildrenCollection" points to. The
examples below demonstrate the usage of this syntax:
Find all contacts in a city for a specific phone book:
// use a sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "address.city = 'Smallville'" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();

Find all contacts for the specific phone book where the city name contains letter 'a':
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "address.city like '%a%'" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();

Find all contacts where age is greater than 20 for a specific phone book:
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "age > 20" );

2015 Backendless Corp.

112

Backendless API for Android

BackendlessDataQuery dataQuery = new BackendlessDataQuery();


dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();

Find all contacts for a specific phone book where age is within the specified range:
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "age >= 21 and age <= 30" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();

Find all contacts for a specific phone book where age is greater than 20 and the city is
Tokyo:
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "age > 20 and address.city = 'Tokyo'" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();

The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have OrderItem class and Order class
public class OrderItem
{
private
private
private
private
private
private

Integer itemId;
String itemName;
Integer quantitySold;
String pricePerItem;
Order Order_id;
Manufacturer Manufacturer_id;

public Integer getItemId()

2015 Backendless Corp.

Data Service

{
return itemId;
}
public void setItemId( Integer id )
{
this.itemId = itemId;
}
public String getItemName()
{
return itemName;
}
public void setItemName( String itemName )
{
this.itemName = itemName;
}
public Integer getQuantitySold()
{
return quantitySold;
}
public void setQuantitySold( Integer quantitySold )
{
this.quantitySold = quantitySold;
}
public String getPricePerItem()
{
return pricePerItem;
}
public void setPricePerItem( String pricePerItem )
{
this.pricePerItem = pricePerItem;
}
public Order getOrder_id()
{
return Order_id;
}
public void setOrder_id( Order Order_id )
{
this.Order_id = Order_id;
}
public Manufacturer getManufacturer_id()
{
return Manufacturer_id;
}
public void setManufacturer_id( Manufacturer Manufacturer_id )
{
this.Manufacturer_id = Manufacturer_id;
}
}

2015 Backendless Corp.

113

114

Backendless API for Android

public class Order


{
private java.util.Date placed;
private String totalCost;
private Integer id;
public java.util.Date getPlaced()
{
return placed;
}
public void setPlaced( java.util.Date placed )
{
this.placed = placed;
}
public String getTotalCost()
{
return totalCost;
}
public void setTotalCost( String totalCost )
{
this.totalCost = totalCost;
}
public Integer getId()
{
return id;
}
public void setId( Integer id )
{
this.id = id;
}
}

To find an order item with relation(s) and update it, run the following query:
private void findAnOrderItemWithRelationsAndUpdateARelation()
{
OrderItem item = new OrderItem();
item.setItemId( 1 );
OrderItem foundItem = Backendless.Persistence.of(OrderItem.class).findFirst
( 2 );
Order order = foundItem.getOrder_id();
order.setTotalCost( order.getTotalCost()+100 );
Backendless.Persistence.of( Order.class ).save( order );
}

2.19

Relations with Geo Points


Backendless Geo Service manages application's geo location data and provides APIs to
work with Geo points. Backendless supports integration between data objects
managed by Data Service and geo points for the scenarios when a logical connection
2015 Backendless Corp.

Data Service

115

between the two entity types must exist in an application. For instance, in a taxi
ordering application a data object may represent a taxi car, while a geo point
represents its location on the map. Link the two together provides great benefits such
as retrieving both objects at once and managing as a consistent, cohesive object
hierarchy.
The Data-to-Geo integration is implemented through object relations. A data table
schema may declare a table column with a special data type - "GeoPoint Relationship".
As a result, the data objects in the table may contain a reference to one or more
GeoPoints. When a data object with a related GeoPoint is saved, Backendless persists
information about both the data object and the geo point in the corresponding
persistent systems and sets up the relationship. Likewise, when a data object is
retrieved by using the API, any related geo points can be retrieved using the same
principle for loading data relations. The data-to-geo relation is bidirectional, it means a
geo point may reference a data object in its metadata. You can learn more about it in
the Relations with Data Objects section of the Geolocation documentation.
The relationship between a data object and a geo point (or a collection of) can be established by using
either the "code first" or the "schema first" approaches. With the former, the relationship is determined
by the data structure persisted with the API. If a data object references a GeoPoint (or a collection of) in
one of its properties, Backendless interprets it as a relation and, as a result, will create a relation
column in the data table schema. With the latter ("schema first") approach, application developer can
declare a relationship in the data table schema first. In either one of these approaches, once a
relationship is declared, data objects and geo points may be linked together by using the Backendless
console as well.

Declaring a Data-to-Geo Relationship in Table Schema


To declare a relationship in a data table schema:
1. Select a table where a relation should be declared.
2. Click the Table Schema and Permissions button in the top right corner. The Table
Schema and Permissions page will display.
3. Click the Add Column button. The pop-up window will display:

2015 Backendless Corp.

116

Backendless API for Android

4. Enter the name of the column in the Name field. This column will represent a data-to-geo
relationship.
5. Select the Geopoint Relationship option from the Type drop-down list.

6. New menu options will become available. Select the cardinality of the relation from the
corresponding drop-down menu. The one-to-one relation means that a table's object can be
linked with only one geo point, while the one-to-many relation means that a table's object can
be linked with multiple geo points.

2015 Backendless Corp.

Data Service

117

7. Click the Save button to save the changes.

Linking a Data Object with Geo Points


Once a data-to-geo relationship is declared (see the instructions above), data objects from the table can
be linked to geo point(s) as described below:
1. Click the name of the table containing an object that you want to link with a geo point.
2. Table columns representing the data-to-geo relationships are identified as "GEOPOINT
relationship" in the header row. The cardinality of the relation is visualized as one red line for
the one-to-one relations and three parallel lines for the one-to-many relations:

3. Click the plus icon next to the object, for which you want to create a data-to-geo relation.
The Set Related GeoPoint pop-up window will display the list of the geo points.

2015 Backendless Corp.

118

Backendless API for Android

4. Use the Geo Category drop-down list to select a geo category from which the points should
be displayed.
5. If you declared a one-to-one relation for a table the object belongs to, you will be able to link
this object with only one geo point (by the means of a radio button). If it is a one-to-many
relationship, the interface uses check boxes, which allow for multiple selection. Click a radiobutton or select check-boxes next to the geo points which you want to link with the data
object.
6. Click the Set Related GeoPoint button to save the changes.
Once a relation is established, it is shown in the data browser as a hyperlink. The hyperlink for the oneto-one relations displays the coordinates of the related geo point. For the one-to-many relations the link
says "multiple Geopoints". In both cases, the link opens the Geolocation screen of the console which
displays the related geo point(s).

Updating Relations
You can update a data-to-geo relation by following the steps shown below:
1. Click the name of the table containing an object that has a data-to-geo relation you want to
edit . Click the plus icon next to the relation:

2015 Backendless Corp.

Data Service

119

2. The Set Related GeoPoint pop-up window will display. Use the radio-buttons (one-to-one
relations) or check-boxes (one-to-many relations) next to select/deselect the geo points.
3. Click the Set Related GeoPoint button to save the changes.

Deleting Relation Definition in Table Schema


A data-to-geo relationship can be removed at the schema level. Removing the relationship is done by
deleting the column which represents it. When a column is removed the relationships identified by the
column between the data objects and geo points are removed as well.
To delete a relationship definition between a data table and the geo points:
1. Click the name of the table which contains a "GeoPoint relationship" column you need to
remove.
2. Click the Table Schema and Permissions button in the top right corner. The Table
Schema and Permissions page will display.
3. Click the check-box next to the column you need to delete.

2015 Backendless Corp.

120

Backendless API for Android

4. Click the Delete Selected button.

Deleting Relation between Data and Geo Point


Objects
To delete relations between a data object and geo point(s) via the Backendless Console:
1. Click the name of the table which contains a data-to-geo relationship you need to remove.
Click the plus icon next to the relation you want to delete.

2. The Set Related GeoPoint pop-up window will display. Click the Unlink Relation button to
delete the relation.

Establishing Relations with Geo Points via API


2015 Backendless Corp.

Data Service

121

Creating a relationship between a data object and a geo point (or a collection of) uses the same API as
saving a data object with a related entity. In the case of data-to-geo relations, the related entity is a geo
point or a collection of geo points. Consider the example that below saves a data object with a related
geo point. The geo point is also persisted in the Geo Service
The example below demonstrates how to link a taxi (a data object) with its location (geo point). First,
create TaxiCab class:
public class TaxiCab
{
public
public
public
public

String carmake;
String carmodel;
GeoPoint location;
List<GeoPoint> previousDropOffs;

public String getCarmake()


{
return carmake;
}
public void setCarmake( String carmake )
{
this.carmake = carmake;
}
public String getCarmodel()
{
return carmodel;
}
public void setCarmodel( String carmodel )
{
this.carmodel = carmodel;
}
public GeoPoint getLocation()
{
return location;
}
public void setLocation( GeoPoint location )
{
this.location = location;
}
public List<GeoPoint> getPreviousDropOffs()
{
return previousDropOffs;
}
public void setPreviousDropOffs( List<GeoPoint> previousDropOffs )
{
this.previousDropOffs = previousDropOffs;
}
}

To set one-to-one or one-to-may relation:


GeoPoint point = new GeoPoint();
point.setLatitude( 40.7148 );

2015 Backendless Corp.

122

Backendless API for Android

point.setLongitude( -74.0059 );
point.addCategory( "taxi" );
point.addMetadata( "service_area", "NYC" );
// one-to-many relation between a data object and geo points
List<GeoPoint> previousDropOffs = new ArrayList<GeoPoint>();
GeoPoint droppOff1 = new GeoPoint( 40.757977, -73.98557 );
droppOff1.addMetadata( "name", "Times Square" );
droppOff1.addCategory( "DropOffs" );
previousDropOffs.add( droppOff1 );
GeoPoint droppOff2 = new GeoPoint( 40.748379, -73.985565 );
droppOff2.addMetadata( "name", "Empire State Building" );
droppOff2.addCategory( "DropOffs" );
previousDropOffs.add( droppOff2 );
TaxiCab taxi = new TaxiCab();
taxi.carmake = "Toyota";
taxi.carmodel = "Prius";
// link one point to data object
taxi.setLocation( point );
// link several points to data object
taxi.setPreviousDropOffs( previousDropOffs );
Backendless.Persistence.save( taxi );

2.20

Security
Data Service supports a very flexible security mechanism for restricting access to objects stored in
Backendless. Security permissions apply to users and roles. A permission can either grant or reject an
operation for a particular asset. In the context of Data Service, the asset is an object which your app can
retrieve, update or delete. Permissions can be granted or rejected globally, where they apply to all tables
and all objects in the data store. Additionally, every table may have its own permission matrix and owner
policy a special instruction whether object owners can or cannot retrieve/update/delete the objects
they own. Finally, every object has its own Access Control List (ACL) which is a matrix of permissions
for the operations applicable specifically to the object:

2015 Backendless Corp.

Data Service

123

The security system is multi-layered. For an API call to retrieve, update or delete object(s), the system
goes through several where each can trim the scope of the operations. The layered order of the decision
making is important and consists of the following:
1.
2.
3.
4.
5.
6.
7.
8.
9.

ObjectACL for the user who makes the call


ObjectACL for user-defined roles assigned to the user who makes the call.
Table permissions for the User account
Table permissions for the user-defined roles
Owner Policy
ObjectACL for system roles
Table permissions for system-level roles
Global user-defined roles
Global system roles

Where:
User-defined roles roles created by the application developer
System roles roles built into Backendless (Authenticated User, NonAuthenticated User,
SocialUser, etc)
Consider the following guide which illustrates the decision making process:
1. Backend receives an API request to load data from a table (the Find operation). All objects
become candidates for the retrieval. Backendless goes through the security permissions chain
to determine which ones must be included.

2015 Backendless Corp.

124

Backendless API for Android

2. ObjectACL for the user who makes the call. Backendless checks if there are any
restrictions for the user account at the object level. Any object in the collection with ACL which
rejects access to the user is excluded from the result. To see or modify the permissions for a
particular object, click the key icon in the ACL column in the data browser in management
console.

2015 Backendless Corp.

Data Service

125

3. ObjectACL for user-defined roles assigned to the user who makes the call. This is the
same check as the one above, except Backendless looks into the permissions for the roles
defined by the application developer. If the user belongs to any of the custom roles,
Backendless checks if these roles are allowed to perform the current operation. In the
screenshot below, only the MyRole role will be checked in this step, since this is the only
custom role in the application:

4. Table permissions for the User account. Every table in Backendless may have its own set of
permissions for users and roles. At this point Backendless checks if the currently logged in
user is allowed to run the current operation. For example, if the Find operation is denied for the
user, no objects would be returned.
5. Table permissions for the user-defined roles. This step is identical to the one described
above with the exception that is checks custom roles for the table. Since this guide reviews the
decision making process for the Find operation, Backendless checks the column for Find. If any
of the custom roles deny access, the operation is rejected and no data is returned.

2015 Backendless Corp.

126

Backendless API for Android

6. Owner Policy. When a new object is created in Backendless, the system automatically links
it with the account of the currently logged in user. You can see that information in the ownerId
column in any of your tables in the data browser. With the association between objects and
users, Backendless provides a way to control whether users can get access to the data they
created. This is done through a concept we call Owner Policy. The policy is available on the
Schema and Permissions screen. Select a table in the data browser and click the Table
Schema and Permissions button in the upper right corner. Select the Owner Policy menu
item. Owner policy can be global (select All Tables from the drop down in the upper right
corner) or it could apply to a specific table.
Granting a permission for an operation in Owner Policy, guarantees that the objects owned by
the current user will be included in the resulting collection. Denying a permission, takes out the
owned objects from the collection of candidate objects to return. Consider the following:
Granting Find permission in Owner Policy:

Results in the following. The objects with bold border are guaranteed to be returned. All other
objects will be subject to the subsequent permission checks.

2015 Backendless Corp.

Data Service

127

However, if the Owner Policy rejects a permission:

The objects owned by the current user will be excluded from the resulting collection. All
remaining objects will be decided by the subsequent permission checks.

7. Object ACL for system roles. This check is identical to step 3 (Object ACL for custom roles).
The difference is the system roles cover larger groups of users. For example, this step would
make possible to restrict access to specific objects for all authenticated (or not authenticated)
2015 Backendless Corp.

128

Backendless API for Android

users, yet the object would be returned with a query made by the objects owner if the Owner
Policy (previous step) grants access.
8. Table permissions for system roles. Identical to step 5, this checks if any of the system
roles reject the operation at the table level.
9. Global custom roles. Global policy applies to all tables and objects. By default all table level
permissions inherit from the global policy. You can configure in the console at: Users >
Security and Permissions. Create a new role and click it to configure the permission matrix:

2.21

Permissions API
Every data object in Backendless has its own access control list (ACL) - a matrix of operations and
principals (application's users or roles). An intersection of an operation and a principal contains a
permission which determines whether the principal has the right to execute the operation. These
permission could be either grant or deny. Backendless console provides an easy to understand way to
see and manage these permissions. For example, the screenshot below demonstrates an ACL matrix
for an object. Notice the intersection of a column for the Update operation and the
NotAuthenticatedUser role. The cell contains a red X icon representing that the permission is denied:

2015 Backendless Corp.

Data Service

129

In addition to managing the ACL permissions with Backendless Console there is also Permissions API:
To grant access for a user
Backendless.Data.Permissions.FIND.grantForUser( userid, dataObject,
responder );
Backendless.Data.Permissions.REMOVE.grantForUser( userid,
dataObject, responder );
Backendless.Data.Permissions.UPDATE.grantForUser( userid,
dataObject, responder );
where:
userid

- a user ID, for which you want to grant a permission.

dataObject

- a data object for which you want to grant the permission.

responder

- a responder object which will receive a callback


when the method successfully saves the object or if
an error occurs. Applies to the asynchronous method
only.

To grant access for a user role


Backendless.Data.Permissions.FIND.grantForRole( rolename,
dataObject, responder );
Backendless.Data.Permissions.REMOVE.grantForRole( rolename,
dataObject, responder );
Backendless.Data.Permissions.UPDATE.grantForRole( rolename,
dataObject, responder );
where:
rolename

- a role name, for which you want to grant a

dataObject

permission.
- a data object for which you want to grant the permission.

responder

- a responder object which will receive a callback


when the method successfully saves the object or if
an error occurs. Applies to the asynchronous method
only.

To grant access for all users


Backendless.Data.Permissions.FIND.grantForAllUsers( dataObject,
responder );
Backendless.Data.Permissions.REMOVE.grantForAllUsers( dataObject,
responder );
Backendless.Data.Permissions.UPDATE.grantForAllUsers( dataObject,

2015 Backendless Corp.

130

Backendless API for Android

responder );
where:
dataObject
responder

- a data object for which you want to grant the permission.

- a responder object which will receive a callback


when the method successfully saves the object or if
an error occurs. Applies to the asynchronous method
only.

To deny access for a user


Backendless.Data.Permissions.FIND.denyForUser( userid, dataObject,
responder );
Backendless.Data.Permissions.REMOVE.denyForUser( userid,
dataObject, responder );
Backendless.Data.Permissions.UPDATE.denyForUser( userid,
dataObject, responder );
where:
userid

- a user ID, for which you want to deny a permission.

dataObject

- a data object for which you want to deny a permission.

responder

- a responder object which will receive a callback


when the method successfully saves the object or if
an error occurs. Applies to the asynchronous method
only.

To deny access for a user role


Backendless.Data.Permissions.FIND.denyForRole( rolename,
dataObject, responder );
Backendless.Data.Permissions.REMOVE.denyForRole( rolename, dataObject,
responder );
Backendless.Data.Permissions.UPDATE.denyForRole( rolename, dataObject,
responder );

where:
rolename

- a role name, for which you want to deny a permission.

dataObject

- a data object for which you want to grant the permission.

responder

- a responder object which will receive a callback


when the method successfully saves the object or if
an error occurs. Applies to the asynchronous method
only.

To deny access for all users


Backendless.Data.Permissions.FIND.denyForAllUsers( dataObject,
responder );
Backendless.Data.Permissions.REMOVE.denyForAllUsers( dataObject,
responder );
Backendless.Data.Permissions.UPDATE.denyForAllUsers( dataObject,
responder );
where:
dataObject
responder

- a data object for which you want to deny the permission.

- a responder object which will receive a callback


when the method successfully saves the object or if
2015 Backendless Corp.

Data Service

131

an error occurs. Applies to the asynchronous method


only.

Messaging Service

3.1

Overview
Data Messaging is an essential function of mobile and desktop applications. It can be used for a
multitude of functions including chat or private messaging, system update broadcast, maintaining game
scores, etc. The Backendless Messaging Service provides API and software infrastructure enabling
publish-subscribe message exchange pattern and mobile push notifications. The service consists of the
following core concepts: channels, publishers, subscribers and registered devices:
channel
- a logical medium "carrying" the messages.
publisher
- a program using the Publishing API to send messages to a channel.
subscriber
- a program using the Subscription API to receive messages from a channel.
registered device
- a mobile device registered with a Backendless channel to receive
push notifications.

Publish-Subscribe Messaging
With the publish-subscribe pattern, one part of the code (or an entire application) can subscribe to
receive messages and another publishes messages. A message can be any data - Backendless
supports messages of primitive or complex data types. To enable publish-subscribe messaging,
Backendless supports the concept of a channel. Subscriptions are "attached" to a channel (or multiple
channels) and messages are published into a channel. By default Backendless sends all messages
published into a channel to all the channel's subscribers. However, a subscription can include message
filters, in this case Backendless delivers only the messages matching the filter.

Push Notifications
A message published to a channel can be tagged as a push notification, thus triggering the logic for
distributing it to the registered devices. Backendless supports push notifications for iOS, Android and
Windows Phone devices. Messages published as push notifications can target either a specific
subscriber (as a device) or a group of subscribers. Subscribers can be grouped by operating system (for
example, a message sent to all registered iOS devices) or as a collection of individual registrations. The
Backendless messaging API supports different types of push notifications - badge updates, alerts, etc.

3.2

Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.

Download SDK
The SDK can be downloaded from the Backendless website.

Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>

2015 Backendless Corp.

132

Backendless API for Android

<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );

Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}

Apps with Google Maps and Geolocation


Android applications using Google Maps, for instance, the Geo service sample app generated by
Backendless code generator, require additional configuration in order for Google maps to be displayed.
The process of configuring an app to support Google maps consists of the following tasks:
1.
Display the debug certificate fingerprint
1. Locate your debug keystore file. The file name is debug.keystore , and is created the first time
you build your project. By default, it is stored in the same directory as your Android Virtual Device
(AVD) files:
OS X and Linux: ~/.android/
Windows Vista and Windows 7: C:\Users\your_user_name\.android\
2. If you are using Eclipse with ADT, and you are not sure where your debug keystore is located, you
can select Windows > Prefs > Android >Build to check the full path, which you can then paste
into a file explorer to locate the directory containing the keystore.
3. List the SHA-1 fingerprint.
For Linux or OS X, open a terminal window and enter the following:
keytool -list -v -keystore ~/.android/debug.keystore -alias
androiddebugkey -storepass android -keypass android
For Windows Vista and Windows 7, run:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.
keystore" -alias androiddebugkey -storepass android -keypass
android
4. You should see output similar to this:
Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US

2015 Backendless Corp.

Messaging Service

133

Issuer: CN=Android Debug, O=Android, C=US


Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01
18:04:04 PST 2033
Certificate fingerprints:
MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:
A1:66:6E:44:5D:75
Signature algorithm name: SHA1withRSA
Version: 3
5. The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the
sequence of 20 two-digit hexadecimal numbers separated by colons.
2.
Create an API project in the Google APIs Console
1. In a browser, navigate to the Google APIs Console.
If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.
4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.

Add the API key to your application


Follow the steps below to include the API key in your application's manifest, contained in the file
AndroidManifest.xml. From there, the Maps API reads the key value and passes it to the Google
Maps server, which then confirms that you have access to Google Maps data.
1. In AndroidManifest.xml, add the following element as a child of the <application> element, by
inserting it just before the closing tag </application> :

2015 Backendless Corp.

134

Backendless API for Android

<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
2. Substitute your API key for API_KEY in the value attribute. This element sets the key com.google.
android.maps.v2.API_KEY to the value of your API key, and makes the API key visible to any
MapFragment in your application.
3. Save AndroidManifest.xml and re-build your application.

Application ID and Secret Key


Values for the application-id and secret-key headers must be obtained through the Backendless
Console:
1. Login to your account and select the application.
2. Click the Manage icon from the vertical icon-menu on the left.
3. The "App Settings" section is selected by default. The interface contains the text fields for
"Application ID" and secret keys for each supported client-side environment.
4. Use the "Copy" button to copy the value into the system clipboard.

Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".

2015 Backendless Corp.

Messaging Service

135

Push Notifications
Android applications must include special configuration into the application manifest file to enable
support for Backendless Push Notifications:
1. Add the following permissions:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE"/>
<permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE" android:
protectionLevel="signature"/>

where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
2. Add receiver declaration into the <application> element in the manifest file
<receiver android:name="com.backendless.push.BackendlessBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="[APP PACKAGE NAME]"/>
</intent-filter>
</receiver>

where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
Receiver is responsible for processing incoming push notifications. Notice the value for the
android:name attribute in the <receiver> element - com.backendless.push.
BackendlessBroadcastReceiver . This is the default receiver, it contains the logic for processing
push notifications and displaying them in the device's Notification Center. The implementation
can be extended in order to get access to the published messages. See the Message
Subscription section for additional details.

3.3

Core Classes
The Backendless Messaging Service uses the following core classes:
Backendless.Messaging - is the central point for all Backendless Messaging APIs. Provides
access to the device registration, subscription management and messaging publishing functionality

SubscriptionOptions - may be used in the subscription call to establish subscriber identity and set messaging filte
package com.backendless.messaging;
public class SubscriptionOptions
{
public SubscriptionOptions()
public SubscriptionOptions( String subscriberId )
public SubscriptionOptions( String subscriberId, String subtopic )
public SubscriptionOptions( String subscriberId, String subtopic, String
selector )
// id uniquely identifying the subscriber in the application
public String getSubscriberId()
public void setSubscriberId( String subscriberId )

2015 Backendless Corp.

136

Backendless API for Android

// subtopics can be used to "multiplex" message distribution over the same


channel
public String getSubtopic()
public void setSubtopic( String subtopic )
// selector is a query in the SQL-92 format referencing message headers.
// if a published message's headers match the query, the message is delivered
to the subscriber
public String getSelector()
public void setSelector( String selector )
}

PublishOptions - this class is the publishing counterpart for the SubscriptionOptions


shown above. Can be used in the publishing API to set the publisher's ID and/or to set properties for
message filtering, such as message headers and subtopic.
package com.backendless.messaging;
public class PublishOptions
{
public PublishOptions()
public PublishOptions( String publisherId )
public PublishOptions( String publisherId, String subtopic )
public PublishOptions( String publisherId, Hashtable<String, String> headers,
String subtopic )
// setter/getter for the id identifying the publisher
public String getPublisherId()
public void setPublisherId( String publisherId )
// methods managing the key/value pairs for the collection of headers
public Map<String, String> getHeaders()
public void setHeaders( Map<String, String> headers )
public void putHeader( String headerKey, String headerValue )
// subtopic - can be used to "multiplex" messages in the same channel
public String getSubtopic()
public void setSubtopic( String subtopic )
}

DeliveryOptions - used in the publishing API for targeted message delivery. Supported options
include: tagging a message as a push notification, scheduling message delivery in the future,
scheduling repeated message delivery and message expiration.
package com.backendless.messaging;
public class DeliveryOptions
{
// accepts a mask value used by Backendless to route the message
// to the registered devices with the specified operating system.
// The mask value may consist of the following values:
// PushBroadcast.IOS, PushBroadcast.ANDROID, PushBroadcast.WP and
PushBroadcast.ALL
public void setPushBroadcast( int pushBroadcast )

2015 Backendless Corp.

Messaging Service

137

// configures a list of registered device IDs to deliver the message to


public void setPushSinglecast( List<String> pushSinglecast )
public void addPushSinglecast( String pushSinglecast )
// sets the time when the message should be published
public void setPublishAt( Date publishAt )
// sets the interval as the number of milliseconds repeated message
publications.
// When a value is set Backendless re-publishes the message with the interval.
public void setRepeatEvery( long repeatEvery )
// sets the time when the message republishing configured with "repeatEvery"
should stop
public void setRepeatExpiresAt( Date repeatExpiresAt )
}

DeviceRegistration - An instance of the class represents a registration of the device with


Backendless. Contains data uniquely identifying the device with Backendless and provider's (Apple,
Google or Microsoft) Push Notification systems.
package com.backendless;
public class DeviceRegistration
{
// registration id assigned by Backendless
public String getId();
// device token identifies the device with Google push notification service
public String getDeviceToken()
// device id - assigned by Backendless SDK
public String getDeviceId();
// operating system of the device
public String getOs();
// version of the device's operating system
public String getOsVersion()
// registration's expiration date/time
public Date getExpiration()
//Backendless channels the device is registered with.
// The array contains a list of channel names as strings.
public List<String> getChannels()
}

Subscription - represents an established subscription between a client and a Backendless channel


package com.backendless;
public class Subscription
{
// returns a unique subscription identifier (assigned by Backendless)
public String getSubscriptionId();

2015 Backendless Corp.

138

Backendless API for Android

// returns the name of the channel the subscription gets messages from
public String getChannelName();
// cancels the subscription
public boolean cancelSubscription();
// suspends the subscription (the client stops receiving new messages)
public void pauseSubscription();
// resumes the subscription
public void resumeSubscription();

3.4

Sync and Async Calls


Most of Backendless API for Android and Java is available in synchronous and asynchronous formats.
The difference in the method signatures is the asynchronous methods accept the AsyncCallback<T>
argument:
package com.backendless.async.callback;
import com.backendless.exceptions.BackendlessFault;
public interface AsyncCallback<T>
{
void handleResponse( T response );
void handleFault( BackendlessFault fault );
}

The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;
import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}

When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.

2015 Backendless Corp.

Messaging Service

3.5

139

Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}

The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )

The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}

The only "special case" from these rules is the Device Registration operation. Any error which may
occur during device registration is delivered to the receiver class configured in the application's manifest.

3.6

Push Notification Setup (Android)


Backendless can deliver published messages as push notifications to Android devices. Additionally,
Backendless Console can be used to publish push notifications. In order to deliver a push notification to
Android, the backend must be configured with Google API Key:
1. Login to Google Developers Console and select or create a project.
2. Click Credentials located under the APIS & AUTH menu.
3. If you dont have Key for server application, you create it:
Click Create new Key under the Public API access section;
Choose Server key;
Click Create;
4. Copy the API Key for server applications:

2015 Backendless Corp.

140

Backendless API for Android

5. Open Backendless Console and select your application.


6. Click Manage and scroll down to Mobile Settings.
7. Paste the Google API Key into corresponding field located under the Android Push
Notifications label:

2015 Backendless Corp.

Messaging Service

141

8. Click Save. At this point the backend is configured and is ready to publish push notifications to
Android devices.
In your project you should register the device in order to receive or send push notifications. To
accomplish this, do the following:
1. Login to Google Developers Console and select your previously created project.
2. Copy the project number located at the top of the screen:

2015 Backendless Corp.

142

Backendless API for Android

3. Use this project number in Backendless.Messaging.registerDevice(...) method as GCMSenderID


argument. For example:

Android applications must also include special configuration into the application manifest file to enable
support for Backendless Push Notifications:
1. Add the following permissions:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE"/>
<permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE" android:
protectionLevel="signature"/>

where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
2. Add receiver declaration into the <application> element in the manifest file
<receiver android:name="com.backendless.push.BackendlessBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>

2015 Backendless Corp.

Messaging Service

143

<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="[APP PACKAGE NAME]"/>
</intent-filter>
</receiver>

where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
Receiver is responsible for processing incoming push notifications. Notice the value for the
android:name attribute in the <receiver> element - com.backendless.push.
BackendlessBroadcastReceiver . This is the default receiver, it contains the logic for processing
push notifications and displaying them in the device's Notification Center. The implementation
can be extended in order to get access to the published messages. See the Message
Subscription section for additional details.

3.7

Push Notification Setup (iOS)


Setting up your backend to support Push Notifications for iOS requires a few steps, most of which are in
Apple Developer Member Center and Keychain Access. The process consists of the following steps:
1.
2.
3.
4.

Creating App ID
Creating Certificate Request
Generating an SSL Certificate
Configuring Backendless App/Backend with the Certificate

Creating App ID
1. First we are going to create an App ID for the mobile application which will receive Push
Notifications. Login to Apple Developer Member Center. Click on App IDs in the Identifiers
section. Use the plus sign + button to create a new ID:

2015 Backendless Corp.

144

Backendless API for Android

2. When prompted enter App ID Prefix. Make sure it is descriptive enough so you recognize it
later when you return to the Member Center.
3. Select Explicit App ID in the App ID Suffix section and enter the same bundle ID which you
will be using in the application:

2015 Backendless Corp.

Messaging Service

145

4. In App Services select the services which the application will use and click continue:

5. Make sure that Push Notifications are enabled and click submit. This will conclude the App ID
creation for the app:
2015 Backendless Corp.

146

Backendless API for Android

Creating Certificate Request


Push Notifications require a certificate which will be used on a device by the means of a provisioning
profile. Also the same certificate (transformed to the Personal Information Exchange .p12 format) will
be used by Backendless to publish Push Notifications. If this makes little sense, do not worry, you will
need to perform these steps only ones and then can move on to code and using the APIs.
1. In order to create a certificate a Certificate Signing Request (CSR) must be issued. To create
a CSR, open Keychain Access and select Keychain Access >> Certificate Assistant >>
Request a Certificate from the main menu:

2015 Backendless Corp.

Messaging Service

147

2. Enter your email address and Common Name (leave the CA Email Address field empty),
select Saved to disk and click Continue:

3. Select a directory where to save the file and click Save.

Generating an SSL Certificate


The CSR file created in the section above will be used to create an SSL Certificate. That certificate will
then be used by Backendless to publish push notifications.
1. Return to Apple Developer Member Center and select All under Certificates. Click the plus
button + to add a new certificate:

2015 Backendless Corp.

148

Backendless API for Android

2. Select certificate type there are two options Development and Production. For now select
Apple Push Notification service SSL (Sandbox):

2015 Backendless Corp.

Messaging Service

149

3. Select the App ID created earlier in these instructions:

4. Next you will see the instructions for generating a CSR which you have already created by
now. Click Continue to proceed to the next step.
5. Select the CSR file created and saved to the disk earlier and click Generate:

2015 Backendless Corp.

150

Backendless API for Android

6. The certificate is ready now, click Download to download it:

7. Add the certificate file to Keychain Access.


8. Open Keychain Access and locate the certificate in the My Certificates section:

2015 Backendless Corp.

Messaging Service

151

9. Right click on the certificate and select the Export option:

10. Save the certificate in the p12 format:

11. Enter a password for the certificate. Make sure to make a record of the password you will
need to use it later in the instructions when you submit the certificate to Backendless:

2015 Backendless Corp.

152

Backendless API for Android

12. Enter your Mac OS X account password to confirm the action. At this point you have a
certificate for Push Notifications.

Configuring Backendless App/Backend with the


Certificate
Since Backendless provides the actual server-side integration for delivering Push Notifications for your
application, it needs to have access to the certificate you created above. The steps below provide the
instructions for uploading the certificate into Backendless:
1. Login to Backendless Console at: https://backendless.com/develop and create/select an
application which you will use on the server-side:

2. Click Manage > App Settings. Locate the Mobile Settings section and upload the .p12
certificate created earlier. Make sure to enter the same password you used when created the
certificate:

2015 Backendless Corp.

Messaging Service

153

3. Now your Backendless server is ready to publish Push Notifications.

3.8

Device Registration
Device registration is required in order for the mobile application running on the device to receive push
notifications from Backendless. Device registration request may contain a list of messaging channels
the device is registering with and an expiration date/time when the registration should be canceled.
Backendless delivers messages marked as push notifications published to the channels to all devices
registered with the channels. If no channels specified, Backendless registers the device with the "default"
channel. If the call references a non-existing channel, Backendless creates the channel and registers the
device with it. Registration expiration is a point in time (expressed as a timestamp) when the device
registration should expire. Backendless removes the device registration at the specified time and device no
longer receives published notifications.
In order to receive push notifications on a mobile device, application running on the device must register

2015 Backendless Corp.

154

Backendless API for Android

with Backendless using the API call below:


Method Signature
public void registerDevice(
public void registerDevice(
String channel )
public void registerDevice(
List<String> channels, Date

Context context, String GCMSenderID )


Context context, String GCMSenderID,
Context context, String GCMSenderID,
expiration )

where:
context
gcmSenderID
channel(s)

expiration

- Android application context.


- Google application project ID. See the Android Developer Guide
for information on how to obtain the GCM Sender ID
- channel (or a collection of channels) to receive messages from.
For the method without the argument, the "Default" channel is
used.
- a timestamp when the device registration should expire.

Errors:
The following errors may occur during the message publishing API call. See the Error Handling
section for details on how to retrieve the error code when the server returns an error:
Error Description
Code
5004

Invalid expiration date. The expiration date must be after the current
time.

8000

Property value exceeds the length limit. Error message should


contain additional details about the violating property.

Example:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
Backendless.Messaging.registerDevice( MainActivity.this, gcmSenderID );

3.9

Retrieve Device Registration


In order to receive push notification application must register the device using the Device Registration
API call. To retrieve device registration or to check if the device is registered with Backendless,
application can use the API described below:
Method Signature
Synchronous Method:
public DeviceRegistration getDeviceRegistration() throws
BackendlessException;
Asynchronous Method:

2015 Backendless Corp.

Messaging Service

155

public void getDeviceRegistration( AsyncCallback<DeviceRegistration


> responder );
Return value:
DeviceRegistration

- the AsyncCallback argument of the asynchronous method


receives an instance of DeviceRegistration in the
handleResponse method.

Errors:
The following errors may occur during the device registration API callThe following errors may
occur during the device registration API call. See the Error Handling section for details on how to
retrieve the error code when the server returns an error:
Error
Code

Description

5000

Unable to retrieve device registration - unknown device ID.

Examples:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
DeviceRegistration devReg = Backendless.Messaging.getDeviceRegistration();

3.10

Managing Registrations
Application developers can manage device registrations using the Backendless Console. To see the
device registrations:
1. Login to console at https://backendless.com/develop
2. Click the application for which you would like to see the device registrations.
3. Click the "Messaging" icon.
4. Select the "Devices" tab.

2015 Backendless Corp.

156

Backendless API for Android

The table displays all the current device registrations. Using the interface, you can:
Search for device registrations. The search string applies to all columns
Remove device registrations. To remove, use the check boxes and then click the "Delete
Selected" button
Deliver push notifications to the selected devices.

Publish a Push Notification from Console


Select the check boxes for the device you would like to send a notification to.
Use the Publish Message section located below the table with the devices.
Make sure to enter the message headers specific for the targeted operating systems.
Make sure to click the "Selected devices" checkbox.
Click the "Publish" button.

3.11

Cancel Device Registration


To cancel the registration of a device with Backendless, an application can use the API described below:
Method Signature
public void unregisterDevice() throws BackendlessException;
Errors:
The following errors may occur during the device registration cancellation API callThe following
errors may occur during the device registration API call. See the Error Handling section for
details on how to retrieve the error code when the server returns an error:
2015 Backendless Corp.

Messaging Service

Error
Code

Description

5001

Unable to cancel device registration - unknown device ID.

157

Example:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
try
{
Backendless.Messaging.unregisterDevice();
}
catch( BackendlessException exception )
{
// either device is not registered or an error occurred during
de-registration
}

3.12

Message Publishing
Application can publish messages to Backendless for subsequent distribution to subscribers.
Backendless delivers published messages to subscribers as message objects and/or to devices as
push notifications. A message must be published to a channel (or a group of channels). Backendless
supports unlimited number of channels. Applications can use them as a filtering mechanism - channel
subscribers see messages published only to that channel. Similarly, devices can specify a channel (or a
group of them) when registering for push notifications. Message publishing supports the following
scenarios:
Publishing with message headers - headers is a collection of name = value pairs of arbitrary
data. Subscribers can set additional filters expressed as SQL queries which Backendless applies
to the headers. When the query matches the published data in headers, message is delivered to
the corresponding subscriber. See example.
Publishing to a subtopic - Subtopics provide an additional level of message filtering.
Multiple subtopics can be defined within a channel. Both publishers and subscribers can
specify a subtopic within a channel. Subtopic names can be defined using a multi-tiered
format:
maintoken[.secondaryToken][.additionalToken]
To receive messages from more than one subtopic, subscribers can use the wildcard
character (*) in place of any tokens in the subtopic name. For instance, a subscriber
could subscribe to the following subtopic: "news..business.* ", and the publisher sends
messages to "news.business.newyork " and "news.business.tokyo ". In this case the
messages published to either subtopic will be delivered to the consumer.

2015 Backendless Corp.

158

Backendless API for Android

The wildcard character in the last position will match any token in that position as well as
tokens after it. For instance, subtopic com.foo.* will match all of the following: com.foo.
bar, com.foo.abc.def , etc. However, the wildcard character in any position other than
the last will match only one token. For example, subtopic com.*.foo will match com.
abc.foo and com.123.foo , but will not match com.foo .
See example.
Publishing a message only/also as a push notification - By default Backendless delivers
published messages only to the "pub/sub subscribers", that is programs subscribed to receive
messages using the Subscription API. However, published messages can also be delivered as
push notifications to the registered devices. The publishing API provides a way to configure the
delivery mode for the following three modes:
API Subscribers (see example)
Only as Push Notifications (see example)
API Subscribers and Push Notifications (same example as above, see the comment in the
example's code)
Publishing a push notification to a group of devices - Backendless can deliver messages
published as a push notifications to devices grouped
by operating system. That is messages can be delivered only to Android devices, iOS or Windows
Phone or a any combination of these. See example.
Publishing a push notification and targeting specific devices - By default Backendless
delivers published messages to all matched subscribers. (Subscribers may be matched by the
topic name or a query). Alternatively, publishers can direct messages to specific subscribers by
specifying the subscriber or device ID in message meta-data. See example.
Delayed publishing - Backendless immediately processes any published messages and
delivers them to subscribers without any delay. However, publishers can specify the time when
the message should be processed. This is applicable to all the publishing options listed above.
Message processing can be canceled at any time using the message cancellation API. See
example.
Scheduled (repeated) publishing - Backendless supports repeated message processing - a
message is published once, but delivered to subscribers with the specified frequency. Repeated
delivery can stop either at the specified time or they can be canceled using the message
cancellation API. For instance, this could be used for reminders or scheduled tasks. See example
.
Method Signatures
Synchronous Methods:
Publishes message to "Default" channel. The message is not a push notification, it does not
have any headers and does not go into any subtopics.
public MessageStatus Backendless.Messaging.publish( Object message
) throws BackendlessException
Same as above, but published into the specified channel.
public MessageStatus Backendless.Messaging.publish( String
channelName,
Object message
) throws BackendlessException

2015 Backendless Corp.

Messaging Service

159

Publishes message to "Default" channel. The message is not a push notification, it may have
headers and/or subtopic defined in the publishOptions argument.
public MessageStatus Backendless.Messaging.publish( Object message,
PublishOptions
publishOptions ) throws BackendlessException
Same as above, but published into the specified channel.
public MessageStatus Backendless.Messaging.publish( String
channelName,
Object message,
PublishOptions publishOptions )
throws BackendlessException
Publishes message to "Default" channel. The message may be configured as a push
notification. It may have headers and/or subtopic defined in the publishOptions argument.
public MessageStatus Backendless.Messaging.publish( Object message,
PublishOptions publishOptions,
DeliveryOptions deliveryOptions )
throws BackendlessException
Same as above, but published into the specified channel.
public MessageStatus Backendless.Messaging.publish( String
channelName,
Object message,
PublishOptions publishOptions,
DeliveryOptions deliveryOptions )
throws BackendlessException
Asynchronous Methods:
The same set of methods is available for the asynchronous invocation. The difference in the
method signatures is the AsyncCallback<MessageStatus> argument:
public void Backendless.Messaging.publish( Object message,
AsyncCallback<
MessageStatus> responder )
public void Backendless.Messaging.publish( String channelName,
Object message,
AsyncCallback<
MessageStatus> responder )
public void Backendless.Messaging.publish( Object message,
PublishOptions
publishOptions,
AsyncCallback<
MessageStatus> responder )

2015 Backendless Corp.

160

Backendless API for Android

public void Backendless.Messaging.publish( String channelName,


Object message,
PublishOptions
publishOptions,
AsyncCallback<
MessageStatus> responder )
public void Backendless.Messaging.publish( Object message,
PublishOptions
publishOptions,
DeliveryOptions
deliveryOptions,
AsyncCallback<
MessageStatus> responder )
public void Backendless.Messaging.publish( String channelName,
Object message,
PublishOptions
publishOptions,
DeliveryOptions
deliveryOptions,
AsyncCallback<
MessageStatus> responder )
where:
channelName
message

publishOptions

deliveryOptions

- name of the channel to publish the message to. If the channel


does not exist, Backendless automatically creates it.
- object to publish. The object can be of any data type - a
primitive value, String, Date, a user-defined complex type, a
collection or an array of these types.
- an instance of PublishOptions . When provided may contain
publisher ID (an arbitrary, application-specific string value
identifying the publisher), subtopic value and/or a collection of
headers.
- an instance of DeliveryOptions . When provides may specify
options for message delivery such as: deliver as a push
notification, deliver to specific devices (or a group of devices
grouped by the operating system), delayed delivery or repeated
delivery.

Return value:
MessageStatus

- a data structure which contains ID of the published message


and the status of the publish operation:

package com.backendless.messaging;
public class MessageStatus implements Comparable<MessageStatus>
{
public PublishStatusEnum getStatus();
public String getMessageId();
@Override
public String toString();

2015 Backendless Corp.

Messaging Service

161

@Override
public int compareTo( MessageStatus messageStatus );
}
Supported message statuses are:
package com.backendless.messaging;
public enum PublishStatusEnum
{
FAILED, PUBLISHED, SCHEDULED, CANCELLED
}
Errors:
The following errors may occur during the message publishing API call. See the Error Handling
section for details on how to retrieve the error code when the server returns an error:
Error
Code

Description

5003

Invalid repeatExpiresAt date in delivery options.

5007

User does not have the permission to publish messages

5030

Invalid publishAt date in the delivery options.

Examples:
Basic message publishing
Publishing with message headers
Publishing to a subtopic
Publishing a message only as a push notification
Publishing a message as a push notification and targeting specific group of devices (grouped by
OS)
Publishing a push notification and targeting specific devices
Delayed publishing
Repeated publishing

Basic message publishing


String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version );
MessageStatus status = Backendless.Messaging.publish( "hello world! );
// message has been published. Message status is available via- response.
getStatus()

Publishing with message headers


String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version);
PublishOptions publishOptions = new PublishOptions();
publishOptions.putHeader( "city", "Tokyo" );
Weather weather = new Weather();

2015 Backendless Corp.

162

Backendless API for Android

weather.setHumidity( 70 );
weather.setTempurature( 80 );
MessageStatus status = Backendless.Messaging.publish( weather, publishOptions
);

Publishing to a subtopic
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version);
PublishOptions publishOptions = new PublishOptions();
publishOptions.setSubtopic( "news.business.newyork" );
MessageStatus status = Backendless.Messaging.publish( "get free coffee at
Moonbucks today", publishOptions );

Publishing a message only as a push notification


String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version );
DeliveryOptions deliveryOptions = new DeliveryOptions();
deliveryOptions.setPushPolicy( PushPolicyEnum.ONLY );
deliveryOptions.setPushBroadcast( PushBroadcastMask.ALL );
MessageStatus status = Backendless.Messaging.publish( "Hi Devices!", null,
deliveryOptions );

Publishing a message as a push notification and targeting


specific group of devices
The only difference between this example and the one above is the value of the pushBroadcast
property in DeliveryOptions :
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version );
DeliveryOptions deliveryOptions = new DeliveryOptions();
deliveryOptions.setPushPolicy( PushPolicyEnum.ONLY );
deliveryOptions.setPushBroadcast( PushBroadcastMask.ANDROID );
PublishOptions publishOptions = new PublishOptions();
publishOptions.putHeader( "android-ticker-text", "You just got a push
notification!" );
publishOptions.putHeader( "android-content-title", "This is a notification
title" );
publishOptions.putHeader( "android-content-text", "Push Notifications are
cool" );
MessageStatus status = Backendless.Messaging.publish( "Hi Devices!",
publishOptions, deliveryOptions );

2015 Backendless Corp.

Messaging Service

163

Publishing a push notification and targeting specific


devices
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version );
DeliveryOptions deliveryOptions = new DeliveryOptions();
deliveryOptions.setPushPolicy( PushPolicyEnum.ONLY );
deliveryOptions.addPushSinglecast( receiver-device-id );
PublishOptions publishOptions = new PublishOptions();
publishOptions.putHeader( "android-ticker-text", "You just got a private push
notification!" );
publishOptions.putHeader( "android-content-title", "This is a notification
title" );
publishOptions.putHeader( "android-content-text", "Push Notifications are
cool" );
MessageStatus status = Backendless.Messaging.publish( "this is a private
message!", publishOptions, deliveryOptions );

Delayed publishing
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
DeliveryOptions deliveryOptions = new DeliveryOptions();
Date publishDate = new Date( System.currentTimeMillis() + 20000 ); // add 20
seconds
deliveryOptions.setPublishAt( publishDate );
MessageStatus status = Backendless.Messaging.publish( "This message was
scheduled 20 sec ago", null, deliveryOptions );

Repeated publishing
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version );
DeliveryOptions deliveryOptions = new DeliveryOptions();
deliveryOptions.setRepeatEvery( 20 ); // the message will be delivered every
20 seconds
deliveryOptions.setRepeatExpiresAt( new Date( System.currentTimeMillis() +
60000 ) );
MessageStatus status = Backendless.Messaging.publish( "This message was
scheduled 20 sec ago", null, deliveryOptions );

2015 Backendless Corp.

164
3.13

Backendless API for Android

Publish Push Notifications


Publishing a push notification is a specialized usage of the Message Publishing API. Push notifications
have different graphical representation on different mobile operating systems. For instance, a push
notification on an iOS device may be either an alert or a badge update, however a notification on a
Windows Phone device may be either a toaster alert or a tile element update. Backendless supports
different formats of the push notification delivery to various operating systems via specialized message
headers. These headers must be added to the publish options object or in case of REST clients, they
are plain message headers:
Operating Headers
System

Description

iOS

"ios-alert":value

Sets the text of


the alert
message. If the
header is not
present and the
published
notification
targets the iOS
devices,
Backendless
sets the header
to the value of
the "message"
argument. To
disable this
behavior, set
the ios-alert
header to null .

"ios-badge":value

Sets the value


to update the
badge with

"ios-sound":URL string or array of bytes Sets either a

URL for the


sound
notification to
play on the
device or an
array of bytes
for the sound to
play.
Android

"android-ticker-text":value

Sets the text of


the ticker
showing up at
the top of a
device's screen
when the device
receives the
notification.

"android-content-title":value

Sets the title of


the notification
2015 Backendless Corp.

Messaging Service

165

as it is visible in
the Android
Notification
Center
"android-content-text":value

Sets the
message of the
notification
which appears
under androidcontent-title

in the Android
Notification
Center.
Windows
Phone

"wp-title":value,
"wp-content":value

Sets the title


and the content
of a toast
notification.

"wp-type":"TILE":
"wp-title" : value,
"wp-backgroundImage" : URL string,
"wp-badge" : number value,
"wp-backTitle" : value,
"wp-backImage" : URL string,
"wp-backContent" : value

Sets the
properties for a
tile notification.

"wp-type":"RAW",
"wp-raw":XMLString

Sets the
properties for a
raw notification

Push notifications can be published directly from Backendless Console or using the API (see the
examples in the Message Publishing section).

3.14

Cancel Scheduled Message


Delayed or scheduled messages can be canceled using the API documented below. Backendless
processes delayed messages at the time specified by the publisher. Scheduled messages are
processed and delivered with a specified interval.
Method Signatures
Synchronous Method:
public boolean Backendless.Messaging.cancel( String messageId )
throws BackendlessException;
Asynchronous Method:
public void Backendless.Messaging.cancel( String messageId,
AsyncCallback<MessageStatus> responder );
where:
messageId

responder

2015 Backendless Corp.

- ID of the message to cancel. Message ID must be obtained


from the MessageStatus object obtained as the result of the
Publish methods.
- contains the ID of a message and its status. If the cancellation
is successful, the getStatus() method on the MessageStatus
object returns the value of com.backendless.messaging.

166

Backendless API for Android

PublishStatusEnum.CANCELED ).

Return value:
boolean

- true if the scheduled message has been successfully


canceled, false otherwise.

Errors:
The following errors may occur during the message cancellation API call. See the Error
Handling section for details on how to retrieve the error code when the server returns an error:
Error
Code

Description

5040

Message has already been canceled or does not exist.

Examples:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
DeliveryOptions deliveryOptions = new DeliveryOptions();
Date publishDate = new Date( System.currentTimeMillis() + 20000 ); // add 20
seconds
deliveryOptions.setPublishAt( PushPolicy.ONLY );
MessageStatus status = Backendless.Messaging.publish( "Test Message", null,
deliveryOptions );
boolean result = Backendless.Messaging.cancel( status.getMessageId() );

3.15

Message Subscription
In order to receive published messages, application must subscribe to a channel using the API below.
Using the API, an application becomes an "API subscriber". Another form of subscription can be
accomplished by using the Device Registration API which provides a way to receive push notifications.
Note that the same mobile application can use both device registration and message subscription APIs.

Synchronous Methods
Subscribing to "Default" channel:
public Subscription Backendless.Messaging.subscribe(
AsyncCallback<List<Message>> subscriptionResponder ) throws
BackendlessException

public Subscription Backendless.Messaging.subscribe( int


pollingInterval,
AsyncCallback<List<Message>>
subscriptionResponder ) throws BackendlessException

2015 Backendless Corp.

Messaging Service

167

public Subscription Backendless.Messaging.subscribe(


AsyncCallback<List<Message>> subscriptionResponder,
SubscriptionOptions
subscriptionOptions ) throws BackendlessException
Subscribing to a specific channel:
public Subscription Backendless.Messaging.subscribe( String
channelName,
AsyncCallback<List<Message>>
subscriptionResponder ) throws BackendlessException

public Subscription Backendless.Messaging.subscribe( String


channelName,
int pollingInterval,
AsyncCallback<List<Message>>
subscriptionResponder ) throws BackendlessException

public Subscription Backendless.Messaging.subscribe( String


channelName,
AsyncCallback<List<Message>>
subscriptionResponder,
SubscriptionOptions
subscriptionOptions ) throws BackendlessException

public Subscription Backendless.Messaging.subscribe( String


channelName,
AsyncCallback<List<Message>>
subscriptionResponder,
SubscriptionOptions
subscriptionOptions,
int pollingInterval ) throws
BackendlessException
where:
channelName
- name of the channel to subscribe to.
subscriptionResponder - responder object where Backendless delivers messages for the
subscriptionOptions

pollingInterval

subscriber.
- contains settings for filtered message delivery. Available options
include subtopic and selector. See the Message Filtering section
below.
- the interval in milliseconds between subsequent polling
requests. Each polling request checks for messages for the
subscription.

Return value:
Subscription

Asynchronous Methods:

2015 Backendless Corp.

- an object representing the subscription. A Subscription object


provides access to a unique identifier which can be used for
subscription cancellation.

168

Backendless API for Android

All asynchronous methods have exactly the same signatures as the synchronous counterparts
except for the AsyncCallback argument which receives a notification about the successful or
failed completion of the method:
Subscribing to "Default" channel:
public void Backendless.Messaging.subscribe(
AsyncCallback<List<Message>> subscriptionResponder,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException

public void Backendless.Messaging.subscribe( int pollingInterval,


AsyncCallback<List<Message>>
subscriptionResponder,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException

public void Backendless.Messaging.subscribe(


AsyncCallback<List<Message>> subscriptionResponder,
SubscriptionOptions subscriptionOptions,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException
Subscribing to a specific channel:
public void Backendless.Messaging.subscribe( String channelName,
AsyncCallback<List<Message>>
subscriptionResponder,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException

public void Backendless.Messaging.subscribe( String channelName,


int pollingInterval,
AsyncCallback<List<Message>>
subscriptionResponder,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException

public void Backendless.Messaging.subscribe( String channelName,


AsyncCallback<List<Message>>
subscriptionResponder,
SubscriptionOptions subscriptionOptions,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException

public void Backendless.Messaging.subscribe( String channelName,


AsyncCallback<List<Message>>
subscriptionResponder,
SubscriptionOptions subscriptionOptions,
int pollingInterval,

2015 Backendless Corp.

Messaging Service

169

AsyncCallback<Subscription> methodCallback )
throws BackendlessException
where:
channelName
- name of the channel to subscribe to.
subscriptionResponder - responder object where Backendless delivers messages for the
subscriptionOptions

pollingInterval

methodCallback

subscriber.
- contains settings for filtered message delivery. Available options
include subtopic and selector. See the Message Filtering section
below.
- the interval in milliseconds between subsequent polling
requests. Each polling request checks for messages for the
subscription
- a responder object notified of the successful or failed
completion of the subscription operation. An invocation the
handleResponse callback method represents a successful
message subscription. The argument of the callback is an
instance of the Subscription class - an object representing the
established subscription. A Subscription object provides access
to a unique identifier which can be used for subscription
cancellation.

Errors:
The following errors may occur during the message cancellation API call. See the Error
Handling section for details on how to retrieve the error code when the server returns an error:
Error
Code

Description

5008

User does not have permission to create a subscription.

5009

General subscription error. See error message for additional


details.

5010

Unknown messaging channel.

Messages
The Java/Android client retrieves messages through an implementation of the AsyncCallback interface.
All subscribe methods described above receive the following argument:
AsyncCallback<List<Message>> subscriptionResponder . Backendless delivers new messages
published to the subscribed channel through the receivedResponse method of the
subscriptionResponder object:
new AsyncCallback<List<Message>>()
{
public void handleResponse( List<Message> response )
{
for( Message message : response )
{
String publisherId = message.getPublisherId();
Object data = message.getData();
}
}
public void handleFault( BackendlessFault fault )
{
// handle error here
}

2015 Backendless Corp.

170

Backendless API for Android

}
The response argument in the handleResponse method is a collection of the com.backendless.
messaging.Message objects:
package com.backendless.messaging;
public class Message
{
public String getMessageId();
public Map<String, String> getHeaders();
public Object getData();
public String getPublisherId();
public long getTimestamp();
public String toString();
}
where:
messageId
headers

data
publisherID
timestamp

- unique message ID. The ID is assigned at the time of message


publishing.
- an associative array which is a collection of key/value pairs.
Includes all the headers included with the message publishing.
Additionally, Backendless adds the following headers:
BL_APPLICATION_ID - contains the ID of the application and
BL_VERSION_URL_PREFIX - contains the name of the version of
the application.
- message payload. It is the object sent by a publisher.
- the property contains sender (publisher) ID if it is provided by
the publisher.
- a timestamp indicating when the message was received by
Backendless from the publisher.

Examples:
Backendless.initApp( activityContext, appId, secretKey, versionName ); //
where to get then argument values
Backendless.Messaging.subscribe( channelName,
new AsyncCallback<List<Message>>()
{
public void handleResponse( List<Message> response )
{
for( Message message : response )
{
String publisherId = message.getPublisherId();
Object data = message.getData();
}
}
public void handleFault( BackendlessFault fault )
{
Toast.makeText( ChatActivity.this, fault.getMessage(), Toast.
LENGTH_SHORT ).show();
}
}
},
new AsyncCallback<Subscription>()
{

2015 Backendless Corp.

Messaging Service

171

public void handleResponse( Subscription response )


{
subscription = response;
}
public void handleFault( BackendlessFault fault )
{
Toast.makeText( ChatActivity.this, fault.getMessage(), Toast.
LENGTH_SHORT ).show();
}
}
);

Message Filtering
Backendless message filtering is a powerful mechanism enabling conditional message delivery, interestbased subscriptions and private messaging. A subscription request may include filters in the form of
subtopics and selectors. Backendless applies subscriber's filters to every message published into the
channel and they match, the message is delivered to the subscriber.
Subtopics
Multiple subtopics can be defined within a channel. Both publishers and subscribers can specify
a subtopic within a channel. Subtopic names can be defined using a multi-tiered format:
maintoken[.secondaryToken][.additionalToken]
To receive messages from more than one subtopic, subscribers can use the wildcard character
(*) in place of any tokens in the subtopic name. For instance, a subscriber could subscribe to
the following subtopic: "news..business.* ", and the publisher sends messages to "news.
business.newyork " and " news.business.tokyo ". In this case the messages published to
either subtopic will be delivered to the consumer.
The wildcard character in the last position will match any token in that position as well as
tokens after it. For instance, subtopic com.foo.* will match all of the following: com.foo.bar,
com.foo.abc.def , etc. However, the wildcard character in any position other than the last will
match only one token. For example, subtopic com.*.foo will match com.abc.foo and
com.123.foo , but will not match com.foo .
Selectors
A selector is a query expressed using the SQL-92 syntax and formatted as the condition part of
the SQL's WHERE clause. A query condition must reference the headers of the published
messages. When a message is published and a subscriber has a selector query, Backendless
executes the query on the headers of the published message. If the result of the query is true,
the message is delivered to the subscriber. Consider the following example where the subscriber
will receive only messages containing the "city " header with the value of "Tokyo ":
Publisher:
String applicationID = "set the app id here";
String secretKey = "set your secret key kere";
String version = "set your version name here";
String gcmSenderID = "set your gcmSenderID from Google API portal";
Backendless.initApp( applicationId, secretKey, version);
PublishOptions publishOptions = new PublishOptions();

2015 Backendless Corp.

172

Backendless API for Android

publishOptions.putHeader( "city", "Tokyo" );


Weather weather = new Weather();
weather.setHumidity( 70 );
weather.setTempurature( 80 );
MessageStatus status = Backendless.Messaging.publish( weather,
publishOptions );
Subscriber:
Backendless.initApp( activityContext, appId, secretKey, versionName
);
AsyncCallback<List<Message>> subscriptionResponder = new
AsyncCallback<List<Message>>()
{
public void handleResponse( List<Message> response )
{
}
public void handleFault( BackendlessFault fault )
{
}
};
SubscriptionOptions subscriptionOptions = new SubscriptionOptions
();
subscriptionOptions.setSelector( "city='Tokyo'" );
Backendless.Messaging.subscribe( subscriptionResponder,
subscriptionOptions );

3.16

Cancel Subscription
In order to stop a client from polling for messages, it must issue subscription cancellation request using
the API method described below:
Method Signatures:
subscriptionObject.cancelSubscription() throws
BackendlessException;
where:
subscriptionObject

- an instance of the Subscription class. The object is returned


as a result of the Message Subscription call.

Return value:
boolean

- true if the scheduled message has been successfully canceled,


False otherwise.

Examples:

2015 Backendless Corp.

Messaging Service

173

Backendless.initApp( activityContext, appId, secretKey, versionName


); // where to get then argument values
Backendless.Messaging.subscribe( channelName, new
AsyncCallback<List<Message>>()
{
public void handleResponse( List<Message> response )
{
}
public void handleFault( BackendlessFault fault )
{
}
},
new AsyncCallback<Subscription>()
{
public void handleResponse( Subscription response )
{
Subscription subscription = response;
subscription.cancelSubscription();
}
public void handleFault( BackendlessFault fault )
{
}
}
);

3.17

Sending Email
Backendless provides API for email delivery on behalf of your application. Before the API can be used,
the Backendless backend must be configured with your own SMTP server information. This is an
important requirement as the API will not work if the Manage > App Settings > Email Settings section
in Backendless Console contains default values.

Configuration
To configure a backend:
1.
2.
3.
4.

Login to Backendless Console.


Select an app.
Click Manage, then scroll down to Email Settings on the App Settings screen.
Fill out the form with the SMTP server information

2015 Backendless Corp.

174

Backendless API for Android

where:
SMTP Server
Port
From
User ID
Password
Security

- Hostname or public IP address of the server where the SMTP server is


running.
- The port number the SMTP server accepts requests on.
- The Name which will appear in the From field of the sent out emails.
- The user id or email address for the SMTP server connection
authentication
- The password for the SMTP server connection authentication.
- Choose between SSL or TLS connection.

Make sure to click Test before saving any configuration changes. The Discard button discards any
unsaved changes.

Sending Email API


Delivers an email message using current server-side email settings to the recipient specified in the API
call.

where:
subject
messageBody
bodyParts

- email message subject.


- plain text or HTML body of the email message.
- an instance of com.backendless.messaging.BodyParts class

2015 Backendless Corp.

Messaging Service

recipient
recipients
attachments

responder

175

which contains either plain text and/or HTML version of the


message body.
- email address to deliver the email message to.
- a collection of email addressed to deliver the email message to.
- an array of file paths for the file entries from the Backendless
File Service. Referenced files will be attached to the email
message. The path is calculated from the root of the file system
(as it is seen in File Service browser in Backendless console)
without the leading slash. For example, if file agreement.txt is
located at /documents/legal/, then the path in the API call must
be "documents/legal/agreement.txt".
- the callback used for asynchronous calls to indicate that the
operation has either successfully completed or resulted in error.

Example:

File Service

4.1

Overview
Every Backendless backend/app is allocated a dedicated file storage space. The file storage is located
remotely on the Backendless servers. The file storage can be used to store application's files and ondemand video streams. Backendless File Service provides the API to work with the file storage. The API
supports the following operations:
File Upload - upload files to the applications's file storage. The operation creates directories up
the hierarchy if necessary. Returns file URL which can be used to download or share the file
with others.
File Download - download file using file's URL. The download operation is subject to the
permissions from the File access control list (ACL).
File Deletion - delete a file from the file storage. The delete operation is subject to the
permissions from the File access control list (ACL).
Directory Deletion - same as file deletion, but applies to the directories.
File/Directory Security (File ACL) - assign/unassign user and roles permissions to upload,
download and delete files and directories. This API is used to modify file or directory ACL.
In addition to the API implementation, the File Service enables the following capabilities:
Git Integration - application developers can interact with the file storage as with a git repository.
Web Hosting - file storage can be used to host static web content.
Custom Domain Name - a custom domain name can be mapped to the file storage in a
Backendless backend. This feature in combination with the Web Hosting provides a way to host
websites on Backendless.
Custom Web Templates Hosting - includes HTML files and JS scripts for special pages used in
various workflows such as user email confirmation, password change and session expiration.

4.2

Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.

2015 Backendless Corp.

176

Backendless API for Android

Download SDK
The SDK can be downloaded from the Backendless website.

Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );

Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}

Apps with Google Maps and Geolocation


Android applications using Google Maps, for instance, the Geo service sample app generated by
Backendless code generator, require additional configuration in order for Google maps to be displayed.
The process of configuring an app to support Google maps consists of the following tasks:
1.
Display the debug certificate fingerprint
1. Locate your debug keystore file. The file name is debug.keystore , and is created the first time
you build your project. By default, it is stored in the same directory as your Android Virtual Device
(AVD) files:
OS X and Linux: ~/.android/
Windows Vista and Windows 7: C:\Users\your_user_name\.android\
2. If you are using Eclipse with ADT, and you are not sure where your debug keystore is located, you
can select Windows > Prefs > Android >Build to check the full path, which you can then paste
into a file explorer to locate the directory containing the keystore.
3. List the SHA-1 fingerprint.
For Linux or OS X, open a terminal window and enter the following:
keytool -list -v -keystore ~/.android/debug.keystore -alias
androiddebugkey -storepass android -keypass android
For Windows Vista and Windows 7, run:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.
keystore" -alias androiddebugkey -storepass android -keypass
android

2015 Backendless Corp.

File Service

177

4. You should see output similar to this:


Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01
18:04:04 PST 2033
Certificate fingerprints:
MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:
A1:66:6E:44:5D:75
Signature algorithm name: SHA1withRSA
Version: 3
5. The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the
sequence of 20 two-digit hexadecimal numbers separated by colons.
2.
Create an API project in the Google APIs Console
1. In a browser, navigate to the Google APIs Console.
If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.
4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:

2015 Backendless Corp.

178

Backendless API for Android

AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.

Add the API key to your application


Follow the steps below to include the API key in your application's manifest, contained in the file
AndroidManifest.xml. From there, the Maps API reads the key value and passes it to the Google
Maps server, which then confirms that you have access to Google Maps data.
1. In AndroidManifest.xml, add the following element as a child of the <application> element, by
inserting it just before the closing tag </application> :
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
2. Substitute your API key for API_KEY in the value attribute. This element sets the key com.google.
android.maps.v2.API_KEY to the value of your API key, and makes the API key visible to any
MapFragment in your application.
3. Save AndroidManifest.xml and re-build your application.

Application ID and Secret Key


Values for the application-id and secret-key headers must be obtained through the Backendless
Console:
1. Login to your account and select the application.
2. Click the Manage icon from the vertical icon-menu on the left.
3. The "App Settings" section is selected by default. The interface contains the text fields for
"Application ID" and secret keys for each supported client-side environment.
4. Use the "Copy" button to copy the value into the system clipboard.

2015 Backendless Corp.

File Service

179

Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".

4.3

Sync and Async Calls


Most of Backendless API for Android and Java is available in synchronous and asynchronous formats.
The difference in the method signatures is the asynchronous methods accept the AsyncCallback<T>
argument:
package com.backendless.async.callback;
import com.backendless.exceptions.BackendlessFault;
public interface AsyncCallback<T>
{
void handleResponse( T response );
void handleFault( BackendlessFault fault );
}

The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;

2015 Backendless Corp.

180

Backendless API for Android

import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}

When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.

4.4

Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}

The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )

The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}

4.5

Handling Files via Console


Backendless Console includes a graphical file browser which supports the following operations:
Creating new file

2015 Backendless Corp.

File Service

181

Editing a file
Getting public URL for a file
Creating file archives

Creating a New File


You can create a file in Backendless Console with the following file extensions:
.conf, ,css, .csv, .htm, .html, .ini, .java, .js, .log, .php, .
properties, .py, .rb, .sh, .txt, .xml, .xsd
To create a file:
1. Log in to Backendless Console. Select an application. Click the Files icon:

2. Select a directory where a new file should created. Click the New File button at the top of
the file listing table.
3. Enter the name in the File name field and select a file extension from the Syntax
highlighter drop-down menu:

2015 Backendless Corp.

182

Backendless API for Android

4. Enter the contents for the file as necessary. Click the Save button.

Editing a File
Backendless supports in-browser editing of the files with the following extensions:
.conf, ,css, .csv, .htm, .html, .ini, .java, .js, .log, .php, .
properties, .py, .rb, .sh, .txt, .xml, .xsd
To edit a file:
1. Select a directory containing the file on the Files screen of the console.
2. Click the Edit file icon next to the file to open it for editing:

3. Once the changes in the file are made click the Save button.

2015 Backendless Corp.

File Service

183

Getting Public URL for a File


A file in Backendless File Storage has two URLs:
a public URL which can be used to download the file outside of Backendless console. This URL
accounts for any permissions assigned to the file or the directory where it resides.
a private URL which makes the file accessible by the developer of the application.
In order to obtain the public URL:
1. Select a directory containing a file on the Files screen of Backendless console.
2. Click the Get Public URL icon next to the file. Backendless console copies the file's public
URL to the computer's clipboard:

Archiving Files
Backendless Console includes a feature enabling to compress directories into a single ZIP file. The
feature applies specifically to directories, meaning an individual file cannot be compressed - it must be
placed into a directory first.
Notice: archiving of directories with total content size greater than 100 Mb may take longer time;
Backendless sends an email to the application developer upon successful completion of the operation.
To archive a directory:
1. Log in to Backendless Console. Select an application and click the Files icon.
2. Navigate to a directory which should be compressed.
3. Click the ZIP Directory button:

2015 Backendless Corp.

184

Backendless API for Android

4. Once the directory is compressed into an archive, it will appear in the parent directory:

4.6

File Upload
The file upload operation delivers and saves a local file in the remote Backendless file storage. The return
value of the operation is the file URL which has the following structure:
https://api.backendless.com/<application id>/<version name>/files/
<path>/<file name>
where:
<application id>
<version name>

- ID of the application which can be obtained from the Manage >


App Settings screen of the Backendless Console
- application's version name
2015 Backendless Corp.

File Service

<path>
<file name>

185

- directory path where the file is saved


- name of the file

The URL assigned to a file and returned as a result of the upload operation accounts for any security
permissions assigned to the file (or the folder it is located in).
Backendless Console includes a file browser with the management functions to upload files, create or
delete directories and files. The file browser is available in the Files section of the console:

File browser also provides a way to see the contents of the files. Every file is a link which opens the file.
The URL of files in file browser is not the same as the URL returned by the file upload operation. The
reason file browser uses a different URL is to let the application developer see the file contents without
any application security constraints. The only constraint applied to the URLs available in file browser is
the application developer must be logged to the console.
Methods:
Java and Android
Synchronous Methods:
Upload a file to a remote path in the Backendless storage.
public BackendlessFile Backendless.Files.upload( File file, String remotePath
) throws Exception

Upload a file to a remote path and receive updates for the status of the upload. Updates are
represented as percentages and are delivered via the uploadCallback argument.
public BackendlessFile Backendless.Files.upload( File file,
String remotePath,
UploadCallback
uploadCallback ) throws Exception

Create a file in the remote Backendless file storage from the bytes provided by OutputStream .
public BackendlessFile Backendless.Files.uploadFromStream(
IOutputStreamRouter outputStreamRouter,
String remoteName,
String remotePath
) throws Exception

Asynchronous Methods:
2015 Backendless Corp.

186

Backendless API for Android

Upload a file to a remote path in the Backendless storage.


public void Backendless.Files.upload( File file,
String remotePath,
AsyncCallback<BackendlessFile>
responder )

Upload a file to a remote path and receive updates for the status of the upload. Updates are
represented as percentages and are delivered via the uploadCallback argument.
public void Backendless.Files.upload( File file,
String remotePath,
UploadCallback uploadCallback,
AsyncCallback<BackendlessFile>
responder )

where:
file
remotePath
uploadCallback
outputStreamRouter
remoteName
responder

- local file to upload to Backendless file storage. Remote file


name is the same as the name of the local file.
- path (without file name) in the remote Backendless file storage
where the file should be saved.
- receives updates through the upload process. Updates
expressed as percentage of file uploaded to the server.
- a wrapper around a java.io.ObjectStream object which
provides the bytes for the upload.
- name of the file to save in the remote Backendless file storage.
- a responder object which receives a callback when the method
successfully uploads the file or if an error occurs. Applies to the
asynchronous methods only.

Android Only
Synchronous Method:
Upload a bitmap image and save as a file in the remote Backendless storage.
public BackendlessFile Backendless.Files.upload( android.graphics.Bitmap
bitmap,
android.graphics.Bitmap.
CompressFormat compressFormat,
int quality,
String remoteName,
String remotePath ) throws
Exception

Asynchronous Method:
Same as the method above, but the upload is handled asynchronously.
void
public void Backendless.Files.upload( android.graphics.Bitmap bitmap,
android.graphics.Bitmap.CompressFormat
compressFormat,
int quality,
String remoteName,
String remotePath,
AsyncCallback<BackendlessFile>
responder ) throws Exception

where:
bitmap

- bitmap to upload to the remote Backendless storage and save


as a file.

2015 Backendless Corp.

File Service

compressFormat
quality
remoteName
remotePath
responder

187

- compress format to use when compressing the bitmap. The


compression is done by the Bitmap.compress method.
- quality of the compression. The value is passed to the Bitmap.
compress method, which handles the compression.
- name of the file to save in the remote Backendless file storage.
- path in the remote Backendless file storage where the file
should be saved.
- a responder object which receives a callback when the method
successfully uploads the file or if an error occurs. Applies to the
asynchronous methods only.

Return Value (sync methods only):


BackendlessFile
- a wrapper around the URL assigned to the uploaded file.
Additional API Classes:
BackendlessFile
package com.backendless.files;
import com.backendless.Backendless;
import com.backendless.async.callback.AsyncCallback;
import com.backendless.exceptions.BackendlessException;
public class BackendlessFile
{
private String fileURL;
// returns URL of the file once it is uploaded to Backendless
public String getFileURL()
{
return fileURL;
}
// removes file from the remote storage (sync call)
public void remove() throws BackendlessException
{
Backendless.Files.remove( fileURL );
}
// removes file from the remote storage (async call)
public void remove( AsyncCallback<Void> responder )
{
Backendless.Files.remove( fileURL, responder );
}
}
UploadCallback
package com.backendless.async.callback;
public interface UploadCallback
{
// method is invoked as a file is being uploaded to report the progress
expressed as percentage between 1 and 100
public void onProgressUpdate( Integer progress );
}
IOutputStreamRouter

2015 Backendless Corp.

188

Backendless API for Android

package com.backendless.files.router;
import java.io.IOException;
import java.io.OutputStream;
public abstract class IOutputStreamRouter
{
private OutputStream outputStream;
// returns the output stream, to which the bytes must be written to
public OutputStream getOutputStream()
{
return outputStream;
}
// Sets the output stream, to which the bytes must be written to in
// order to upload contents to the remote Backendless storage.
// This method is called internally by Backendless client library.
public void setOutputStream( OutputStream outputStream )
{
this.outputStream = outputStream;
}
// An implementation of this method must write data to the stream
returned
// by the getOutputStream() method. The method must observe the size of
the buffer
// passed as the argument when writing bytes to the output stream
public abstract void writeStream( int bufferSize ) throws IOException;
public void flush() throws IOException
{
outputStream.flush();
}
public void close() throws IOException
{
outputStream.close();
}
}

Example:
Bitmap photo = (Bitmap) getIntent().getExtras().get( "data" );
Backendless.Files.Android.upload( photo, Bitmap.CompressFormat.PNG, 100,
"myphoto.png", "mypics", new AsyncCallback<BackendlessFile>()
{
@Override
public void handleResponse( final BackendlessFile backendlessFile )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{

2015 Backendless Corp.

File Service

189

Toast.makeText( UploadingActivity.this, backendlessFault.toString(),


Toast.LENGTH_SHORT ).show();
}
}

4.7

Save Files From Byte Arrays


In addition to the traditional file upload, files can be saved by uploading a byte array which becomes the
content of the saved file.
Synchronous Methods:
Save a file in a remote path in the Backendless file storage. The path includes both the directory
where the file should be saved and the file name. If another file with the same name already
exists, it is not overwritten and an error is returned.
public void saveFile( String filePathName, byte[] fileContent )
Save a file in a remote path in the Backendless file storage. The path includes both the directory
where the file should be saved and the file name. The overwrite argument determines if the file
should be overwritten, if it already exists.
public void saveFile( String filePathName, byte[] fileContent,
boolean overwrite )
Save a file in a remote path in the Backendless file storage. If the file with the same name
already exists, it is not overwritten and error is returned.
public void saveFile( String path, String fileName, byte[] fileContent )

Save a file in a remote path in the Backendless file storage. The overwrite argument
determines if the file should be overwritten if it already exists.
public void saveFile( String path, String fileName, byte[]
fileContent, boolean overwrite )
Asynchronous Methods:
Save file in a remote path in the Backendless file storage. The path includes both the directory
where the file should be saved and the file name. If the file with the same name already exists, it
is not overwritten and error is returned.
public void saveFile( String filePathName, byte[] fileContent,
AsyncCallback<Void> responder )
Save a file in a remote path in the Backendless file storage. The path includes both the directory
where the file should be saved and the file name. The overwrite argument determines if the file
should be overwritten if it already exists.
public void saveFile( String filePathName, byte[] fileContent,
boolean overwrite, AsyncCallback<Void> responder )
Save a file in a remote path in the Backendless file storage. If the file with the same name
already exists, it is not overwritten and error is returned.

2015 Backendless Corp.

190

Backendless API for Android

public void saveFile( String path, String fileName, byte[]


fileContent, AsyncCallback<Void> responder )
Save a file in a remote path in the Backendless file storage. The overwrite argument
determines if the file should be overwritten if it already exists.
public void saveFile( String path, String fileName, byte[]
fileContent, boolean overwrite, AsyncCallback<Void> responder )
where:
filePathName

path

fileName
fileContent
overwrite

responder

- identifies both the directory where the file should be saved and
the file name. Must start with "/" which represents the root
directory of the remote file storage.
- path of the directory where the file should be stored. Must start
with "/" which represents the root directory of the remote file
storage.
- name of the file where the byte content should be written to.
- an array of bytes to save.
- the file is overwritten if the argument value is true and the file
already exists. Otherwise, if the value is false and another file
with the same name already exists, an error is returned.
- a responder object which receives a callback when the method
successfully completes or if an error occurs. Applies to the
asynchronous methods only.

Example:
The example below describes how to save a file entitled "fox.txt" from the string "The quick
brown fox jumps over the lazy dog." You will need to specify:
content of a new file ("The quick brown fox jumps over the lazy dog")
where to save a new file ("testfolder")
a name of the newly created file ("fox.txt")
whether a new file should overwrite the existing file, if any (true)
public static void saveFile()
{
byte[] bytes = "The quick brown fox jumps over the lazy dog".getBytes();
String savedFileURL = Backendless.Files.saveFile( "tempfolder", "fox.txt",
bytes, true );
System.out.println( "File saved. File URL - " + savedFileURL );
}

The server will return notification and link to the newly added file ("File saved. File URL - " )
or an error.
Errors:

Error codes returned on attempt to save a file from the byte array.
Error Description
Code
6016

When saving a new file from the byte array, the payload exceeds
2,800,000 bytes.

6003

A file you are trying to save already exists in the system and cannot
overwrite since overwrite argument is ether set to false or

2015 Backendless Corp.

File Service

191

omitted.

4.8

File Download

Downloading a File via the Backendless Console


To download a file:
1. Log in to Backendless Console.
2. Select an application for which you want to create a new file. The application page will display
3. Click the Files tab on the left menu. The Root folder containing all files of an application will
display.
4. Locate a file you want to download. Click the Download file icon next to this file.

Downloading a File via API


Downloading a file from the Backendless file storage is the basic HTTP GET operation. The operation
should use the same URL which Backendless returned as the result of the file upload operation.
Alternatively, if the file was uploaded manually using the console, the URL can be composed as:
https://api.backendless.com/<application id>/<version name>/files/
<path>/<file name>
where:
<application id>
<version name>
<path>
<file name>

- ID of the application which can be obtained from the Manage >


App Settings screen of the Backendless Console.
- Application's version name.
- Directory path where the file is saved.
- Name of the file.

Files fetched with the URL scheme defined above are subject to the security constraints and
permissions established by the application developer. See the Files Security section for additional
details on how to secure file storage. Fetching a file secured by an access control list (ACL) policy
requires an additional HTTP header in the request:
2015 Backendless Corp.

192

Backendless API for Android

user-token:<value>
where:
<value>

4.9

- Value of the user token established for the current user session
as a result of the user login operation. The token uniquely
identifies the user session. It is used by Backendless to
establish user identity for all operations where the token is
present. This is necessary in order to determine permissions
applicable to the user and the roles associated with the account.

File Deletion
To delete a file from the Backendless file storage, it must be identified by the file path/name. Files in the
Backendless storage have the following URL structure:
https://api.backendless.com/<application id>/<version name>/files/
<path>/<file name>
The API to delete a file uses the <path>/<filename> part to identify the file which must be deleted.
Methods:
Synchronous Method:
public void Backendless.Files.remove( String filePath ) throws Exception

Asynchronous Method:
public void Backendless.Files.remove( String filePath, AsyncCallback<Void>
responder )

where:
filePath
responder

- Path of the file to delete. The path must consist of the file path
and file name.
- A responder object which receives a callback when the method
successfully deleted the file or if an error occurs. Applies to the
asynchronous methods only.

Example:
Backendless.Files.remove( "pictures/myphoto.png", new AsyncCallback<Void>()
{
@Override
public void handleResponse()
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}

2015 Backendless Corp.

File Service

4.10

193

Directory Deletion
To delete a directory from the Backendless file storage, it must be identified by the its path. Directories
in the Backendless storage have the following URL structure:
https://api.backendless.com/<application id>/<version name>/files/
<path>
The API to delete a directory uses the <path> element from the URL above.
Methods:
Synchronous Method:
public void Backendless.Files.removeDirectory( String path ) throws Exception

Asynchronous Method:
public void Backendless.Files.removeDirectory( String path,
AsyncCallback<Void> responder )

where:
path
responder

- path of the directory to delete.


- a responder object which receives a callback when the method
successfully deleted the directory or if an error occurs. Applies to
the asynchronous methods only.

Example:
Backendless.Files.remove( "pictures", new AsyncCallback<Void>()
{
@Override
public void handleResponse()
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}

4.11

Git Integration
Backendless file storage can also function as a git repository. This could be very convenient for
deploying multiple files from the developer's computer with a single command. Git integration is disabled
by default. To enable git for the file storage:
1. Open Backendless Console
2. Select your app/backend
3. Click Manage and scroll to the Enable .git support section
4. Use the toggle to turn git integration on or off:

2015 Backendless Corp.

194

Backendless API for Android

When the git integration is turned on, all files present in or uploaded to the file storage are immediately
committed to the repository. This integration is bi-directional. It means that any files committed into the
git repository by the means of git, will also be copied into the file storage. When git integration is being
turned off, the git repository is deleted with all the associated history (the files remain in the file storage).
With the git integration enabled, a new folder (.git) appears in the File Browser on the Files screen. The
folder contains the files from the git repository. When a file is uploaded to file storage either via the
Upload API or using the File Browser, it is automatically committed to the repository. Likewise, when a
file is pushed into the repository, it becomes available and visible in the file storage. The same applies to
editing and deleting files either in the Backendless Console or in git repository.
When git is enabled, the repository is available at the following address:
https://git.backendless.com/<application id>/.git
where:
<application id>

- application ID available in Backendless Console at Manage >


App Settings .

When the Backendless backend is configured with a custom domain name, the repository URL is:
http://<custom domain name>/.git
The repository uses the same authentication as Backendless Console. That means all git commands
must use the same developer email address and password as for logging in to Backendless Console.
It is important to note that any system level files created by git are also placed into the file storage (the .
git directory). These files are accounted for when calculating the file space used by the app/backend.

Configuring Local Environment


You have no local files and there is a remote GIT repository:
There are files in the Backendless storage and there are no files locally:
Clone existing repository:
mkdir /path/to/your/project
cd /path/to/your/project
git clone http://git.backendless.com/<your application id>/.git
cd <your application id>
Adding a file locally and pushing to Backendless git:
>
>
>
>

echo "First file" >> file.txt


git add file.txt
git commit -m 'Initial commit with new file'
git push -u origin master

You have with an existing GIT project in your local


environment:
This applies when you already have a local git project. You also enabled git integration in Backendless

2015 Backendless Corp.

File Service

195

and need to "integrate" your local git project with the git repository in Backendless.
> cd /path/to/my/repo
> git remote add origin http://git.backendless.com/<your application
id>/.git
# pushes the repo and its refs for the first time to Backendless git
> git push -u origin --all
# pushes any tags to Backendless git
> git push -u origin --tags

You have an existing FILE project in your local


environment.
This applies when you have existing files locally and need to add them to the git repository you
initialized in Backendless.
> cd /path/to/my/repo
> git init
> git remote add origin http://git.backendless.com/<your
application id>/.git
> git pull -u origin master
> git add *
> git commit -m merge with existing project
> git push -u origin master

4.12

Web Hosting
Backendless file storage includes a special directory which facilitates web hosting for the app/backend.
The directory name is /web :

2015 Backendless Corp.

196

Backendless API for Android

The /web folder serves as the web server root. The web server is available at the following URLs:
With custom domain name enabled for the account:
http://custom domain name
Without custom domain name:
https://api.backendless.com/<application id>/<version name>/files/
web
where:
<application id>
<version name>

4.13

- ID of the application which can be obtained from the Manage >


App Settings screen of the Backendless Console
- application's version name

Custom Domain Name


Backendless File Service supports mapping of a custom domain name to the application's backend. As
a result, once a domain name is mapped, the following backend's resources become available via the
custom URL:
Service API endpoint. The default endpoint for all Backendless services is:
https://api.backendless.com
With a custom domain name, the endpoint is also available at:
http://<custom domain name>/api
Web Hosting. Backendless file storage contains a special directory - /web , which serves as the
web site root. When a custom domain name is mapped to a Backendless application/backend,
the contents of the /web directory are served for the HTTP requests with the domain name. See
the Web Hosting section for additional details.
git endpoint. When the Backendless git integration is enabled, the git endpoint with a custom
domain name is:
http://<custom domain name>/.git
Before a custom domain name is assigned to a Backendless application:
1. Create a CNAME record for the domain name and map it to backendless.com.
2. Open Backendless Console and select your application/backend.
3. Click Manage and scroll down to the "Custom Domain" section.
4. Enter the domain name into the text field and click Save

2015 Backendless Corp.

File Service

197

The Custom Domain mapping is a feature included into Backendless Plus package. The package
enables multiple features for a flat monthly subscription fee. Backendless Plus can be enabled in
console at Manage > Billing .

4.14

Custom Web Template Hosting


A client-to-backend workflow may include interaction with web pages presented to the users of the
application. Consider the following scenarios:
User registration. When a user registers with an application, he receives an email with a link to a
page. Clicking the link acknowledges that the email address is valid and the user account is
confirmed.
Password change. When a user requests password change (or password recovery), an email is
sent to the user with a link to a web page where they can reset the password.
Session expiration. When a user session with the application expires, he is redirected to a
webpage.
All these use cases have something in common - they all force the user to interact with a web page. The
Backendless Plus package allows to customize the look and feel of these pages. Once Backendless
Plus is enabled (use the Manage > Billing section in console to turn it on), Backendless puts the
templates for these pages into the /web/templates path of the backend's file storage:

2015 Backendless Corp.

198

Backendless API for Android

The default style of these pages is neutral:


Registration confirmation page:

Password change page:

2015 Backendless Corp.

File Service

199

Session expiration page:

The look and feel as well as the logic in the pages can be customized by modifying the HTML/CSS /JS
files provided for each template. For example, the contents of the change_password folder is:

2015 Backendless Corp.

200

4.15

Backendless API for Android

Files Security
Access to files and directories can be restricted using permissions. Backendless supports the following
permissions for files and directories:
Read - permission to download a file. This permission can be applied to a directory, in that case it
applies recursively to all files contained therein.
Write - permission to upload a file or modify a directory by uploading files into it.
Remove - permission to delete a file or a directory.
To modify the permission matrix for a file or a directory, click the "Edit Permissions" link in file browser
in console. The permission assignment screen contains allows to work with permissions for specific
user accounts or for application roles.
To assign permissions to a user account, type in userid in the "enter user name" field, select the user(s)
and click "Add":

2015 Backendless Corp.

File Service

201

To modify a permission for an operation for a user, click the icon in the corresponding column. The icon
has 3 states:
- inherit GRANT permission from the global permission matrix. This is the default permission.
- explicit GRANT of the permission for the operation. Allows the user to perform the operation.
- DENY permission for the operation. Restricts the user from performing the operation.
Managing permissions for roles is identical to users, except all roles are automatically listed in the table:

Geo Service

5.1

Overview
Backendless Geolocation Service is a system supporting management and search of geo points. A geo
point in the most primitive format consists of a pair of coordinates: latitude and longitude. Optionally a
geo point may contain metadata, which is a collection of arbitrary key/value pairs. A geo point belongs
to a category, which is a logical grouping of geo points. The diagram bellow illustrates these concepts:

Backendless allows infinite number of geo points managed for an application. Geo points can be added
via an API call or the import functionality in Backendless console. Once the backend is populated with
geo points, the search API can be used to run the following types of geo queries:
Radius-based search - Searches for geo points in a circular map area defined by the coordinates
of the central point and a radius. Backendless returns all geo points within the area.

2015 Backendless Corp.

202

Backendless API for Android

Search in a rectangular map area - Searches for geo points in a rectangular map area identified by
the coordinates of two corners defining the area (North West and South East):

Additionally, the geo search API supports the following search options available in the APIs:
Filtering by categories - Both types of search (radius-based and rectangular) can specify the
categories in which the backend should search for the geo points.
Query-based search - The metadata associated with the geo points can be used in queries
which should be formatted using the SQL-92 syntax. For example, the geo point shown in the
image above can be discovered with the following queries:
cuisine = 'French'
cuisine LIKE 'Fr%' and Atmosphere = 'Casual'
cuisine = 'French' and (Price = '$$$$' or Price = '$$$')
Relative search - Runs a search for a subset of metadata key/value pairs to match up to the
specified threshold value. The threshold must be expressed as a percentage of matches.

2015 Backendless Corp.

Geo Service

5.2

203

Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.

Download SDK
The SDK can be downloaded from the Backendless website.

Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );

Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}

Apps with Google Maps and Geolocation


Android applications using Google Maps, for instance, the Geo service sample app generated by
Backendless code generator, require additional configuration in order for Google maps to be displayed.
The process of configuring an app to support Google maps consists of the following tasks:
1.
Display the debug certificate fingerprint
1. Locate your debug keystore file. The file name is debug.keystore , and is created the first time
you build your project. By default, it is stored in the same directory as your Android Virtual Device
(AVD) files:
OS X and Linux: ~/.android/
Windows Vista and Windows 7: C:\Users\your_user_name\.android\
2. If you are using Eclipse with ADT, and you are not sure where your debug keystore is located, you
can select Windows > Prefs > Android >Build to check the full path, which you can then paste
into a file explorer to locate the directory containing the keystore.
3. List the SHA-1 fingerprint.
For Linux or OS X, open a terminal window and enter the following:

2015 Backendless Corp.

204

Backendless API for Android

keytool -list -v -keystore ~/.android/debug.keystore -alias


androiddebugkey -storepass android -keypass android
For Windows Vista and Windows 7, run:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.
keystore" -alias androiddebugkey -storepass android -keypass
android
4. You should see output similar to this:
Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01
18:04:04 PST 2033
Certificate fingerprints:
MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:
A1:66:6E:44:5D:75
Signature algorithm name: SHA1withRSA
Version: 3
5. The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the
sequence of 20 two-digit hexadecimal numbers separated by colons.
2.
Create an API project in the Google APIs Console
1. In a browser, navigate to the Google APIs Console.
If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.
4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....

2015 Backendless Corp.

Geo Service

205

5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.

Add the API key to your application


Follow the steps below to include the API key in your application's manifest, contained in the file
AndroidManifest.xml. From there, the Maps API reads the key value and passes it to the Google
Maps server, which then confirms that you have access to Google Maps data.
1. In AndroidManifest.xml, add the following element as a child of the <application> element, by
inserting it just before the closing tag </application> :
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
2. Substitute your API key for API_KEY in the value attribute. This element sets the key com.google.
android.maps.v2.API_KEY to the value of your API key, and makes the API key visible to any
MapFragment in your application.
3. Save AndroidManifest.xml and re-build your application.

Application ID and Secret Key


Values for the application-id and secret-key headers must be obtained through the Backendless
Console:
1. Login to your account and select the application.
2. Click the Manage icon from the vertical icon-menu on the left.
3. The "App Settings" section is selected by default. The interface contains the text fields for
"Application ID" and secret keys for each supported client-side environment.
4. Use the "Copy" button to copy the value into the system clipboard.

2015 Backendless Corp.

206

Backendless API for Android

Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".

5.3

Sync and Async Calls


Most of Backendless API for Android and Java is available in synchronous and asynchronous formats.
The difference in the method signatures is the asynchronous methods accept the AsyncCallback<T>
argument:
package com.backendless.async.callback;
import com.backendless.exceptions.BackendlessFault;
public interface AsyncCallback<T>
{
void handleResponse( T response );
void handleFault( BackendlessFault fault );
}

The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;

2015 Backendless Corp.

Geo Service

207

import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}

When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.

5.4

Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}

The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )

The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}

5.5

Adding a Geo Category


This API creates a geo category. A geo category is a logical grouping of geo points. Category name
may contain the following literals: a-z, A-Z, numbers 0-9 and the underscore (_ ) character. The name

2015 Backendless Corp.

208

Backendless API for Android

must start with a literal. Category names can be inspected using Backendless Console (see the image
below) or using the API call retrieving a list of categories.

Adding Categories in Console


Backendless Console supports adding a category via the graphical interface. To create a category:
1. Login to Backendless Console
2. Select your app/backend.
3. Click the Geolocation icon in the menu on the left.
4. Use the "plus" icon in the section containing the list of categories:

5. Enter the category name in the popup and click "Save".


The image below shows the Geolocation screen with categories in the app:

2015 Backendless Corp.

Geo Service

209

Adding Categories with the API


Methods:
Synchronous Method:
public GeoCategory Backedless.Geo.addCategory( String categoryName ) throws
BackendlessException

Asynchronous Method:
public void Backedless.Geo.addCategory( String categoryName,
AsyncCallback<GeoCategory> responder )

where:
categoryName
responder

- name of the category to create.


- a responder object which receives a callback when the method
successfully creates the category or if an error occurs. Applies
to the asynchronous methods only.

Return Value:
An instance of GeoCategory representing the new category with object properties for category's
objectId, name and number of geo points in it:
GetCategory.java
package com.backendless.geo;
public class GeoCategory implements Comparable<GeoCategory>

2015 Backendless Corp.

210

Backendless API for Android

{
private String objectId;
private String name;
private int size;
// returns objectId assigned to the category
public String getId()
{
return objectId;
}
// returns category name
public String getName()
{
return name;
}
// returns number of geo points in the category
public int getSize()
{
return size;
}
public void setName( String name )
{
this.name = name;
}
@Override
public boolean equals( Object o )
{
if( this == o )
return true;
if( o == null || getClass() != o.getClass() )
return false;
GeoCategory that = (GeoCategory) o;
if( !name.equals( that.name ) )
return false;
if( objectId != null ? !objectId.equals( that.objectId ) : that.objectId
!= null )
return false;
return true;
}
@Override
public int hashCode()
{
int result = objectId != null ? objectId.hashCode() : 0;
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString()

2015 Backendless Corp.

Geo Service

211

{
final StringBuffer sb = new StringBuffer();
sb.append( "GeoCategory" );
sb.append( "{name='" ).append( name ).append( '\'' );
sb.append( ", size=" ).append( size );
sb.append( '}' );
return sb.toString();
}
@Override
public int compareTo( GeoCategory arg )
{
if( this == arg )
return 0;
int nameDiff = this.name == null ? (arg.getName() == null ? 0 : -1) :
this.name.compareTo( arg.getName() );
if( nameDiff != 0 )
return nameDiff;
return size - arg.getSize();
}
}

Example:
Backendless.Geo.addCategory( "mycategory", new AsyncCallback<GeoCategory>()
{
@Override
public void handleResponse( GeoCategory category )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}

5.6

Deleting a Geo Category


This API deletes a geo category. If the category does not exist, the service returns an error.

Removing Categories in Console


Backendless Console supports category deletion via the graphical interface. To delete a category:
1. Login to Backendless Console
2. Select your app/backend.
3. Click the Geolocation icon in the menu on the left.
4. Use the "minus" icon in the section containing the list of categories:

2015 Backendless Corp.

212

Backendless API for Android

5. Select 'Yes' in the confirmation popup.

Deleting Categories with the API


Methods:
Synchronous Method:
public boolean Backedless.Geo.deleteCategory( String categoryName ) throws
BackendlessException

Asynchronous Method:
public void Backedless.Geo.deleteCategory( String categoryName,
AsyncCallback<Boolean> responder )

where:

2015 Backendless Corp.

Geo Service

categoryName
responder

213

- name of the category to delete.


- a responder object which receives a callback when the method
successfully deletes the category or if an error occurs. Applies to
the asynchronous methods only.

Return Value:
true if the category is deleted, false otherwise.
Example:
Backendless.Geo.deleteCategory( "mycategory", new AsyncCallback<Boolean>()
{
@Override
public void handleResponse( Boolean result )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}

5.7

Retrieving Geo Categories


This API retrieves a list of all the application's geo categories.
Methods:
Synchronous Method:
public List<GeoCategory> Backedless.Geo.getCategories() throws
BackendlessException

Asynchronous Method:
public void Backedless.Geo.addCategory( AsyncCallback<List<GeoCategory>>
responder )

where:
responder

- a responder object which receives a callback when the method


successfully retrieves the categories contained in the application
or if an error occurs. Applies to the asynchronous methods only.

Return Value:
A list of the GeoCategory objects. Each GeoCategory instance represents a category with the
properties of objectId, name and number of geo points in it:
GetCategory.java
package com.backendless.geo;
public class GeoCategory implements Comparable<GeoCategory>
{
private String objectId;
private String name;
private int size;
// returns objectId assigned to the category
public String getId()

2015 Backendless Corp.

214

Backendless API for Android

{
return objectId;
}
// returns category name
public String getName()
{
return name;
}
// returns number of geo points in the category
public int getSize()
{
return size;
}
public void setName( String name )
{
this.name = name;
}
@Override
public boolean equals( Object o )
{
if( this == o )
return true;
if( o == null || getClass() != o.getClass() )
return false;
GeoCategory that = (GeoCategory) o;
if( !name.equals( that.name ) )
return false;
if( objectId != null ? !objectId.equals( that.objectId ) : that.objectId
!= null )
return false;
return true;
}
@Override
public int hashCode()
{
int result = objectId != null ? objectId.hashCode() : 0;
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString()
{
final StringBuffer sb = new StringBuffer();
sb.append( "GeoCategory" );
sb.append( "{name='" ).append( name ).append( '\'' );
sb.append( ", size=" ).append( size );
sb.append( '}' );
return sb.toString();

2015 Backendless Corp.

Geo Service

215

}
@Override
public int compareTo( GeoCategory arg )
{
if( this == arg )
return 0;
int nameDiff = this.name == null ? (arg.getName() == null ? 0 : -1) :
this.name.compareTo( arg.getName() );
if( nameDiff != 0 )
return nameDiff;
return size - arg.getSize();
}
}

Example:
Backendless.Geo.getCategories(new AsyncCallback<List<GeoCategory>>()
{
@Override
public void handleResponse( List<GeoCategory> category )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}

5.8

Adding a GeoPoint
This API adds a geo point to the backend geo location storage. Once a geo point is added, it becomes
searchable through all search mechanisms supported by Backendless. At the present moment there are
two ways to add geo points: (1) using this API or (2) using the Backendless console's import function.
Methods:
Synchronous Methods:
public GeoPoint savePoint( GeoPoint geoPoint ) throws BackendlessException
public GeoPoint savePoint( double latitude,
double longitude,
Map<String, Object> metadata ) throws
BackendlessException
public GeoPoint savePoint( double latitude,
double longitude,
List<String> categories,
Map<String, Object> metadata ) throws
BackendlessException

Asynchronous Method:
public GeoPoint savePoint( GeoPoint geoPoint, AsyncCallback<GeoPoint>

2015 Backendless Corp.

216

Backendless API for Android

responder ) throws BackendlessException


public GeoPoint savePoint( double latitude,
double longitude,
Map<String, Object> metadata,
AsyncCallback<GeoPoint> responder ) throws
BackendlessException
public GeoPoint savePoint( double latitude,
double longitude,
List<String> categories,
Map<String, Object> metadata,
AsyncCallback<GeoPoint> responder ) throws
BackendlessException

where:
geoPoint
latitude
longitude
categories

metadata

responder

- a strongly typed object defining the properties of a geo point to


add. See the class definition in the "Return Value" section.
- latitude of the point to add.
- longitude of the point to add.
- list of categories the point is added to. If a category does not
exist at the time when a point is added, Backendless creates the
category and adds the point to it. For the methods without this
argument, the point is added to the "Default" category.
- metadata associated with the geo point. Must be an arbitrary
collection (map) of key/value pairs. Accepted values for this
parameter are: String, Number (integer and double), and Data
Service objects. Date values must be represented as number in
the Unix timestamp format (number of milliseconds since
January 1, 1970 at UTC). Learn more about using date in search
queries for category, radius, or rectangular area search.
- a responder object which receives a callback when the method
successfully adds the geo point. Applies to the asynchronous
methods only.

Return Value:
An instance of the GeoPoint class representing the new geo point added to the application's
geo location storage. Below is a brief version of the class. The complete source code listing is
available in the Backendless github repository.
Example:
List<String> categories = new ArrayList<String>()
categories.add( "restaurants" );
categories.add( "cool_places" );
Map<String, Object> meta = new HashMap<String, Object>();
meta.put( "name", "Eatzi's" );
Backendless.Geo.savePoint( 32.81, -96.80, categories, meta, new
AsyncCallback<GeoPoint>()
{
@Override
public void handleResponse( GeoPoint geoPoint )
{
System.out.println( geoPoint.getObjectId() );
}

2015 Backendless Corp.

Geo Service

217

@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
});
}

5.9

Updating a GeoPoint
Geo update API relies on the same methods used for Adding a Geo Point. The primary difference is
in order to update a geo point it must have the objectId property assigned by Backendless. The
semantics of the properties in an update request is as follows:
objectId is a required property.
All other properties (latitude , longitude , categories , metadata ) are optional, but at least
one must contain a value.
If latitude or longitude contain values, the new values replace the existing ones.
If categories contains a value, the geo point is moved to the specified categories (with
coordinates and metadata).
If categories is null , the geo point stays in the current category.
If metadata is null , the geo point keeps the current metadata.
If metadata contains any key/value pairs, the new metadata replaces the existing one.
If metadata is an empty object/dictionary, the existing metadata is removed.

5.10

Deleting a GeoPoint
There are two ways to delete a geopoint from the Geolocation storage:
Using the Backendless Console
Using the API

Deleting a GeoPoint using the Backendless Console


To delete a geo point using the Backendless Console:
1. Log in to the Backendless Console, select your app and click the Geolocation icon.
2. Select the geo category from which the geopoint will be deleted.
3. Click the checkboxes next to the geopoint(s) which should be deleted.
4. Click Delete Selected from the button bar as shown below:

2015 Backendless Corp.

218

Backendless API for Android

5. Click Delete in the confirmation popup to confirm the deletion:

6. A confirmation notification will appear in the top right. The selected geopoint(s) are removed.

Deleting a GeoPoint with API


Synchronous Method:
public void removePoint( GeoPoint geoPoint )

Asynchronous Method:
public void removePoint( GeoPoint geoPoint, AsyncCallback<Void> responder )

where:
geoPoint
responder

- a geopoint object to delete from the Geolocation storage.


- a responder object which receives a callback when the method
successfully delete the geopoint. Applies to the asynchronous
2015 Backendless Corp.

Geo Service

219

method only.
Return Value:
None
Example:
The code below demonstrates how to delete a geopoint. A geopoint is added first, then
subsequently deleted.
Synchronous API sample:
GeoPoint geoPoint = new GeoPoint( -31.96, 115.84 );
GeoPoint savedGeoPoint = Backendless.Geo.savePoint( geoPoint );
Backendless.Geo.removePoint( savedGeoPoint );

Asynchronous API sample:


GeoPoint geoPoint = new GeoPoint( -31.96, 115.84 );
Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
{
@Override
public void handleResponse( GeoPoint savedGeoPoint )
{
Backendless.Geo.removePoint( savedGeoPoint, new AsyncCallback<Void>()
{
@Override
public void handleResponse( Void response )
{
// GeoPoint has been deleted
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with fault.
getCode()
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with fault.
getCode()
}
});

5.11

Importing Geo Data


Backendless console supports bulk import of geo points with metadata. The import procedure
automatically places the geo points into the specified categories. The raw data must be in a comma
separated values (CSV) format. A single line in the file defines one geo point as shown below:
latitude,longitude,"category1,category2","key1=value1|key2=value2"
where:
latitude

2015 Backendless Corp.

- the latitude coordinate of a geo point

220

Backendless API for Android

- the longitude coordinate of a geo point


- comma separated list of categories. The value must be
enclosed in double quotes.
key1=value1|key2=value2 - Geo point metadata. Multiple key/value pairs must be
separated with the pipe "|" character. The value must be
enclosed in double quotes.
longitude
category1,category2

Consider the following example:


40.4165,-3.70256,"restaurants,popular","city=MADRID|cuisine=french|
price=high"
41.38879,2.15899,"restaurants,popular","city=BARCELONA|
cuisine=asian|price=low"
To import geo points:
1. Open Backendless console.
2. Select the application/backend.
3. Click Manage, then Import.
4. Click the "add file" button next to "Geo Service" and select the file to import.
5. Click the "Import" button to initiate the import process.
The import process runs asynchronously. When the process is complete, Backendless sends an email
to the application developer. The email text includes the log entries informing about any errors which
could have occurred during the import. Upon successful completion of the import, the Geolocation
section of the console displays the categories, geo points and their metadata:

2015 Backendless Corp.

Geo Service

5.12

221

Search in Category
This API supports two types of geo searches:
Search in one or more geo categories.
Search in one or more categories based on metadata properties.

Methods:
Synchronous Method:
public BackendlessCollection<GeoPoint> Backedless.Geo.getPoints(
BackendlessGeoQuery geoQuery ) throws BackendlessException

Asynchronous Method:
public void getPoints( BackendlessGeoQuery geoQuery,
AsyncCallback<BackendlessCollection<GeoPoint>>
responder )

where:
geoQuery
responder

- a query object encapsulating the search parameters. See the


Running Search Queries section below for details.
- a responder object which receives a callback when the method
successfully returns search result or if an error occurs. Applies
to the asynchronous method only.

Return Value:
A collection of GeoPoint objects matching the search query. The class properties are:
objectId
- an ID assigned to geo point by Backendless when it is saved in
the backend geo location storage.
latitude
- latitude of the geo point.
longitude
- longitude of the geo point.
categories
- an array of geo categories the point belong to.
metadata
- metadata associated with the geo point. Accepted values for
this parameter are: String, Number (integer and double), and
Data Service objects. Date values must be represented as
number in the Unix timestamp format (number of milliseconds
since January 1, 1970 at UTC). Learn more about using date in
search queries for category, radius, or rectangular area search.
Geo points returned from the search query are contained inside of BackendlessCollection .
Since the search query may produce a large number of geo points, not all of them are returned
at once. Instead, all found geo points are divided into 'pages'. The size of each page is
determined by the pageSize parameter in the query object. The first response returns the first
page. The collection class includes methods for loading additional pages.
BackendlessCollection also includes the total number of all geo points found by the search
operation (the totalObjects value).
All geo points in the entire search result are indexed. The index of the first geo point is 0. The
offset parameter in the query object and in some method of BackendlessCollection
specifies the index from which to load the next page of geo points. For example, suppose the
entire search result is 200 points (the totalObjects value returned in the collection is 200). If
the initial pageSize is 20, then only 20 geo points are returned in the first response. To get the
second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].

2015 Backendless Corp.

222

Backendless API for Android

Running Search Queries


The geo query object includes multiple parameters, none of them are required. As a result, depending on
which parameters contain values, the semantics of the search would change. Any search must be
performed within at least one category. If no category names are provided, the search is performed in the
Default category.
Search in categories
To search in one or more categories without any constraints on metadata or proximity to a
center point, simply set the names of the categories in the query object. The request returns all
geo points divided into pages of data, one page at a time.
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

Search in categories with metadata


Metadata-based search finds all geo points which match all specified metadata properties in the
given categories. The example below searches for the geo points in the Restaurants category
with metadata containing "Cuisine = French" and "Atmosphere = Romantic" .
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
HashMap<String, String> metaSearch = new HashMap<String, String>();
metaSearch.put( "Cuisine", "French" );
metaSearch.put( "Athmosphere", "Romantic" );
geoQuery.setMetadata( metaSearch );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

Search in categories by date


The search query used to retrieve geo points may reference date values. These values must be
stored as a number of milliseconds since January 1st, 1970 at UTC. The example below
demonstrates the use of a date/time timestamp in a search query.
Using dates in where clause when searching in categories

The search query used to retrieve geo points may reference date values. These
values must be stored as a number of milliseconds since January 1st, 1970 at
UTC. The example below demonstrates the usage of a date/time timestamp in a
search query:
Search in category by date by using synchronous call:
public void searchByDateInCategorySync() throws ParseException
{
try
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:
mm" );
Date updated = dateFormat.parse( "17.01.2015 at 12:00" );
// create

2015 Backendless Corp.

Geo Service

223

GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );


geoPoint.addCategory( "Coffee" );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "Parking", true );
geoPoint.addMetadata( "updated", new Date().getTime() );
geoPoint = Backendless.Geo.savePoint( geoPoint );
System.out.println( String.format( "searchByDateInCategory -> point: %s",
geoPoint ) );
// search
BackendlessGeoQuery query = new BackendlessGeoQuery( Arrays.asList(
"Coffee" ) );
query.setWhereClause( String.format( "updated > %d", updated.getTime
() ) );
query.setIncludeMeta( true );
BackendlessCollection<GeoPoint> points = Backendless.Geo.getPoints( query
);
System.out.println( String.format( "searchByDateInCategory GETPOINTS: %s"
, points.getCurrentPage() ) );
}
catch( BackendlessException e )
{
System.err.println( String.format( "searchByDateInCategory FAULT = %s", e
) );
}
}

Search in category by date by using asynchronous call:


public void searchByDateInCategoryAsync() throws ParseException
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:mm"
);
final Date updated = dateFormat.parse( "17.01.2015 at 12:00" );
// create
GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );
geoPoint.addCategory( "Coffee" );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "Parking", true );
geoPoint.addMetadata( "updated", new Date().getTime() );
Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
{
@Override
public void handleResponse( GeoPoint geoPoint )
{
System.out.println( String.format( "searchByDateInCategory -> point: %
s", geoPoint ) );
// search
BackendlessGeoQuery query = new BackendlessGeoQuery( Arrays.asList(
"Coffee" ) );
query.setWhereClause( String.format( "updated > %d", updated.getTime
() ) );
query.setIncludeMeta( true );

2015 Backendless Corp.

224

Backendless API for Android

Backendless.Geo.getPoints( query, new


AsyncCallback<BackendlessCollection<GeoPoint>>()
{
@Override
public void handleResponse( BackendlessCollection<GeoPoint> points )
{
System.out.println( String.format( "searchByDateInCategory
GETPOINTS: %s", points.getCurrentPage() ) );
}
@Override
public void handleFault( BackendlessFault fault )
{
System.err.println( String.format( "searchByDateInCategory FAULT =
%s", fault ) );
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
System.err.println( String.format( "searchByDateInCategory SAVEPOINT: %
s", fault ) );
}
} );
}

Requesting meta in response


Geo points returned in the search results do not include their metadata properties by default.
The search query object includes a property which can be used to request the metadata to be
included. This property can be used with any search options described above. For example, the
following code runs a search in a category and requests the metadata to be included:
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
geoQuery.setIncludeMeta( true );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

5.13

Search in Radius
This API supports multiple types of geo searches:
Search for geo points located within specified distance (radius) from a given point.
Search in radius based on metadata.
Methods:
Synchronous Method:
public BackendlessCollection<GeoPoint> Backedless.Geo.getPoints(
BackendlessGeoQuery geoQuery ) throws BackendlessException

Asynchronous Method:
public void getPoints( BackendlessGeoQuery geoQuery,
AsyncCallback<BackendlessCollection<GeoPoint>>
responder )

2015 Backendless Corp.

Geo Service

225

where:
geoQuery
responder

- a query object encapsulating the search parameters. See the


Running Search Queries section below for details.
- a responder object which receives a callback when the method
successfully returns search result or if an error occurs. Applies
to the asynchronous method only.

Return Value:
A collection of GeoPoint objects matching the search query. The GeoPoint class is defined as
GeoPoint.java

2015 Backendless Corp.

226

Backendless API for Android

package com.backendless.geo;
import
import
import
import
import

java.io.Serializable;
java.util.ArrayList;
java.util.HashMap;
java.util.List;
java.util.Map;

public class GeoPoint implements Serializable


{
private static final long serialVersionUID = -4982310969493523406L;
private
private
private
private
private
private

String objectId;
double latitude;
double longitude;
List<String> categories;
Map<String, String> metadata;
Double distance;

private final static int multiplier = 1000000;


public GeoPoint()
{
}
public GeoPoint( double latitude, double longitude )
{
this.latitude = latitude;
this.longitude = longitude;
}
public GeoPoint( int latitudeE6, int longitudeE6 )
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
}

public GeoPoint( double latitude, double longitude, List<String> categories, Map<String, S


{
this.latitude = latitude;
this.longitude = longitude;
this.categories = categories;
this.metadata = metadata;
}

public GeoPoint( int latitudeE6, int longitudeE6, List<String> categories, Map<String, Str
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
this.categories = categories;
this.metadata = metadata;
}
public String getObjectId();
public double getLatitude();
public void setLatitude( double latitude );
public double getLongitude();
public void setLongitude( double longitude );

2015 Backendless Corp.

Geo Service

227

public int getLatitudeE6();


public void setLatitudeE6( int latitudeE6 );
public int getLongitudeE6();
public void setLongitudeE6( int longitudeE6 );
public List<String> getCategories();
public void addCategory( String category );
public void setCategories( List<String> categories );
public
public
public
public
public
public

Map<String, String> getMetadata();


String getMetadata( String key );
void putMetadata( String key, String value );
void putAllMetadata( Map<String, String> metadata );
void clearMetadata();
void setMetadata( Map<String, String> metadata );

public Double getDistance();


public void setDistance( Double distance );
@Override
public boolean equals( Object o );
@Override
public int hashCode();
@Override
public String toString();
}

where the geo point object properties are:


objectId
- ID assigned to geo point by Backendless when it is saved in
the backend geo location storage.
latitude
- latitude of the geo point.
longitude
- longitude of the geo point.
categories
- an array of geo categories the point belong to.
metadata
- metadata associated with the geo point. Accepted values for
this parameter are: String, Number (integer and double), and
Data Service objects. Date values must be represented as
number in the Unix timestamp format (number of milliseconds
since January 1, 1970 at UTC). Learn more about using date in
search queries for category, radius, or rectangular area search.
distance
- distance between the center point referenced in the search
query and the geo point. The distance is always in units
specified in the search query.
Geo points returned from the search query are contained inside of BackendlessCollection .
Since the search query may produce a large number of geo points, not all of them are returned
at once. Instead, all found geo points are divided into 'pages'. The size of each page is
determined by the pageSize parameter in the query object. The first response returns the first
page. The collection class includes methods for loading additional pages.
BackendlessCollection also includes the total number of all geo points found by the search
operation (the totalObjects value).
All geo points in the entire search result are indexed. The index of the first geo point is 0. The
offset parameter in the query object and in some method of BackendlessCollection
specifies the index from which to load the next page of geo points. For example, suppose the
entire search result is 200 points (the totalObjects value returned in the collection is 200). If
the initial pageSize is 20, then only 20 geo points are returned in the first response. To get the
2015 Backendless Corp.

228

Backendless API for Android

second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].
The geo points in the search results will be sorted by their proximity to the central point (center
of the radius): the geo points that are closest to the central point will be listed first.
Below is a compressed version of the BackendlessCollection class. The full source code
listing is available in the Backendless github repository:
BackendlessCollection.java
package com.backendless;
import
import
import
import

com.backendless.async.callback.AsyncCallback;
com.backendless.exceptions.BackendlessException;
com.backendless.geo.BackendlessGeoQuery;
com.backendless.persistence.BackendlessDataQuery;

public class BackendlessCollection<E>


{
// returns the total number of geo points available for the search
query.
// This number may be different than the number of geo points currently
present
// in the collection as the collection contains only a 'page' of data,
that is
// a subset with the size defined by pageSize value in
BackendlessGeoQuery
// See the Running Search Queries section below for details.
public int getTotalObjects()
// Returns the current page - the collection of geo points returned
// for the latest search query request. The size of the page is
determined
// by the pageSize parameter in the query object.
public List<E> getCurrentPage()
// Sets the query object to modify pageSize and offset for the subsequent
// request to load additional pages of data.
public void setQuery( IBackendlessQuery query )
// Returns the query object which will drive the subsequent nextPage/
previousPage requests.
// This may not be the original geo query object as the underlying offset
value will change.
public IBackendlessQuery getQuery()
// Loads the next page using pageSize and offset from the query object.
public BackendlessCollection<E> nextPage() throws BackendlessException
// Same as above, but runs the operation asynchronously
public void nextPage( AsyncCallback<BackendlessCollection<E>> responder )
// Loads 'pageSize' number of geo points from the search result from the
specified offset (index).
public BackendlessCollection<E> getPage( int pageSize, int offset )
throws BackendlessException

2015 Backendless Corp.

Geo Service

229

// Same as above, but is executed asynchronously


public void getPage( int pageSize, int offset,
AsyncCallback<BackendlessCollection<E>> responder )
// Loads previous using pageSize and offset from the current query
object.
public BackendlessCollection<E> previousPage() throws
BackendlessException
// Same as above, but is loaded asynchronously
public void previousPage( AsyncCallback<BackendlessCollection<E>>
responder )
}

Running Search Queries


The geo query object includes multiple parameters, none of them are required. As a result, depending on
which parameters contain values, the semantics of the search would change. Any search must be
performed within at least one category. If no category names are provided, the search is performed in the
Default category.
Search in categories with radius
Radius-based search establishes a circular area by setting the coordinates of a central point
and a distance (radius). Backendless searches for geo points in the specified distance from the
coordinates in the center and includes them into the search result. The value of the distance is
interpreted based in the units parameter, which can be METERS , KILOMETERS , MILES , YARDS ,
FEET:
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
geoQuery.setLatitude( 41.38 );
geoQuery.setLongitude( 2.15 );
geoQuery.setRadius( 10000d );
geoQuery.setUnits( Units.METERS );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

Search in categories with radius and metadata


This is the same as above, with the difference that the search result includes only geo points
with the matching metadata:
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
geoQuery.setLatitude( 41.38 );
geoQuery.setLongitude( 2.15 );
geoQuery.setRadius( 10000d );
geoQuery.setUnits( Units.METERS );
HashMap<String, String> metaSearch = new HashMap<String, String>();
metaSearch.put( "Cuisine", "French" );
metaSearch.put( "Athmosphere", "Romantic" );
geoQuery.setMetadata( metaSearch );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

Search in radius by date


The search query used to retrieve geo points may reference date values. These values must be
2015 Backendless Corp.

230

Backendless API for Android

stored as a number of milliseconds since January 1st, 1970 at UTC. The example below
demonstrates the use of a date/time timestamp in a search query.
Using dates in where clause when searching in radius

The search query used to retrieve geo points may reference date values. These
values must be stored as a number of milliseconds since January 1st, 1970 at
UTC. The example below demonstrates the usage of a date/time timestamp in a
search condition:
Search in radius by date by using synchronous call:
public void searchByDateInRadiusSync() throws ParseException
{
try
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:
mm" );
Date updated = dateFormat.parse( "17.01.2015 at 12:00" );
// create
GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );
geoPoint.setCategories( Arrays.asList( "City", "Coffee" ) );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "City", "Honolulu" );
geoPoint.addMetadata( "Parking", true );
geoPoint.addMetadata( "updated", new Date().getTime() );
geoPoint = Backendless.Geo.savePoint( geoPoint );
System.out.println( String.format( "searchByDateInRadius -> point: %s",
geoPoint ) );
// search
BackendlessGeoQuery query = new BackendlessGeoQuery( 21.30, -157.85, 50,
Units.KILOMETERS );
query.addCategory( "City" );
query.setWhereClause( String.format( "updated > %d", updated.getTime
() ) );
query.setIncludeMeta( true );
BackendlessCollection<GeoPoint> points = Backendless.Geo.getPoints( query
);
System.out.println( String.format( "searchByDateInRadius GETPOINTS: %s",
points.getCurrentPage() ) );
}
catch( BackendlessException e )
{
System.err.println( String.format( "searchByDateInRadius FAULT = %s", e )
);
}
}

Search in radius by date by using asynchronous call:


public void searchByDateInRadiusAsync() throws ParseException
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:mm"
);

2015 Backendless Corp.

Geo Service

231

final Date updated = dateFormat.parse( "17.01.2015 at 12:00" );


// create
GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );
geoPoint.setCategories( Arrays.asList( "City", "Coffee" ) );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "City", "Honolulu" );
geoPoint.addMetadata( "Parking", true );
geoPoint.addMetadata( "updated", new Date().getTime() );
Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
{
@Override
public void handleResponse( GeoPoint geoPoint )
{
System.out.println( String.format( "searchByDateInRadius -> point: %s",
geoPoint ) );
// search
BackendlessGeoQuery query = new BackendlessGeoQuery( 21.30, -157.85, 50
, Units.KILOMETERS );
query.addCategory( "City" );
query.setWhereClause( String.format( "updated > %d", updated.getTime
() ) );
query.setIncludeMeta( true );
Backendless.Geo.getPoints( query, new
AsyncCallback<BackendlessCollection<GeoPoint>>()
{
@Override
public void handleResponse( BackendlessCollection<GeoPoint> points )
{
System.out.println( String.format( "searchByDateInRadius GETPOINTS:
%s", points.getCurrentPage() ) );
}
@Override
public void handleFault( BackendlessFault fault )
{
System.err.println( String.format( "searchByDateInRadius FAULT = %
s", fault ) );
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
System.err.println( String.format( "searchByDateInRadius SAVEPOINT: %s"
, fault ) );
}
} );
}

Requesting meta in response


Geo points returned in the search results do not include their metadata properties by default.
The search query object includes a property which can be used to request the metadata to be
included. This property can be used with any search options described above. The syntax for
requesting metadata in response is described in the Search in Category section.

2015 Backendless Corp.

232
5.14

Backendless API for Android

Search in Rectangular Area


This API runs a search within a rectangular area of the map. The area is defined with the coordinates of
the North West and South East corners of the map rectangle.
Methods:
Synchronous Method:
public BackendlessCollection<GeoPoint> Backedless.Geo.getPoints(
BackendlessGeoQuery geoQuery ) throws BackendlessException

Asynchronous Method:
public void getPoints( BackendlessGeoQuery geoQuery,
AsyncCallback<BackendlessCollection<GeoPoint>>
responder )

where:
geoQuery
responder

- a query object encapsulating the search parameters. See the


Running Search Queries section below for details.
- a responder object which receives a callback when the method
successfully returns search result or if an error occurs. Applies
to the asynchronous method only.

Return Value:
A collection of GeoPoint objects matching the search query. The GeoPoint class is defined as:
GeoPoint.java

2015 Backendless Corp.

Geo Service

233

package com.backendless.geo;
import
import
import
import
import

java.io.Serializable;
java.util.ArrayList;
java.util.HashMap;
java.util.List;
java.util.Map;

public class GeoPoint implements Serializable


{
private static final long serialVersionUID = -4982310969493523406L;
private
private
private
private
private
private

String objectId;
double latitude;
double longitude;
List<String> categories;
Map<String, String> metadata;
Double distance;

private final static int multiplier = 1000000;


public GeoPoint()
{
}
public GeoPoint( double latitude, double longitude )
{
this.latitude = latitude;
this.longitude = longitude;
}
public GeoPoint( int latitudeE6, int longitudeE6 )
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
}

public GeoPoint( double latitude, double longitude, List<String> categories, Map<String, S


{
this.latitude = latitude;
this.longitude = longitude;
this.categories = categories;
this.metadata = metadata;
}

public GeoPoint( int latitudeE6, int longitudeE6, List<String> categories, Map<String, Str
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
this.categories = categories;
this.metadata = metadata;
}
public String getObjectId();
public double getLatitude();
public void setLatitude( double latitude );
public double getLongitude();
public void setLongitude( double longitude );

2015 Backendless Corp.

234

Backendless API for Android

public int getLatitudeE6();


public void setLatitudeE6( int latitudeE6 );
public int getLongitudeE6();
public void setLongitudeE6( int longitudeE6 );
public List<String> getCategories();
public void addCategory( String category );
public void setCategories( List<String> categories );
public
public
public
public
public
public

Map<String, String> getMetadata();


String getMetadata( String key );
void putMetadata( String key, String value );
void putAllMetadata( Map<String, String> metadata );
void clearMetadata();
void setMetadata( Map<String, String> metadata );

public Double getDistance();


public void setDistance( Double distance );
@Override
public boolean equals( Object o );
@Override
public int hashCode();
@Override
public String toString();
}

where the geo point object properties are:


objectId
- ID assigned to geo point by Backendless when it is saved in
the backend geo location storage.
latitude
- latitude of the geo point.
longitude
- longitude of the geo point.
categories
- an array of geo categories the point belong to.
metadata
- metadata associated with the geo point. Accepted values for
this parameter are: String, Number (integer and double), and
Data Service objects. Date values must be represented as
number in the Unix timestamp format (number of milliseconds
since January 1, 1970 at UTC). Learn more about using date in
search queries for category, radius, or rectangular area search.
distance
- distance between the center point referenced in the search
query and the geo point. The distance is always in units
specified in the search query.
Geo points returned from the search query are contained inside of BackendlessCollection .
Since the search query may produce a large number of geo points, not all of them are returned
at once. Instead, all found geo points are divided into 'pages'. The size of each page is
determined by the pageSize parameter in the query object. The first response returns the first
page. The collection class includes methods for loading additional pages.
BackendlessCollection also includes the total number of all geo points found by the search
operation (the totalObjects value).
All geo points in the entire search result are indexed. The index of the first geo point is 0. The
offset parameter in the query object and in some method of BackendlessCollection
specifies the index from which to load the next page of geo points. For example, suppose the
entire search result is 200 points (the totalObjects value returned in the collection is 200). If
the initial pageSize is 20, then only 20 geo points are returned in the first response. To get the
2015 Backendless Corp.

Geo Service

235

second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].
Below is a compressed version of the BackendlessCollection class. The full source code
listing is available in the Backendless github repository:
BackendlessCollection.java
package com.backendless;
import
import
import
import

com.backendless.async.callback.AsyncCallback;
com.backendless.exceptions.BackendlessException;
com.backendless.geo.BackendlessGeoQuery;
com.backendless.persistence.BackendlessDataQuery;

public class BackendlessCollection<E>


{
// returns the total number of geo points available for the search
query.
// This number may be different than the number of geo points currently
present
// in the collection as the collection contains only a 'page' of data,
that is
// a subset with the size defined by pageSize value in
BackendlessGeoQuery
// See the Running Search Queries section below for details.
public int getTotalObjects()
// Returns the current page - the collection of geo points returned
// for the latest search query request. The size of the page is
determined
// by the pageSize parameter in the query object.
public List<E> getCurrentPage()
// Sets the query object to modify pageSize and offset for the subsequent
// request to load additional pages of data.
public void setQuery( IBackendlessQuery query )
// Returns the query object which will drive the subsequent nextPage/
previousPage requests.
// This may not be the original geo query object as the underlying offset
value will change.
public IBackendlessQuery getQuery()
// Loads the next page using pageSize and offset from the query object.
public BackendlessCollection<E> nextPage() throws BackendlessException
// Same as above, but runs the operation asynchronously
public void nextPage( AsyncCallback<BackendlessCollection<E>> responder )
// Loads 'pageSize' number of geo points from the search result from the
specified offset (index).
public BackendlessCollection<E> getPage( int pageSize, int offset )
throws BackendlessException
// Same as above, but is executed asynchronously
public void getPage( int pageSize, int offset,
AsyncCallback<BackendlessCollection<E>> responder )

2015 Backendless Corp.

236

Backendless API for Android

// Loads previous using pageSize and offset from the current query
object.
public BackendlessCollection<E> previousPage() throws
BackendlessException
// Same as above, but is loaded asynchronously
public void previousPage( AsyncCallback<BackendlessCollection<E>>
responder )
}

Running Search Queries


The geo query object includes multiple parameters, however, only the coordinates defining the
rectangular area are required. A search query must be performed within at least one category. If no
category names are provided, the search is performed in the Default category.
Search in a rectangle in categories
Rectangle-based search establishes a geographic area by setting the coordinates of the North
West and South East corners of the area. Backendless searches for geo points in the specified
area and includes them into the search result:
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
geoQuery.setSearchRectangle( new double[32.78, -96.8, 25.79, -80.22] );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

Search in categories in a rectangular area and metadata


This is the same as above, with the difference that the search result includes only geo points
with the matching metadata:
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "Restaurants" );
HashMap<String, String> metaSearch = new HashMap<String, String>();
metaSearch.put( "Cuisine", "French" );
metaSearch.put( "Athmosphere", "Romantic" );
geoQuery.setMetadata( metaSearch );
geoQuery.setSearchRectangle( new double[32.78, -96.8, 25.79, -80.22] );
BackendlessCollection<GeoPoint> geoPoints = Backendless.Geo.getPoints(
geoQuery );

Search in rectangular area by date


The search query used to retrieve geo points may reference date values. These values must be
stored as a number of milliseconds since January 1st, 1970 at UTC. The example below
demonstrates the use of a date/time timestamp in a search query.
Using dates in where clause when searching in a rectangular area

The search query used to retrieve geo points may reference date values. These
values must be stored as a number of milliseconds since January 1st, 1970 at
UTC. The example below demonstrates the usage of a date/time timestamp in a
search query:
Search in rectangular area by date by using synchronous call:

2015 Backendless Corp.

Geo Service

237

public void searchByDateInRectangularAreaSync() throws ParseException


{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:mm"
);
Date opened = dateFormat.parse( "17.01.2015 at 07:00" );
Date closed = dateFormat.parse( "17.01.2015 at 23:00" );
Date now = dateFormat.parse( "17.01.2015 at 15:20" );
try
{
// create
GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );
geoPoint.addCategory( "Coffee" );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "opened", opened.getTime() );
geoPoint.addMetadata( "closed", closed.getTime() );
geoPoint = Backendless.Geo.savePoint( geoPoint );
System.out.println( String.format( "searchByDateInRectangularArea ->
point: %s", geoPoint ) );
// search
GeoPoint nortWestCorner = new GeoPoint( 21.306944 + 0.5, -157.858333 0.5 );
GeoPoint southEastCorner = new GeoPoint( 21.306944 - 0.5, -157.858333 +
0.5 );
BackendlessGeoQuery query = new BackendlessGeoQuery( nortWestCorner,
southEastCorner );
query.addCategory( "Coffee" );
query.setWhereClause( String.format( "opened < %d AND closed > %d", now.
getTime(), now.getTime() ) );
query.setIncludeMeta( true );
BackendlessCollection<GeoPoint> points = Backendless.Geo.getPoints( query
);
System.out.println( String.format( "searchByDateInRectangularArea
GETPOINTS: %s", points.getCurrentPage() ) );
}
catch( BackendlessException e )
{
System.err.println( String.format( "searchByDateInRectangularArea FAULT =
%s", e ) );
}
}

Search in rectangular area by date by using asynchronous call:


public void searchByDateInRectangularAreaAsync() throws ParseException
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:mm"
);
Date opened = dateFormat.parse( "17.01.2015 at 07:00" );
Date closed = dateFormat.parse( "17.01.2015 at 23:00" );
final Date now = dateFormat.parse( "17.01.2015 at 15:20" );
// create

2015 Backendless Corp.

238

Backendless API for Android

GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );


geoPoint.addCategory( "Coffee" );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "opened", opened.getTime() );
geoPoint.addMetadata( "closed", closed.getTime() );
Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
{
@Override
public void handleResponse( GeoPoint geoPoint )
{
System.out.println( String.format( "searchByDateInRectangularArea ->
point: %s", geoPoint ) );
// search
GeoPoint nortWestCorner = new GeoPoint( 21.306944 + 0.5, -157.858333 0.5 );
GeoPoint southEastCorner = new GeoPoint( 21.306944 - 0.5, -157.858333 +
0.5 );
BackendlessGeoQuery query = new BackendlessGeoQuery( nortWestCorner,
southEastCorner );
query.addCategory( "Coffee" );
query.setWhereClause( String.format( "opened < %d AND closed > %d",
now.getTime(), now.getTime() ) );
query.setIncludeMeta( true );
Backendless.Geo.getPoints( query, new
AsyncCallback<BackendlessCollection<GeoPoint>>()
{
@Override
public void handleResponse( BackendlessCollection<GeoPoint> points )
{
System.out.println( String.format( "searchByDateInRectangularArea
GETPOINTS: %s", points.getCurrentPage() ) );
}
@Override
public void handleFault( BackendlessFault fault )
{
System.err.println( String.format( "searchByDateInRectangularArea
FAULT = %s", fault ) );
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
System.err.println( String.format( "searchByDateInRectangularArea
SAVEPOINT: %s", fault ) );
}
} );
}

Requesting meta in response


Geo points returned in the search results do not include their metadata properties by default.
The search query object includes a property which can be used to request the metadata to be
included. This property can be used with any search options described above. The syntax for
requesting metadata in response is described in the Search in Category section.

2015 Backendless Corp.

Geo Service

5.15

239

Geo Point Clustering


Geo point search in a category, radius or a rectangular area may return too many geo points within
close geographic proximity from each other. This might be difficult to view and process in a client
application. To address this problem, Backendless supports the geo clustering feature. A geo cluster is
a group of several geo points located close to each other. The two screenshots below demonstrate the
advantages of clustering: the picture on the top displays search results in the Backendless Console with
clustering turned off and the one on the bottom displays search results as clusters when clustering has
been enabled:
Geo Points View:

Clusters and Points View:

Backendless creates clusters by splitting the map into a grid of squares. Geo points
which belong to a square are placed into the same cluster. When a square contains
only one point, it remains non-clustered.

Enabling Geo Clustering in Backendless Console


The Geolocation page displays non-clustered geo points by default. To enable clustering:
1. Log in to Backendless Console, select an application and click the Geolocation icon.
2. Click the Map-driven navigation toggle. The toggle changes how the geo points are loaded
from the backend. In the map-driven mode console loads the geo points for the rectangular
area shown in the map.

2015 Backendless Corp.

240

Backendless API for Android

3. Click the Geo Clustering toggle to enable clustering.

4. Console reloads geo points and clusters for the current viewport of the map and displays the
results. A cluster is visualized as a blue marker on the map with a number indicating how
many geo points it represents.

2015 Backendless Corp.

Geo Service

241

Geo clustering is also available with the "Search in Radius" option, which searches for geo points in a
circular area. To enable this functionality, click the Search in radius toggle:

If you want to see the geo points in a cluster, zoom in the map or double-click a cluster's marker.
Zooming the map in too much (when using the clustering along with the search in radius) may result that
the search radius will be much bigger than the visible part of the map on the screen. In this case, the
pop-up window will display offering you to resize (zoom out) the map. Clicking the Yes button zooms the
map out.

2015 Backendless Corp.

242

Backendless API for Android

Clicking a cluster's marker will reveal the coordinates and metadata of a cluster.

Retrieving Clustered Geo Points


Geo point clustering can be requested by setting clustering parameters in BackendlessGeoQuery,
which is used in the calls to retrieve geo points from a category, radius or rectangular area:

public void setClusteringParams(double westLongitude, double


eastLongitude, int mapWidth, int clusterGridSize )
// same as above, but uses the default clusterGridSize of 100

2015 Backendless Corp.

Geo Service

243

public void setClusteringParams(double westLongitude, double


eastLongitude, int mapWidth ).
where
westLongitude
eastLongitude
mapWidth
clusterGridSize

- the longitude of any point on the western boundary of the map


in degrees.
- the longitude of any point on the eastern boundary of the map in
degrees.
- the size of the viewing area of the map in pixels.
- the size in pixels of the grid's squares used to group points into
clusters. The default value is 100 pixels

Once the clustering parameters are set, the geo point search API will return clustered geo points. The
return value is a collection of GeoCluster and/or GeoPoint objects. Instances of the latter may be
returned when a geo point does not have any neighboring points within the grid's square it belongs to.
The GeoCluster class extends from GeoPoint and supports all the inherited properties: latitude,
longitude, categories and metadata. Additionally, geo cluster has its own property representing the
number of points the cluster consists of. Since a GeoCluster object an instance of the GeoPoint class,
the processing of search responses may look like this:
int mapWidth = 500;
double westLongitude = 23.123;
double eastLongitude = -80.238;
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "geoservice_sample" );
geoQuery.initClustering( westLongitude, eastLongitude, mapWidth );
BackendlessCollection<GeoPoint> points = Backendless.Geo.getPoints( geoQuery
);
Iterator<GeoPoint> iterator=points.getCurrentPage().iterator();
while( iterator.hasNext() )
{
GeoPoint geoPointOrCluster =iterator.next();
if( geoPointOrCluster instanceof GeoCluster )
{
GeoCluster geoCluster = (GeoCluster) geoPointOrCluster;
System.out.println( "Number of points in the cluster: " + geoCluster.
getTotalPoints() );
}
else
{
System.out.println( "Loaded geo point" );
}
System.out.println( "latitude - " + geoPointOrCluster.getLatitude() + ",
longitude - " + geoPointOrCluster.getLongitude() );
}

Loading the Geo Points from a Cluster


With the geo clustering feature enabled, you may need to reveal the geo points gathered in a cluster. In
the Backendless Console, you can click the cluster to reveal its details as described above. By using
the calls described below, you will be able to reveal the geo points in a cluster via API.
2015 Backendless Corp.

244

Backendless API for Android

Methods:
Revealing the geo points from a cluster is made via com.backendless.Geo class.
Synchronous method
public BackendlessCollection<GeoPoint> loadGeoPoints( final
GeoCluster geoCluster )
Asynchronous method
public void loadGeoPoints( final GeoCluster geoCluster, final
AsyncCallback<BackendlessCollection<GeoPoint>> responder )
Return Value:
A collection of GeoPoint objects that are included into a cluster. The GeoPoint class is defined
as:
GeoPoint.java

2015 Backendless Corp.

Geo Service

245

package com.backendless.geo;
import
import
import
import
import

java.io.Serializable;
java.util.ArrayList;
java.util.HashMap;
java.util.List;
java.util.Map;

public class GeoPoint implements Serializable


{
private static final long serialVersionUID = -4982310969493523406L;
private
private
private
private
private
private

String objectId;
double latitude;
double longitude;
List<String> categories;
Map<String, String> metadata;
Double distance;

private final static int multiplier = 1000000;


public GeoPoint()
{
}
public GeoPoint( double latitude, double longitude )
{
this.latitude = latitude;
this.longitude = longitude;
}
public GeoPoint( int latitudeE6, int longitudeE6 )
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
}

public GeoPoint( double latitude, double longitude, List<String> categories, Map<String, S


{
this.latitude = latitude;
this.longitude = longitude;
this.categories = categories;
this.metadata = metadata;
}

public GeoPoint( int latitudeE6, int longitudeE6, List<String> categories, Map<String, Str
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
this.categories = categories;
this.metadata = metadata;
}
public String getObjectId();
public double getLatitude();
public void setLatitude( double latitude );
public double getLongitude();
public void setLongitude( double longitude );

2015 Backendless Corp.

246

Backendless API for Android

public int getLatitudeE6();


public void setLatitudeE6( int latitudeE6 );
public int getLongitudeE6();
public void setLongitudeE6( int longitudeE6 );
public List<String> getCategories();
public void addCategory( String category );
public void setCategories( List<String> categories );
public
public
public
public
public
public

Map<String, String> getMetadata();


String getMetadata( String key );
void putMetadata( String key, String value );
void putAllMetadata( Map<String, String> metadata );
void clearMetadata();
void setMetadata( Map<String, String> metadata );

public Double getDistance();


public void setDistance( Double distance );
@Override
public boolean equals( Object o );
@Override
public int hashCode();
@Override
public String toString();
}

where the geo point object properties are:


objectId
- ID assigned to geo point by Backendless when it is saved in
the backend geo location storage.
latitude
- latitude of the geo point.
longitude
- longitude of the geo point.
categories
- an array of geo categories the point belong to.
metadata
- metadata associated with the geo point. Accepted values for
this parameter are: String, Number (integer and double), and
Data Service objects. Date values must be represented as
number in the Unix timestamp format (number of milliseconds
since January 1, 1970 at UTC). Learn more about using date in
search queries for category, radius, or rectangular area search.
Geo points returned from the search query are contained inside of BackendlessCollection .
Since the search query may produce a large number of geo points, not all of them are returned
at once. Instead, all found geo points are divided into 'pages'. The size of each page is
determined by the pageSize parameter in the query object. The first response returns the first
page. The collection class includes methods for loading additional pages.
BackendlessCollection also includes the total number of all geo points found by the search
operation (the totalObjects value).
All geo points in the entire search result are indexed. The index of the first geo point is 0. The
offset parameter in the query object and in some method of BackendlessCollection
specifies the index from which to load the next page of geo points. For example, suppose the
entire search result is 200 points (the totalObjects value returned in the collection is 200). If
the initial pageSize is 20, then only 20 geo points are returned in the first response. To get the
second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].
2015 Backendless Corp.

Geo Service

247

Below is a compressed version of the BackendlessCollection class. The full source code
listing is available in the Backendless github repository:
BackendlessCollection.java
package com.backendless;
import
import
import
import

com.backendless.async.callback.AsyncCallback;
com.backendless.exceptions.BackendlessException;
com.backendless.geo.BackendlessGeoQuery;
com.backendless.persistence.BackendlessDataQuery;

public class BackendlessCollection<E>


{
// returns the total number of geo points available for the search
query.
// This number may be different than the number of geo points currently
present
// in the collection as the collection contains only a 'page' of data,
that is
// a subset with the size defined by pageSize value in
BackendlessGeoQuery
// See the Running Search Queries section below for details.
public int getTotalObjects()
// Returns the current page - the collection of geo points returned
// for the latest search query request. The size of the page is
determined
// by the pageSize parameter in the query object.
public List<E> getCurrentPage()
// Sets the query object to modify pageSize and offset for the subsequent
// request to load additional pages of data.
public void setQuery( IBackendlessQuery query )
// Returns the query object which will drive the subsequent nextPage/
previousPage requests.
// This may not be the original geo query object as the underlying offset
value will change.
public IBackendlessQuery getQuery()
// Loads the next page using pageSize and offset from the query object.
public BackendlessCollection<E> nextPage() throws BackendlessException
// Same as above, but runs the operation asynchronously
public void nextPage( AsyncCallback<BackendlessCollection<E>> responder )
// Loads 'pageSize' number of geo points from the search result from the
specified offset (index).
public BackendlessCollection<E> getPage( int pageSize, int offset )
throws BackendlessException
// Same as above, but is executed asynchronously
public void getPage( int pageSize, int offset,
AsyncCallback<BackendlessCollection<E>> responder )
// Loads previous using pageSize and offset from the current query
object.

2015 Backendless Corp.

248

Backendless API for Android

public BackendlessCollection<E> previousPage() throws


BackendlessException
// Same as above, but is loaded asynchronously
public void previousPage( AsyncCallback<BackendlessCollection<E>>
responder )
}

5.16

Relations with Data Objects


Backendless Data Service manages application's data objects and provides APIs to
work with data objects. Backendless provides integration between data objects
managed by Data Service and geo points managed by Geo Service for the scenarios
when a logical connection between the two types must exist in an application. For
instance, in a taxi ordering app a data object may represent a taxi car, while a geo
point represents its location on the map. It is logical to link the two together so they
can be retrieved and managed at once.
The Geo-to-Data integration is implemented through geo point metadata. A metadata
property may reference one or more data objects. These relations may be created
using the API or with Backendless Console. Once a relation is established, the console
displays it in the Metadata column as a link to related data object(s). When a geo
point is retrieved using the API, any related data objects can be retrieved by using the
same principle for loading geo point metadata. The geo-to-data relation is bidirectoral,
that is, a data object may reference a geo point through object properties (table
columns). You can learn more about it in the Relations with Geo Points section of the Data
documentation.
Apart from linking with the data objects, you can also link a geo point with a user
object. Establishing relations with a user objects is performed the same way as with a
data object.

Establishing Relations with a Data Object via


Console
To link a geo point with a data object:
1. Click the Geolocation icon to open the Geo location screen.
2. Select a geo category to get a list of geo points from it.
3. Click the plus icon for the geo point you want to link with a data object.

2015 Backendless Corp.

Geo Service

249

4. The Add Related Object pop-up window will display.

5. Type in a metadata property name in the Metadata property name field. The new property
will be associated with the related data object.
6. Select a data table from from the Data table drop-down menu. If you want to establish
relation with a user object, select the Users option from the drop-down menu. A list of the
data objects which belong to the selected table will display.
7. Select the check-boxes for the data object(s) you want to link with the geo point.
8. Click the Add Related Objects button to establish a relation and save the changes.
Once the relation is established, the name of the property and the related data table will display next to
the corresponding geo point.

Updating Relations
2015 Backendless Corp.

250

Backendless API for Android

You can update a geo to data relation (for instance, add or remove objects to/from the relation) by
following the instructions below:
1. Select a geo category and locate the geo point.
2. To update a geo to data relation, click the link with the name of the data table in the geo point
metadata column.

3. To add a new geo-to-data metadata property, follow the instructions above.


4. For both scenarios (2) and (3), use the Add Related Object pop-up window to make the
necessary changes.
5. Click the Update Relation button to save the changes.

Deleting Relations
To delete a relation between a geo point and a data object:
1. Select a geo category and locate the geo point.
2. Click the link with the name of the data table in the geo point metadata column. The Add
Related Object pop-up window will display.

2015 Backendless Corp.

Geo Service

251

3. Un-check the check-boxes next to the data objects you want to unlink from the geo point.
4. Click the Update Relation button to save the changes.

Establishing a Geo to Data Relation by using API


Creating a relationship between a geo point and data objects uses the same API as saving a geo point
with metadata. The data object is also persisted in the Data Service.
The example below adds a geo point representing a location of a taxi cab. The geo point includes the
"TaxiCab" metadata property which references an object from the TaxiCab data table. This is an
example of a one-to-one relation (one geo point is related to one data object).
First, declare the TaxiCab class:
public class TaxiCab
{
public
public
public
public

String carmake;
String carmodel;
GeoPoint location;
List<GeoPoint> previousDropOffs;

public String getCarmake()


{
return carmake;
}
public void setCarmake( String carmake )
{
this.carmake = carmake;
}
public String getCarmodel()
{
return carmodel;
}
public void setCarmodel( String carmodel )

2015 Backendless Corp.

252

Backendless API for Android

{
this.carmodel = carmodel;
}
public GeoPoint getLocation()
{
return location;
}
public void setLocation( GeoPoint location )
{
this.location = location;
}
public List<GeoPoint> getPreviousDropOffs()
{
return previousDropOffs;
}
public void setPreviousDropOffs( List<GeoPoint> previousDropOffs )
{
this.previousDropOffs = previousDropOffs;
}
}

Create a one-to-one relation:


TaxiCab cab = new TaxiCab();
cab.carmake = "Ford";
cab.carmodel = "Crown Victoria";
GeoPoint pickupLocation = new GeoPoint( 40.750549, -73.994232 );
pickupLocation.addCategory( "Pickups" );
pickupLocation.addMetadata( "TaxiCab", cab );
Backendless.Geo.savePoint( pickupLocation );

Alternatively, create a one-to-many relation:


TaxiCab cab1 = new TaxiCab();
cab1.carmake = "Ford";
cab1.carmodel = "Crown Victoria";
TaxiCab cab2 = new TaxiCab();
cab2.carmake = "Toyota";
cab2.carmodel = "Prius";
List<TaxiCab> availableCabs = new ArrayList<TaxiCab>( );
availableCabs.add( cab1 );
availableCabs.add( cab2 );
GeoPoint pickupLocation1 = new GeoPoint( 40.750549, -73.994232 );
pickupLocation1.addCategory( "Pickups" );
pickupLocation1.addMetadata( "AvailableCabs", availableCabs );
Backendless.Geo.savePoint( pickupLocation1 );

5.17

Geofence Designer

About Geofencing
Geofencing on the surface, comprises drawing a stationary geometric boundary around an area on a

2015 Backendless Corp.

Geo Service

253

map. This action creates programmatically a set of simple or complex global coordinates which
represent a shape. A boundary represents a fence, which surrounds the area. For Backendless, the
boundary and area become meaningful when a Geopoint crosses the boundary or stays within the area.
Geofences work with Geopoints. A Geopoint is the most elementary Geolocation concept in
Backendless. It is a point (latitude and longitude coordinate pairs) on the map that is accessible via API
and allowed to move (change coordinates), i.e., a user carrying a mobile device. In addition to the
coordinates, the Geopoint includes metadata in context for the Geopoint.

The Geofence Designer


Geofence Designer is a feature of the Geolocation service available under Geolocation > Geofencing.
It is a design time tool for drawing Geofences on an interactive global map and associating them with
events and actions, which can be triggered based on the location of registered Geopoints. Backendless
integrates the designer with Google Maps, enabling the developer to design precise Geofence
positions, locations, and shapes.
The Design Tool includes line, rectangle, and circle drawing tools (the cursor changes from hand to
crosshairs when selected) for creating Geofence boundaries. Boundaries can be geometrically
symmetrical or irregular shapes and have no minimum or maximum size constraints. In use cases, a
Geofence conceptually fences in a city block, a shopping center, a sports stadium, or perhaps a mall;
even smaller geographic areas are possible such as the shoe department in a retail store.

The Line Tool


To define an irregular Geofence, the line tool draws editable line segments and control points. For
example, in the United States, the shape of Nevada is irregular. To create this shape, select the line tool
to start drawing lines around it.
Place the cursor on the map where the first control point should be and click. Drag the cursor to the next

2015 Backendless Corp.

254

Backendless API for Android

place and Click again. The first line segment appears. Repeat these steps until you have nearly
completed the shape of your boundary. (Its not a Geofence just yet.) Click the cursor on the last control
point (which was the first one set). Backendless detects a closed shape and enables a new Geofence.
NOTE: If you accidentally close the Geofence before completing the drawing, the New Geofence dialog
box appears. If you click Cancel, the Geofence will be removed. To keep the Geofence, click Save,
then re-edit the shape as needed.

Immediately after the shape closes, a popup appears prompting you to name the Geofence. Enter a
name in the Geofence Name text box. Since this example uses the state of Nevada, it makes sense to
name it Nevada. Click Save to enable the Geofence. (We will refer to this example again.)
The result is a new item row in the List of Geofences. The Geofence area is filled with green, and the
item row is highlighted in yellow when a Geofence is selected on the map or on the list. See the image
below.

Naming Geofences with intelligible


names is a good practice. Any

2015 Backendless Corp.

Geo Service

255

alphanumeric character or special


character is permitted. An unusual
scenario could be two cities with the
same name, but in different states,
such as Dallas, Texas and Dallas,
Illinois. A solution and a best practice
could be Dallas-Texas and DallasIllinois. This practice should apply to
all three tools.

The Rectangle Tool


The Rectangle Tool is self-describing. A fence can be drawn quickly around a square or rectangular area.
After the Geofence is named and saved, the shape aspect can be adjusted by dragging a line segment
or corner control point. Like the line tool, parameters can be entered in a dialog immediately after the
shape is drawn. Should you need to edit the shape, an undo tool appears and to restore the previous
edit.

The Circle Tool


The Circle Tool is also self-describing. A circle can be drawn quickly around an area, repositioned, and
resized. Like the line tool, parameters can be entered in a dialog immediately after the shape is drawn.

Deleting a Geofence
A Delete button is positioned directly below the interactive map. For each selected checkbox next to the
Geofence hyperlink, the Delete button removes those Geofences. Once a Geofence is deleted, it cannot
be restored.

2015 Backendless Corp.

256

Backendless API for Android

List of Geofences and Locator Tool


A map filled with Geofences can appear cluttered, especially if the design comprises numerous shapes
across several remotely located areas. Backendless organizes the boundary data and actions in a table
format below the map, the List of Geofences. The table contains a row for each Geofence along with
parameter controls.
From left to right, the second column shows the Geofence name, which is hyperlinked to the Update
Geofence dialog. The Geofence locator icon is next to the Geofence hyperlink (see image below). The
tool repositions the map view to an optimal zoom-level, from which the Geofence boundary can be
easily viewed, accessed, and edited.

For example, two cities in


Texas, Arlington and Plano,
have several Geofences in
each. In the image below the
map is tightly zoomed on an
area in Plano. The work on
Plano is done, so the
developer needs to reposition
the interactive map to
Arlington. Click the locator
icon next to an Arlington
Geofence. Backendless
repositions the map and sets
the optimal zoom level.
Further, it does not matter
whether the two Geofences
are on different continents;
the locator tool works the
same either way.

2015 Backendless Corp.

Geo Service

257

Geofence and Geopoints Events


Geofences and Geopoints are integrated entities of Geolocation. As such, Backendless tracks three
specific events:
When a Geopoint enters a Geofence, crosses the boundary, On Enter action executes.
When a Geopoint stays inside a shape for a preconfigured amount of time, On Stay actions
executes.
When a Geopoint exits the Geofence, it crosses the boundary and is outside the shape, On
Exit action executes.

Geopoint Qualification Criteria: Exclusion/Inclusion


Tracking every Geopoint within a Geofence is not desirable in every case. A Geofence plan could specify
tracking only Geopoints of a certain nature, for example, visitors or preferred customers.
Qualification Criteria, which identifies the types of Geopoints Backendless traces and tracks for a
specific geogence, can be defined in the Update Geofence dialog. The Geofence hyperlink opens the
dialog. A criterion, in this case, is a special string entered in the Geopoint Qualification Criteria text box.
(The string format is the SQL 92 syntax, regular SQL as relates to a database query WHERE clause.)
For example, if tracking only visitors, the SQL would need something like usertype=visitor . Where
usertype is a Geopoints metadata property. visitor is the metadata property value.
The string can be tested via the Validate button. Upon a successful validation, a success message
displays.

Conversely, Backendless could track only employees, excluding visitors, and function as a time clock
action, such as clock-in and clock-out.

Events and Actions


Detecting, tracing, and tracking geopoints in relationship to a geofence establish the event clockwork for
execution of developer defined actions. An event occurs as a Geopoint transports into and out of or
stays in a Geofence. An action is a set of parameters the developer selects to perform a function, such
as deliver a message to a mobile device or add a record to a database.
Events

2015 Backendless Corp.

258

Backendless API for Android

Three event types are organized in columns in the List of Geofences. The events types are:
On Enter a Geopoint crosses the Geofence boundary into the defined area
On Stay a Geopoint remains in the Geofence area for at least a specified period
On Exit a Geopoint crosses the Geofence boundary out of the defined area
Actions
For each of the above events, a developer can select an action and specify parameters to be executed
from Backendless. There are four action types:
Push notification
Publish-subscribe (pub/sub) message
Send a custom event
Call a URL
The scenarios for choosing an action are wildly different; however, they drive the action and parameter
choices the developer makes. When an action type is selected for an event, a dialog appears where
action parameters can be entered. The fields in the dialog are specific to the action type.

Whenever an action is configured, visual elements indicate whether the parameters are complete. A gear
icon and green checkmark indicate proper configuration, where as a red X in place of the checkmark
indicates improper configuration. The configuration can be edited by clicking the gear to reopen the
currently assigned action dialog.
Push Notification Action
All three Geofence events can trigger this action. Push Notification, in basic form, is a message sent to
a mobile device associated with a Geopoint or to a group of devices registered with a channel. The
Configure Push Notification Action dialog provides flexible parameter options:
Content Configuration configure Push Notification content look and feel for Android, iOS, or
Windows Phone.
Message Headers allows header name and header value.
Delivery to individual Geopoints or those registered to a channel.

2015 Backendless Corp.

Geo Service

259

Send a Pub/Sub Message Action


This action sends a publish/subscribe message. The developer enters the message parameters in the
Configure Pub/Sub Message Action dialog. (Learn more about Message Publishing.) The dialog contains
the following fields:
Channel name the name for a channel. Backendless creates the channel if it doesnt exist.
Topic name the name of a topic used for filtered delivery.
Message headers optional. Use the key=value format. Comma delimited.
Message body written in JSON. The body of the message to be delivered.

2015 Backendless Corp.

260

Backendless API for Android

A probation officer issues an


ankle bracelet to a probationer.
He needs to set area from which
the probationer cannot leave,
such as restricted to travel only
within a state. The PO would set
up a Geofence outlining
Colorados state-line borders. For
that Geofence, he would set the
action event On Exit to send a
notification to the application on
his device that would provide
metadata about the probationer
such as location, the
probationers photo, phone
number, address, etc.
Call URL Action
This action executes an HTTP command on the specified URL. Supported commands are GET, POST,
or PUT. The developer configures the call in the Configure Call URL Action dialog. The dialog contains
the following fields:
Command choice of GET, POST, or PUT
URL a fully formed internet protocol URL.
Request headers any of the HTTP header types in the form of key=value.

2015 Backendless Corp.

Geo Service

261

Body text message for calling the URL.

Send Custom Event


Provides a means for enabling Geofencing processing logic to execute your custom server-side code
deployed to Backendless. Send Custom Event issues an event that you define in the Send Custom
Event dialog box. See Custom Events under Business Logic. Backendless acts as a functional
intermediary for your custom event handler which contains business logic specific to the application
such as sending out an email or saving a record to a database.

2015 Backendless Corp.

262

Backendless API for Android

Is Active - Geofence Monitoring


Is Active is the final column in the List of Geofences. It indicates whether server-side Geofence
monitoring can be activated. When monitoring is ON, Backendless tracks any movements of the
Geopoints in relation to the corresponding Geofence. The list below shows item content:
Missing Actions look for red X next to the edit action gear. An action is improperly configured.
ON/OFF Toggle click to switch either on or off. ON activates the server-side monitoring for the
selected Geofence.
When an action is properly configured, i.e. complete, the Is Active toggle for the selected Geofence
appears. When set to ON, a popup provides cautionary information and a checkbox option, which
applies actions to Geopoints located within the Geofence at the time when the monitoring is turned on (i.
e. the toggle is being set to ON). See the image below.

2015 Backendless Corp.

Geo Service

263

Once server-side monitoring is activated by setting the Is Active toggle to ON, a play button appears
next to the gear icon. This button executes the action on-demand for any Geopoints within the
Geofence. (This function can be useful when debugging.)

View Geopoints in a Geofence


For any Geofence that is currently active (i.e. is under server-side monitoring), a checkbox option
displays Geopoints located within the Geofence. Geopoints located within the Geofence are
represented by a marker for Google Maps inside the Geofence.
The frequency of refresh on the screen is controlled by the refresh interval. The value is in seconds and
can be set to a value from 10 to 300. You can force a refresh by clicking the refresh button. See the
image below.

5.18

Geofence API
Client-side geofence monitoring is the process of tracking the position of the device in relation to the
geofences defined in Backendless. Functionality in Backendless client libraries loads information about

2015 Backendless Corp.

264

Backendless API for Android

geofences (or a specific geofence) and tracks device positional changes. When the device crosses,
stays in, or exits a geofence boundary, the Backendless client executes a callback. Based on this,
Backendless client-side monitoring supports the following options:
In-app callback for the on enter, on stay and on exit events. A callback is executed on the
device when it's location enters, stays in or exits a geofence. With this approach the client
application decides how to handle a geofence event.
In-app callback interface
Client applications must use a special interface to receive local callbacks. The
Backendless library invokes the interface methods when the current device's location
enters, stays in or exits a geofence:
Consider the following example of the in-app callback interface implementation. The
example creates and saves a geopoint with dummy coordinates (0,0). As the device
changes its location, the in-app callback is executed and the code saves the device's
location on the server. See the example below.
Remote callback for the on enter, on stay and on exit event. The Backendless client
automatically notifies the server when a geofence event occurs. If there is an action associated
with the event, Backendless executes on the server-side.
The API applies the options above either to a specific or all geofences. It is important to note that a
Backendless client can monitor only the geofences which are not activated for the server-side
monitoring. In other words, a geofence with the "Is Active toggle in the Backendless console set to ON
cannot be tracked on the client-side.

Android App Configuration


An Android application which uses the Backendless Geofence API must include the following
permission in the Android app manifest:
<uses-permission android:name="android.permission.
ACCESS_FINE_LOCATION" />
To enable location tracking while app is running in background, add the following manifest:
<service android:name="com.backendless.geo.LocationTracker"/>
In order to enable location tracking after device reboot (or power cycle), the following must be added to
application's manifest:
<uses-permission android:name="android.permission.
RECEIVE_BOOT_COMPLETED" />
<receiver android:name="com.backendless.AutoStart" >
<intent-filter>
<action android:name="android.intent.action.
BOOT_COMPLETED"/>
</intent-filter>
</receiver>

Geofence APIs
The Geolocation service provides the following APIs:
Client-side location monitoring:
Start location monitoring for a specific geofence with an in-app callback

2015 Backendless Corp.

Geo Service

265

Start location monitoring for all geofences with an in-app callback


Start location monitoring for a specific geofence with a remote callback
Start location monitoring for all geofences with a remote callback
Stop location monitoring for a specific geofence
Stop location monitoring for all geofences
Executing a geofence action:
Run the OnEnter action
Run the OnStay action
Run the OnExit action
Retrieve geopoints from a geofence

Start location monitoring for a specific geofence with an inapp callback


Starts client-side monitoring of the device's location for the geofence. Uses the callback to notify when
the device enters, stays in, or exits the geofence boundary.

where:
geofenceName
callback
responder

- name of a geofence, which monitors the device's location.


- a callback object that the Backendless library notifies when the
device crosses, stays in, or exits the geofence boundary.
- a responder that notifies the calling program when the operation
started successfully geofence monitoring or resulted in error.

Example:

Start location monitoring for all geofences with an in-app


callback
Starts client-side monitoring of the device's location for all geofences. Uses the callback to notify when
the device enters, stays in or exits a geofence boundary.
where:
callback
responder

- a callback object that the Backendless library notifies when the


device crosses, stays in, or exits the geofence boundary.
- a responder that notifies the calling program when the operation
started successfully geofence monitoring or resulted in error.

Example:

Start location monitoring for a specific geofence with a


remote callback
Starts client-side monitoring of the device's location for a specific geofence. This function notifies the
server when the device enters, stays in, or exits the geofence boundary.
where:
geofenceName
geoPoint

2015 Backendless Corp.

- name of a geofence, which monitors the device's location.


- the geopoint object to pass to the server. The geopoint
represents the current device location. It may be a geopoint

266

Backendless API for Android

responder

stored in the Geolocation storage (with objectId assigned to it)


or a new geopoint object. The Backendless client assigns the
current location coordinates to the geoPoint object before
sending it to the server.
- a responder that notifies the calling program when the operation
has started successfully geofence monitoring or has resulted in
error.

Example:

Start location monitoring for all geofences with a remote


callback
Starts client-side monitoring of the device's location for all geofences. This function notifies the server
when the device enters, stays in or exits a geofence boundary.

where:
geoPoint

responder

- the geopoint object to pass to the server. The geopoint


represents the current device location. It may be a geopoint
stored in the Geolocation storage (with objectId assigned to it)
or a new geopoint object. The Backendless client assigns
current location coordinates to the geoPoint object before
sending it to the server.
- a responder used for to notify the calling program when the
operation has either successfully started geofence monitoring or
resulted in error.

Example:

Stop location monitoring for a specific geofence


Stops client-side monitoring of the device's location for the geofence.

where:
geofenceName

- name of the geofence for which device location monitoring will


stop.

Example:

Stop location monitoring for all geofences


Stops client-side monitoring of device locations for all geofences.

Example:

Configure Location Tracker


Configures client-side location tracker

2015 Backendless Corp.

Geo Service

267

where:
- minimum time in milliseconds as an
interval between location updates;
minDistance
- minimum distance in meters as a difference
between locations; if distance is less than
this value, android service will not send new
coordinates to app;
acceptedDistanceAfterReboot
- max distance between two locations after
rebooting the device; if distance is greater
than this value, service resets location status
and starts fence(s) monitoring from the
beginning. Used when user reboots the
device.
minTime

Example:

Run the OnEnter Action


Requests the server to run the configured OnEnter action for a geofence either for all geopoints located
within the geofence or for the specified geopoint.
Synchronous methods:
Asynchronous methods:

where:
geofenceName
geoPoint

callback

- name of the geofence on which the OnEnter action will run.


- a geopoint which will be used as the context for the action
execution. Any substitutions which the action may be configured
with will be resolved against the geopoint.
- a responder that notifies the calling program when the operation
has requested successfully the action to be executed or resulted
in error.

Example:

Run the OnStay Action


Requests the server to run the configured OnStay action for a geofence either for all geopoints located
within the geofence or for the specified geopoint.
Synchronous methods:
Asynchronous methods:

where:
geofenceName
geoPoint

callback

2015 Backendless Corp.

- name of a geofence on which the OnStay action will run.


- a geopoint which will be used as the context for the action
execution. Any substitutions which the action may be configured
with will be resolved against the geopoint.
- a responder that notifies the calling program when the operation

268

Backendless API for Android

has requested successfully the action to be executed or resulted


in error.
Example:

Run the OnExit Action


Requests the server to run the configured OnExit action for a geofence either for all geopoints located
within the geofence or for the specified geopoint.
Synchronous methods:
Asynchronous methods:

where:
geofenceName
geoPoint

callback

- name of a geofence on which the OnExit action will run.


- a geopoint which will be used as the context for the action
execution. Any substitutions which the action may be configured
with will be resolved against the geopoint.
- a responder that notifies the calling program when the operation
has requested successfully the action to be executed or resulted
in error.

Example:

Retrieve Geopoints from a Geofence


Retrieves a collection of geopoints currently located within a geofence
Synchronous method:
Asynchronous method:

where:
geofenceName
query

callback

- name of a geofence to retrieve the geopoints from.


- a BackendlessGeoQuery object controlling various aspects of
geopoint retrieval such as paging, inclusion of metadata and/or
SQL search query.
- a responder that notifies the calling program when the operation
has successfully retrieved geopoints or resulted in error.

Example:
The example loads geopoints from a geofence called "Manhattan". The returned geopoints will have
metadata properties and will match the SQL query:

2015 Backendless Corp.

Index

Index
-Bbackendless.jar

6, 131, 175, 203

-IIdentity

11

-LLogin
17
Logout
28

-PPassword
property
recovery

11
29

-UUser Properties
defining with API 11
defining with console
11
retrieve user entity description
13
update user registration
25
User Registration
API call
14
disable registration
14
email confirmation
14
registration with external system
14

2015 Backendless Corp.

269

270

Backendless API for Android

Endnotes 2... (after index)

2015 Backendless Corp.

Back Cover

You might also like