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

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. 

About the Author 

 

Overview

Android* apps that rely on a backend server for data or content need to provide offline capability for a seamless userexperience. 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.

 

 

Figure 1: A Restaurant Sample App - Little Chef

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.

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 ContentProvider and implement its 6 required methods - query(), insert(), update(), delete() getType(), onCreate(). 

 

public class RestaurantContentProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.restaurant.provider";
    private static final int MENU = 1;
    private static final int MENU_ID = 2;
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        sURIMatcher.addURI(AUTHORITY, "menu", MENU);
        sURIMatcher.addURI(AUTHORITY, "menu/#", MENU_ID);
    }

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

    @Override
    public boolean onCreate() {
        mDb = new RestaurantDatabase(getContext());
        return true;
    }

    @Override
    public String getType(Uri uri) {
        switch (sURIMatcher.match(uri)) {
            case MENU:
                return "vnd.Android.cursor.dir/vnd.com.example.restaurant.provider.menu";
            case MENU_ID:
                return "vnd.Android.cursor.dir/vnd.com.example.restaurant.provider.menu";
            default:
                throw new RuntimeException("getType No URI Match: " + uri);
        }
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        switch (sURIMatcher.match(uri)) {
            case MENU:
                return mDb.getMenuItems();
            default:
                throw new UnsupportedOperationException("Not yet implemented");
        }

    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }        

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.

     public void loadDataFromLocalDataBase(Context ctx) {
        if (mMenuItems != null && mMenuItems.size() > 0) {
            clear();
        }

        mMenuItems = new ArrayList<MenuItem>();

        Set<String> categories = new HashSet<String>();
        //Cursor c = db.getMenuItems();
        Cursor c = ctx.getContentResolver().query(RestaurantContentProvider.MENU_URI, null, null, null, null);
        while (c.moveToNext()) {
            String category = c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.CATEGORY));
            categories.add(category);

            MenuItem menuItem = new MenuItem();
            menuItem.setCategory(category);
            menuItem.setName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NAME)));
            menuItem.setDescription(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.DESCRIPTION)));
            menuItem.setNutrition(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NUTRITION)));
            menuItem.setPrice(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.PRICE)));
            menuItem.setImagename(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.IMAGENAME)));
            mMenuItems.add(menuItem);
        }
        c.close();

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

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.

        <provider
            Android:name="com.example.restaurant.RestaurantContentProvider"
            Android:authorities="com.example.restaurant.provider"
            Android:enabled="true"
            Android:exported="true" >
        </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.

 

About the Author

Ashok Emani is a Software Engineer in the Intel Software and Services Group. He currently works on the Intel® Atom™ processor scale enabling projects.

 

Notices

INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT.

UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR.

Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information.

The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.

Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order.

Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark* and MobileMark*, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products.

Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license.

Intel, the Intel logo, and Atom are trademarks of Intel Corporation in the U.S. and/or other countries.

Copyright © 2013 Intel Corporation. All rights reserved.

*Other names and brands may be claimed as the property of others.

Optimization Notice

Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel.

Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice.

Notice revision #20110804

 

 

++This sample source code is released under the Intel OBL Sample Source Code License (MS-LPL Compatible)

For more complete information about compiler optimizations, see our Optimization Notice.

2 comments

Top
k m.'s picture

Great topic, is part 2 coming out any time soon?

Add a Comment

Have a technical question? Visit our forums. Have site or software product issues? Contact support.