Handling Offline Capability and Data Sync In An Android App - Part 1

Published Date
30 - Apr - 2015
| Last Updated
30 - Apr - 2015
 
Handling Offline Capability and Data Sync In An Android App – Par...

Abstract

Mobile apps with a backend need to provide offline capability as the devices may not have continuous network access. We also need an efficient way for our app to automatically sync with a backend server. In this article we will take a look at how Android* sync adapter framework can be leveraged for a restaurant sample app to enable seamless data sync. We will discuss how to provide offline capability using content providers and local SQLite database. This will be a 2-part series; part 1 will cover the usage of content provider APIs with a local SQLite database.

Contents

Abstract. 
Overview... 
A Retail Business Restaurant Sample App – Little Chef.
Using a Content Provider with local SQLite database. 
Implementing the RestaurantContentProvider 
Accessing RestaurantContentProvider using ContentResolver 
Adding RestaurantContentProvider to app Manifest. 

Overview

Android* apps that rely on a backend server for data or content need to provide offline capability for a seamless user experience. This requires us to maintain a local copy of the data model. We also need an efficient way to keep the local data model insync with the one on the backend server.  We can achieve both of these features using the standard Android APIs.

Content provider APIs can be used to abstract away the data model, and using SQLite APIs we can maintain a device resident SQLite database for a local copy of the data. For efficient data sync with the server, Android provides sync adapter framework APIs to automatically handle network connection drops, background syncing, and scheduling.

Additionally, we can hook up the sync adapter framework to the content provider, enabling us to reuse the same data abstraction for in-app use and background sync. Please refer to the following link for more details on the sync adapter framework in Android.

http://developer.Android.com/training/sync-adapters/index.html
In this 2-part series, we will take a look at how these APIs were used in a sample restaurant Android app, providing a seamless user experience. Part 1 will cover using content provider APIs with a local SQLite database.

 A Retail Business Restaurant Sample App – Little Chef.

“Little Chef” is a sample restaurant app with several features including menu content, loyalty club, and location based services among others. The app uses a backend server for getting the latest menu content and updates. The backend database can be updated using a web frontend.
We will use a local SQLite database to mirror the data model needed for the app. Content providers are used so we can enable search suggestions, or in the future share menu content with other Android apps. Android sync adapter framework is used to keep the data between the app and backend in sync.

In the next few sections, we will look at how these features are implemented in this sample app.

Using a Content Provider with a Local SQLite Database

 Previously, we discussed how to use a local SQLite database with the Little Chef sample app. Please see the following link for reference.
https://software.intel.com/en-us/Android/articles/using-a-database-with-your-Android-app
Android content provider APIs can help us with new use cases like sharing the menu content with other apps on the device and offering custom search and/or advanced copy/paste operations on the device.

Once implemented, content providers can be used as a generic data abstraction along with other Android APIs like UI adapters or sync adapters, for example.

For a comprehensive reference on creating and using content providers please see the official reference at the following link.
http://developer.Android.com/guide/topics/providers/content-providers.html
For the sample app we will use a content provider as the front-end for a local SQLite database.
For the sample app we will use a content provider as the front-end for a local SQLite database.
Previously, the sample app was directly accessing the menu items from a local SQLite database using the getMenuItems method. We can wrap around this function with a simple content provider.

Implementing the RestaurantContentProvider

To implement a content provider in Android, we need to create a class that extends from abstract class Content Provider and implement its 6 required methods - query(), insert(), update(), delete() getType(), onCreate(). 

01 public class RestaurantContentProvider extends ContentProvider {

02 private static final String AUTHORITY = "com.example.restaurant.provider";

03 private static final int MENU = 1;

04 private static final int MENU_ID = 2;

05 private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);

06 static {

07 sURIMatcher.addURI(AUTHORITY, "menu", MENU);

08 sURIMatcher.addURI(AUTHORITY, "menu/#", MENU_ID);

09 }

10
 

11 public static final Uri MENU_URI = Uri.parse("content://" + AUTHORITY + "/" + "menu");

12 private RestaurantDatabase mDb;

13
 

14 @Override

15 public boolean onCreate() {

16 mDb = new RestaurantDatabase(getContext());

17 return true;

18 }

19
 

20 @Override

21 public String getType(Uri uri) {

22 switch (sURIMatcher.match(uri)) {

23 case MENU:

24 return "vnd.Android.cursor.dir/vnd.com.example.restaurant.provider.menu";

25 case MENU_ID:

26 return "vnd.Android.cursor.dir/vnd.com.example.restaurant.provider.menu";

27 default:

28 throw new RuntimeException("getType No URI Match: " + uri);

29 }

30 }

31
 

32 @Override

33 public Cursor query(Uri uri, String[] projection, String selection,

34 String[] selectionArgs, String sortOrder) {

35
 

36 switch (sURIMatcher.match(uri)) {

37 case MENU:

38 return mDb.getMenuItems();

39 default:

40 throw new UnsupportedOperationException("Not yet implemented");

41  }

42
 

43 }

44
 

45 @Override

46 public int delete(Uri uri, String selection, String[] selectionArgs) {

47 // Implement this to handle requests to delete one or more rows.

48 throw new UnsupportedOperationException("Not yet implemented");

49  }<span>        </span>
Code Snippet 1, a simple content provider wrapper ++
 
URIs are used to uniquely address the resources and an AUTHORITY tag to identify the content provider.
Accessing the content provider is done via a ContentResolver object available from the application context.  ContentResolver uses the URIs and AUTHORITY tag to route the calls to the appropriate content provider.
Accessing RestaurantContentProvider Using ContentResolver
In the current version of the sample app, we access the menu items directly from the SQLite database. Using a ContentResolver we can direct our data access calls to the content provider wrapper we created earlier.

01 public void loadDataFromLocalDataBase(Context ctx) {

02 if (mMenuItems != null && mMenuItems.size() > 0) {

03 clear();

04  }

05
 

06  mMenuItems = new ArrayList<MenuItem>();

07
 

08 Set<String> categories = new HashSet<String>();

09 //Cursor c = db.getMenuItems();

10  Cursor c = ctx.getContentResolver().query(RestaurantContentProvider.MENU_URI, null, null, null, null);

11 while (c.moveToNext()) {

12 String category = c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.CATEGORY));

13 categories.add(category);

14
 

15 MenuItem menuItem = new MenuItem();

16 menuItem.setCategory(category);

17 menuItem.setName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NAME)));

18  menuItem.setDescription(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.DESCRIPTION)));

19 menuItem.setNutrition(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NUTRITION)));

20 menuItem.setPrice(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.PRICE)));

21 menuItem.setImagename(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.IMAGENAME)));

22 mMenuItems.add(menuItem);

23 }

24 c.close();

25
 

26 mCategoryList = new ArrayList<String>(categories);

27 }

Code Snippet 2, accessing content provider wrapper ++
 
Since both SQLite and content provider APIs use cursor objects, we can simply switch out the call as shown in the above code snippet.
 
Adding RestaurantContentProvider to App Manifest
Finally, we need to register our content provider with the Android system by adding it to the app manifest.
1 <provider

2 Android:name="com.example.restaurant.RestaurantContentProvider"

3 Android:authorities="com.example.restaurant.provider"

4 Android:enabled="true"

5 Android:exported="true" >

6 </provider>

Code Snippet 3. Add RestaurantContentProvider to app manifest ++

The provider element has several options to customize content provider permissions and security. Please see the following link for more details.
http://developer.Android.com/guide/topics/manifest/provider-element.html
In code snippet 3, we have the exported flag set to ‘True’ so we can selectively share some restaurant app content with other apps on the device.
In the 2nd part of the article we will take a look at how the restaurant sample app leverages the Android sync adapter framework to achieve seamless data sync with the backend server.

For more such Android resources and tools from Intel, please visit the Intel® Developer Zone

Source: https://software.intel.com/en-us/articles/handling-offline-capability-and-data-sync-in-an-android-app-part-1