Professional Documents
Culture Documents
Cloud store and query users, data objects, and files in your
Android applications
C. Enrique Ortiz
Mobile Technologist
Independent
Explore the advantages of storing mobile application data in a private cloud with
this introduction to the Parse SDK for Android. Mobilist C. Enrique Ortiz introduces
the Parse API classes for cloud-storing and manipulating users, data objects, and
files for your mobile applications.
The Parse mobile SDK provides cloud-based APIs and services for iOS, Android,
and Windows applications. Parse SDK also provides a JavaScript and REST
APIs. Using the Parse API, you can cloud-enable your mobile applications very
quickly and with minimal effort. A mobile application that is integrated with the Parse
API can easily store data objects and files on the Parse cloud, send and listen to
push notifications, manage users, handle geo-location data, and use social media
platforms such as Twitter and Facebook. For mobile applications that need to scale,
the Parse SDK offers all the elasticity of a cloud platform.
Before you start
I assume for the purposes of this article that you are already familiar with
the basic concepts required to program mobile apps with JSON, Android,
and Eclipse. Before you read any further, go to Parse.com and define your
application. Just follow the simple instructions starting from the sign-up
page.
This article introduces the core Parse API classes for Parse users, data objects, and
files. You'll learn how to work with access control lists (ACLs) and how to perform
CRUD operations on data objects, and how to store and retrieve files in the Parse
cloud. Examples are built on the Parse SDK for Android (see Resources).
Trademarks
Page 1 of 16
developerWorks
ibm.com/developerWorks/
notifications. Application keys and settings are managed via the dashboard. The
dashboard also provides a data browser that allows developers to browse and even
edit stored Parse objects. The data browser is very useful for debugging. Figure 1 is
a screenshot of the Parse dashboard:
Figure 1. The Parse dashboard
Applications are authenticated via an application ID and a client ID. To get your
application and client IDs, you must register your application via the Parse
dashboard. You will use these keys when initializing the Parse library on your app.
Page 2 of 16
ibm.com/developerWorks/
developerWorks
The code in Listing 2 creates a ParseObject which is stored as an object in the Parse
cloud. Many MyObjects of the same class are then stored as rows of ParseObject
data objects that can be saved, queried for, updated, and deleted from Parse's
cloud storage. It's even possible to save data when the application is offline the
Parse library simply saves the data locally until a network connection has been reestablished.
Modifying a ParseObject
If you are familiar with mobile application development, then you know that long
operations such as network operations should typically be done in the background,
on a worker thread and not on the main system UI thread. This keeps the main
system thread from blocking and affecting the responsiveness of your user interface.
Later in the article, I'll show you how Parse facilities work in the background to save,
delete, and find objects. For now, consider the following synchronous remove()
method, which is used to remove a key from the Parse object:
pObject.remove("myNumber"); // remove the field/key "myNumber" from pObject
Page 3 of 16
developerWorks
ibm.com/developerWorks/
After removing or adding fields, or updating a current field, you can save (or update)
the data object on the cloud by calling one of the ParseObject's save...() methods,
which I discuss later in the article.
Summary: ParseObject
ParseObject represents a data object on the Parse cloud. It provides
methods to add name-value pairs, test whether a given key is present, and
delete or fetch a given ParseObject from the server. ParseObject also
lets you use various get...() and put...() methods to manipulate
ParseObject data, merge ParseObjects, save a ParseObject in the
server, and more.
Page 4 of 16
ibm.com/developerWorks/
developerWorks
The username and email must be unique. If a username or email is already in use,
the sign-up call will fail. You should have a mechanism to notify the user if either field
fails, as well as a process for trying again.
After signup, users can log in to your app, as shown in Listing 4:
Listing 4. ParseUser log in
ParseUser.logInInBackground("eortiz", "123456", new LogInCallback() {
public void done(ParseUser user, ParseException e) {
if (user != null) {
// Successful. Allow access to app.
} else {
// Failed
}
}
});
You can update user information by calling ParseUser.save(). Note, however, that
only the owner of ParseUser can modify its content; for everyone else, the data is
read-only.
Parse caches the currently logged-in user. You can query the current user by calling
ParseUser.currentUser(). The currentUser method allows you to quickly access
current user information, so that you need only prompt for credentials if the current
user session is not active. Listing 5 show how to retrieve a current user in Parse:
Listing 5. ParseUser getting the current user
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
// current user is valid
} else {
// current user not valid, ask for credentials
}
Parse ACLs
An ACL is a list of access permissions (or controls) that are associated to a
data object. The ParseACL class allows you to define the permissions for a given
ParseObject. With ACLs, you can define public access to your application data
objects and you can limit access to specific users or groups of users (via roles).
Listing 7 demonstrates the use of Parse ACLs:
Listing 7. Using ParseACL for access permissions
// Define a Parse user
ParseUser user = new ParseUser();
Page 5 of 16
developerWorks
ibm.com/developerWorks/
user.setUsername(username);
:
:
// Define a read/write ACL
ParseACL rwACL = new ParseACL();
rwACL.setReadAccess(user, true); // allow user to do reads
rwACL.setWriteAccess(user, true); // allow user to do writes
:
:
// Define a Parse object and its ACL
ParseObject gameObject = new ParseObject("Game");
gameObject.setACL(rwACL); // allow user do read/writes on gameObject
gameObject.saveInBackground(); // save the ACL'ed object to the cloud
:
:
// You can define a public ACL that gives public access to the object
ParseACL publicACL = new ParseACL();
publicACL.setPublicReadAccess(true);
publicACL.setPublicWriteAccess(true);
gameObject.setACL(publicACL); // allow public read/writes
gameObject.saveInBackground(); // save the ACL'ed object to the cloud
You can also define a default ACL for all newly created objects. In Listing 8 I have set
the default ACL to be public for reads and writes, as defined by publicACL in Listing
7.
Listing 8. Setting default ACL
// Set a default ACL for all newly created objects as public access
ParseACL.setDefaultACL(publicACL, true);
While not shown here, we could also use the ParseRole class to grant access
permissions to groups of users.
Next, we'll look at how Parse data objects are saved to and retrieved from the Parse
cloud.
Page 6 of 16
ibm.com/developerWorks/
developerWorks
aware that it is your responsibility to call this method on its own worker thread to
prevent the UI from blocking.
Listing 9 shows the code to save a Parse data object in the background:
Listing 9. Save ParseObject in the background
// ParseObject
ParseObject pObject = new ParseObject("ExampleObject");
pObject.put("myNumber", number);
pObject.put("myString", name);
pObject.saveInBackground(); // asynchronous, no callback
And Listing 10 shows the code for saving a Parse data object the background with a
callback:
Listing 10. Save in the background with callback
pObject.saveInBackground(new SaveCallback () {
@Override
public void done(ParseException ex) {
if (ex == null) {
isSaved = true;
} else {
// Failed
isSaved = false;
}
}
});
Page 7 of 16
developerWorks
ibm.com/developerWorks/
or more Parse objects using attributes. If you already have a ParseObject, you can
refresh or synchronize its contents by fetching the latest values from the server. We'll
look at all of these options in the following code snips.
Fetching ParseObjects
To fetch a data object from the Parse cloud, use the ParseObject method fetch() or
fetchInBackground(), shown in Listing 11:
Listing 11. Fetching (unconditional)
// ParseObject
ParseObject pObject = new ParseObject("ExampleObject");
:
:
// Fetch the parse object unconditionally
try {
pObject.fetch();
} catch (ParseException e) {
e.printStackTrace();
}
// Fetch the parse object unconditionally, with Callback
pObject.fetchInBackground(new GetCallback() {
@Override
public void done(ParseObject obj, ParseException ex) {
if (ex == null) {
// Success
} else {
// Failed
}
}
});
You can also fetch only if needed; for example, when fetching a Parse object that has
related Parse objects, as in Listing 12:
Listing 12. Fetching as needed
ParseObject po = new ParseObject("ExampleObject");
:
ParseObject po2 = po.getParseObject("key");
// Fetch only if needed
try {
po2.fetchIfNeeded();
} catch (ParseException e) {
e.printStackTrace();
}
Page 8 of 16
ibm.com/developerWorks/
developerWorks
this; each one takes for input a list of Parse objects and then returns a list of Parse
objects:
fetchAll(List<ParseObject> objects)
fetchAllIfNeeded(List<ParseObject> objects)
fetchAllIfNeededInBackground(List<ParseObject> objects, FindCallback
callback)
fetchAllInBackground(List<ParseObject> objects, FindCallback callback)
In Listing 15 I've used a query constraint to retrieve one or more matching Parse
objects; in this case active Players:
Parse cloud-based services for Android apps
Page 9 of 16
developerWorks
ibm.com/developerWorks/
examples:
whereMatches(String key, String regex) finds string values that match the
provided regular expression.
whereStartsWith(String key, String prefix) finds string values that start
with a provided string.
whereContains(String key, String substring) finds values that contain a
provided string.
whereGreaterThan(String key, Object value) finds values that are greater
than the provided value.
whereWithinKilometers(String key, ParseGeoPoint point, double
maxDistance) finds objects with point values near the point given and within the
maximum distance given.
Query results can be ordered, as shown in Listing 17. To order query results, call one
of the query orderBy...() methods, specifying the field to order by.
Parse cloud-based services for Android apps
Page 10 of 16
ibm.com/developerWorks/
developerWorks
For example:
Something else to note is that ParseQuery results are cached. You can set the querycache policy in various ways depending on your application needs. The following
cache policies are currently supported:
IGNORE_CACHE: Do not use the cache; this is the default cache policy.
CACHE_ONLY: Only load from the cache. If there are no cached results, there will
be a ParseException.
NETWORK_ONLY: Always go to the network, but save results to the cache.
CACHE_ELSE_NETWORK: Hit the cache first. If that fails, load from the network. If
neither cache nor network succeed, the result will be a ParseException.
NETWORK_ELSE_CACHE: Hit the network first. If that fails, load from the cache. If
neither network nor cache succeed, the result will be a ParseException.
CACHE_THEN_NETWORK: Hit the cache first, then load from the network. Note that
the FindCallback will actually be called twice: first with the cached results, then
with the network results. This policy can only be used asynchronously with
findInBackground.
Setting the cache policy is simple. Do it before calling the find...() method, as
shown in Listing 18:
Page 11 of 16
developerWorks
ibm.com/developerWorks/
The ParseQuery class provides all the methods needed to query for data objects that
are stored on the cloud. With ParseQuery, you can specify query constraints of all
kinds, count matching data objects, set limits, skip data objects, order by, clear the
cache, and much more.
Page 12 of 16
ibm.com/developerWorks/
developerWorks
File data is represented by the byte[] syntax. Note that currently, a given file cannot
be larger than 10MB. When naming a file, the Parse library takes care of potential
name collisions. Providing a file extension helps Parse handle file content.
Listing 20 demonstrates saving a JPG file:
Listing 20. Saving a ParseFile
// Save image file
Drawable drawable = ...;
Bitmap bitmap = (Bitmap)((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] data = stream.toByteArray();
ParseFile imageFile = new ParseFile("image.jpg", data);
imageFile.saveInBackground();
The initial statements in Listing 20 convert the bitmap into a byte[]. The byte[]
is then saved using a ParseFile saveInBackground() method, similar to how a
ParseObject is saved on the server.
Once the file has been saved on Parse, it must be associated with (put into) a
ParseOject. In other words, Parse files are not true standalone objects and to be
retrieved and used later on, it must be associated with a given ParseObject instance.
This limitation might be addressed on a future release of Parse. Listing 21 associates
the image file with a Player Parse object:
Listing 21. Associating a ParseFile with a ParseObject
// Associate image with Parse object
ParseObject po = new ParseObject("Players");
po.put("name", "eortiz");
po.put("photo", imageFile);
po.saveInBackground();
I have associated the file with the data object, then saved the object on the server
using saveInBackgroud(), which I previously discussed.
Listing 22 shows how to retrieve a file that is associated with a data object:
Listing 22. Retrieving the ParseFile
// Retrieving the file
ParseFile imageFile2 = (ParseFile)po.get("photo");
imageFile2.getDataInBackground(new GetDataCallback() {
public void done(byte[] data, ParseException e) {
if (data != null) {
// Success; data has the file
} else {
// Failed
}
}
});
Page 13 of 16
developerWorks
ibm.com/developerWorks/
In conclusion
The Parse API is very comprehensive and includes classes for accessing mobile
services such as push notification, using geo-data, integrating with social media
platforms, and more. In this article, I've scratched the surface of what you can do with
Parse by introducing the Parse APIs for data and file cloud storage. Knowing how to
store and manipulate Parse users, data objects, files, and ACLs in the Parse cloud is
good foundation for further exploring this cloud platform for mobile development.
Acknowledgments
Many thanks to Athen O'Shea for his review of this article.
Page 14 of 16
ibm.com/developerWorks/
developerWorks
Resources
Learn more about the Parse Android SDK; also see the Parse Quick Guide to
choose a mobile platform, set up an app, and download and install your Parse
SDK.
View a complete listing of the Parse Android APIs.
"Develop Android applications with Eclipse (Frank Ableson, developerWorks,
February 2008): Get more practice developing Android apps in the Eclipse
development environment, this time using the Android Eclipse plug-in.
"Introduction to jQuery Mobile" (C. Enrique Ortiz, developerWorks, May 2012):
Learn the basics of jQuery Mobile and how to write a functional mobile web
application user interface. Working examples guide you through pages,
navigation, toolbars, list views, form controls, and transition effects in jQuery
Mobile.
"Solve your many-device-to-many-platform mobile application integration
challenges" (Olivier Picciotto, developerWorks, August 2012): Mobile
development and cloud computing are practically inseparable these days, but
integrating mobile applications into the cloud is still new territory. Learn how the
mobile enterprise application platform (MEAP) solves some mobile-to-cloud
integration challenges.
"DevOps for mobile development" (Michael Rowe, developerWorks, July 2012):
Companies all over the world want to exploit the mobile market by providing
customers and users with apps that make mobile computing easier. This
article thinks through some of the technical and business issues involved with
integrating development and operations for mobile platforms in the workplace.
Follow developerWorks on Twitter.
Watch developerWorks on-demand demos ranging from product installation
and setup demos for beginners, to advanced functionality for experienced
developers.
Get involved in the developerWorks community. Connect with other
developerWorks users while exploring the developer-driven blogs, forums,
groups, and wikis.
Page 15 of 16
developerWorks
ibm.com/developerWorks/
Page 16 of 16