You are on page 1of 9

MOBILITY

Getting Started with Offline-Enabled Android Applications
Offline-enabled mobile applications are very useful when you venture into remote areas of the world, far away from data centers and internet connections. In this article, Mark Gearhart, a Senior Consultant at SAP, describes a database test of the Sybase Unwired Platform and a native Android application designed to operate offline. By Mark Gearhart, SAP NS2

A

Mark Gearhart is a Senior Consultant with SAP. He is involved in the development of database architectures and rich internet applications for temporal and realtime systems. His background encompasses the energy and financial industries with a special interest in data storage architectures and fast user interfaces. He can be reached at mark. gearhart@sap.com.
20 ISUG TECHNICAL JOURNAL

ll I really wanted to do was to create a mobile Android application that could read from an Enterprise Information System (EIS) database and display the result on the device screen. That’s not a big effort using the Sybase Unwired Platform. After all, that’s what their Hybrid Web Containers were designed for, and it takes just a couple minutes to build this type of application. However, I also wanted to retain the data on the device and display it even if the internet or EIS were down. This is a significant requirement, and causes things to get complicated quickly. In a review of recent mobility requests, I have noted a frequent requirement for the ability to operate in this offline-enabled mode. Fortunately, the Sybase Unwired Platform supports both offline-enabled and online-only application types. Thanks to this, enterprises can choose the architecture that works best for the application, rather than having to force all application requirements into a one-size-fits-all model. Offline-Enabled Applications Offline-enabled applications are built around the Sybase Unwired Platform’s Mobile Business Object data synchronization capabilities, with a native

client that runs on the mobile device. This type of offline-enabled architecture is best suited to generally complex applications, which someone might use to do the vast majority of their day-today work. Examples are Mobile CRM Sales, Mobile Field Service, and applications for people whose entire day is generally spent away from the office or away from the internet. There are two features of the native architecture that make it ideal for this: the native client, and the offline capabilities of data synchronization. The native client works best for complex, multi-screen applications. Browsers are not well suited for this type of complexity and rich UI requirements. The downside of having to write a new UI for each platform is often irrelevant, as these types of applications are often associated with company-provided devices, rather than expected to run on employee-owned smartphones. The other feature is offline enablement. With data synchronization, a copy of the relevant data is stored on a mobile database on the client device, so that updates can be made while there is no connection. Real-time queries are supported in an offline mode because the data is stored locally, ready to be synchronized when the connection is established again. Offline enablement is important from a cost perspective. In many

The Sybase Unwired Platform architecture to support this type of application consists of a Customer MBO (Mobile Business Object) which resides in the Unwired Server and in the UltraLite database on the device. There is no point spending data plan money on reloading the customer’s address again. This architecture is best suited to lightweight mobile applications that normally involve some simple task or business process. Unfortunately. we are not going to distract things with a user interface yet. Not only is connectivity disabled due to geographic distances. This MBO is the container which contains instances (rows) of the Customer table originating from the EIS. this also means that a Hybrid Web Container based application. The application itself is managed once. For these situations. on a single codeline. Sybase also provides the Sybase Mobilink software needed to synchronize data from the EIS to the device. Enterprises in those areas would much rather update all relevant data in the warehouse or office using corporate WiFi at the beginning and end of the day. the EIS is just an SQL Anywhere database. no offline capabilities. it is disabled for security. when the EIS data source is unavailable. Then. etc. which would benefit from a very nice UI. we will verify that the UltraLite database still exists on the device and contains the downloaded data. Necessary Reference Material After you’ve downloaded the Sybase Unwired Platform from the Sybase Product Download Site at https://subscribenet. and a good Android reference manual: Figure 1: Reference Material AUGUST 2012 21 . The container handles the configuration and display of the application for the specific device OS. and businessapproval requests are typical. A native application has no such widget restriction because it is built for a specific operating system. Android. cannot use widgets specific to Android or iOS. There is no local database on the device. data plans are incredibly expensive. and there is no need for multiple front ends for iOS.GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS emerging markets. Offline enablement is also important so that one can move freely. Since this is a database and synchronization test. this will be done later after we have a basic database framework in place. Dashboards that get “up-to-the-minute” enterprise information. there are at least three references (see Figure 1) you will need if you are not already an expert in Android and the Sybase Unwired Platform – some SUP installation instructions. This is a great thing. plus object queries such as findAll. they also take much longer to build since they require low-level coding. The application must use the lowest common denominator. Even in the most high-tech environments. we will build a very simple application that (a) creates an Sybase UltraLite database on the device. and is based on HTML5 and Javascript. This is ideal for environments where both Android and iOS must be supported. Sybase provides the UltraLite database for the device as well as the Sybase Unwired Server which contains a cached copy of the MBO’s. Even though the nicest looking and most capable applications tend to be native applications. the Sybase Unwired Platform also supports online-only applications. a Sybase Unwired Platform Developers Guide. In our case. The online-only application runs inside a Hybrid Web Container. although it could have been any of the SAP Business Suites or any other data source. having access to downloaded information on the device obviates the need to remain connected. (b) opens up a database connection to the Sybase Unwired Server. and limit any online usage to situations that legitimately require real-time lookups. The UI design is not complex. and (c) downloads data from the EIS data source to the UltraLite database on the device. com. Online-Only Applications By contrast. ships at sea or troops on deployment will not be in contact with a base that contains the EIS where the data resides. and hence. as we are about to see. which we will use in our application. Let’s Build an Offline-Enabled Application To prove that a offline-enabled application works. I won’t spend any time with it for this article other than to say that online-only applications connect directly to the EIS.

onRestart Active Lifetime Visible Lifetime Full Lifetime Figure 2: Application Lifecycle 22 ISUG TECHNICAL JOURNAL . it is executed when the application is launched. There are also other opportunities for synchronization. we will be using Activities. Services. visible. and Broadcast Receivers.java class contains all the Activity event handlers and you are free to override their default operations as desired.com. onResume A MainActivity. If we are going to automatically create a local device database and synchronize it from a remote data source. we are concerned with the onCreate Activity. The version and ESD are important. all in one spot. onPause Activity. therefore.isug. Figure 3: Our Simple Application Activity. all in the correct order. This MainActivity. Personally. onSaveInstanceState Activity. ESD #3 for Android devices. we would also synchronize within the onRestart Activity. onRestoreInstanceState Activity. onStart Activity. For the simple application. onCreate Activity. such as when a user updates data on the device. An Activity component is a single screen with a user interface. since the installation often changes significantly from one release to the next.xml. by Reto Meier. If we were going to maintain synchronization with the remote data source throughout the activity’s lifecycle. it would happen here.GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS Installing both the Sybase Unwired Platform and Android Development Kit is not trivial. Any good reference will work. which I have written and which extends the Activity class. The Developers Guide contains the API documentation for things such as createDatabase() and synchronize(). It requires a lot of flipping back and forth between several installation guides and internet sites. As shown in Figure 3. I wrote an installation guide that captures. and active lifetimes. and an Android reference manual is handy for understanding things like the Activity class and process threading. Android provides a series of event handlers that are fired when an Activity transits through its full. is declared as the MAIN class with a LAUNCHER category in AndroidManifest. onDestroy Activity. I am using the book “Android 4 Application Development”. This 28-page installation guide is included as a downloadable with the article at my. To ensure that Activities can react to state changes. This kind of knowledge is required when developing native Android applications. a method for installing the Sybase Unwired Platform version 2. The Sybase Unwired Platform Developers Guide and an Android reference manual are also required. when the application is launched we present an action screen to test the creation and synchronization of a local database from the EIS data source. Activity. onStop Activity. Content Providers.1. but there are many other good resources out there. Defining the Android Activity Lifecycle Of the four Android application components: Activities. Figure 2 summarizes these lifetimes in terms of Activity states.java class.

break.onApplicationSettingsChanged").i(DEBUG_TAG. you can use the registerCallbackHandler method in the generated database class. int errorCode. } @Override public void onHttpCommunicationError(int errorCode. The Sybase Unwired Platform documentation goes to great lengths to explain all the methods for tracing the execution of code. and application registration status: private class MyApplicationCallback implements ApplicationCallback { @Override public void onApplicationSettingsChanged(StringList nameList) { Log. or changes in data.sybase. This scenario might be useful if you want to create an initial entry screen. String debugString. String errorMessage) { Log. "MyApplicationCallback.onSynchronize"). error= ( " + errorMessage + ")"). In our case. "MyApplicationCallback. showConnectionStatus(0. case SynchronizationStatus.JANUARY 2012 PB MOBILITY AUGUST 2012 23 . To register for a particular MBO. case SynchronizationStatus. case SynchronizationStatus. break.ASYNC_REPLAY_UPLOADED: debugString="ASYNC_REPLAY_UPLOADED".mobile. We also define a “Get Status” button to dump the current status of the application. "MyApplicationCallback. For another type of application. case SynchronizationStatus. StringProperties httpHeaders) { Log.onRegistrationStatusChanged. There are a million moving parts to a mobile application.i(DEBUG_TAG. we have registered the callback handler for the entire database: private class MyDatabaseCallback extends DefaultCallbackHandler { @Override public int onSynchronize(GenericList<SynchronizationGroup> groups. int errorCode. String errorMessage) { Log.i(DEBUG_TAG. Setting up Callback Listeners Callback listeners are provided so your application can monitor changes and notifications from Sybase Unwired Platform.STARTING: debugString="STARTING". Therefore.ApplicationCallback class is used for monitoring changes to application settings. These APIs provide notifications on an operation’s success or failure.ERROR: debugString="ERROR".onDeviceConditionChanged. messaging connection status. error= ( " + errorMessage + ")").GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS We define a “Start Application” button within the onCreate Activity. break. SynchronizationContext context) { Log.DOWNLOADING: debugString="DOWNLOADING". condition = (" + condition + ")"). a database callback listener and a synchronization status listener. it can register an application callback listener. we should likewise go to great lengths in setting up and enabling the supplied Callback Listeners. And rightly so. "MyApplicationCallback. break. String errorMessage. showApplicationStatus(0. } @Override public void onRegistrationStatusChanged(int registrationStatus. break.i(DEBUG_TAG. switch(context. use the registerCallbackHandler method in the generated MBO class.CallbackHandler class is used to monitor notifications and changes related to the database. To register callback handlers at the package level.i(DEBUG_TAG. and then initiate further actions when buttons are pushed."MyDatabaseCallback.onConnectionStatusChanged"). break. } @Override public void onConnectionStatusChanged(int connectionStatus.FINISHING: debugString="FINISHING".ASYNC_REPLAY_COMPLETED: debugString="ASYNC_REPLAY_COMPLETED". DECEMBER 2011 . with potential errors in both the network and the servers all the way from the EIS down to the device. When your application starts.onHttpCommunicationError.STARTING_ON_NOTIFICATION: debugString="STARTING_ON_NOTIFICATION". Our three listeners are coded as follows.connectionStatus). we could automatically callup a UI view to display local data when it becomes available rather than clicking a button to start the process.i(DEBUG_TAG.persistence.getStatus()) { case SynchronizationStatus. break. This can also be coded if desired. The Application Listener The com. case SynchronizationStatus. case SynchronizationStatus.registrationStatus).sybase. } } The Database Listener The com. } @Override public void onDeviceConditionChanged(int condition) { Log. "MyApplicationCallback.

break. case SyncStatusState. default: debugString = "Invalid SynchronizationStatus". default: infoMessage = "STATE" + syncState + "[" + interval + "]".CONTINUE. String infoMessage.APPLICATION_DATA_UPLOADING_DONE: infoMessage = "UPLOAD DONE [" + interval + "] " + statusData.getSentByteCount() + ":" + statusData. connection. public MySyncStatusListener() { start = System. break.GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS } case SynchronizationStatus. To show status.APPLICATION_SYNC_SENDING_SCHEMA: infoMessage = "SENDING SCHEMA [" + interval + "]". we code up three classes which inspect the pertinent properties of the application: The showRegistrationStatus Class Each device must register with the Server before establishing a connection. and can be used in the user interface to indicate synchronization progress: private class MySyncStatusListener implements SyncStatusListener { long start.UPLOADING: debugString"UPLOADING".getSentRowCount() + " R<" + statusData.i(DEBUG_TAG. break. When pressed. case SyncStatusState. it shows the status of the registration. break.getReceivedByteCount() + ":" + statusData.getReceivedRowCount() + ")". case SyncStatusState. case SyncStatusState.i(DEBUG_TAG. break.getReceivedByteCount() + ":" + statusData.APPLICATION_DATA_DOWNLOADING: infoMessage = "DATA DOWNLOADING[" + interval + "] " + statusData.getReceivedByteCount() + ":" + statusData. break.start.getReceivedRowCount() + ")". break. } public boolean objectSyncStatus(ObjectSyncStatusData statusData) { long now = System.getCurrentMBO() + ": (S>" + statusData. int syncState = statusData. and database. break. case SyncStatusState. The documentation states that “the process defines the properties for the device and backend interaction with Unwired Server.SYNC_DONE: infoMessage = "DONE [" + interval + "]".getReceivedByteCount() + ":" + statusData.getSentRowCount() + " R<" + statusData.getSyncStatusState(). break.getReceivedRowCount() + ")". break.getCurrentMBO() + ": (S>" + statusData.getSentByteCount() + ":" + statusData.getStatus() + " (" + debugString + ")").APPLICA TION_SYNC_RECEIVING_UPLOAD_ACK: infoMessage = "RECEIVING UPLOAD ACK [" + interval + "]".getSentByteCount() + ":" + statusData.APPLICA TION_SYNC_COMMITTING_DOWNLOAD: infoMessage = "COMMITTING DOWNLOAD [" + interval + "] " + statusData.getSentRowCount() + " R<" + statusData.SYNC_STARTING: infoMessage = "START [" + interval + "]". case SyncStatusState.getCurrentMBO() + ": (S>" + statusData.getSentByteCount() + ":" + statusData. start = now. long interval = now . Log.getSentRowCount() + " R<" + statusData.persistence.synchronize status = " + context. case SyncStatusState.currentTimeMillis().” This is vague. case SyncStatusState. } } Showing Status on Demand In addition to listening for status changes as they happen. break.currentTimeMillis().APPLICATION_SYNC_CANCELLED: infoMessage = "SYNC CANCELLED [" + interval + "]".APPLICATION_SYNC_SENDING_HEADER: infoMessage = "SENDING HEADERS [" + interval + "]". case SyncStatusState. we would also like to show the status of the system on demand. return false. "SyncListener (" + infoMessage + ")").APPLICATION_SYNC_DISCONNECTING: infoMessage = "DISCONNECTING [" + interval + "]". switch (syncState) { case SyncStatusState. What it really means is that devices must be registered via SUP Messaging before 24 ISUG TECHNICAL JOURNAL ."MyDatabaseCallback.APPLICA TION_DA TA_DOWNLOADING_DONE: infoMessage = "DATA DOWNLOADING DONE [" + interval + "]". we have placed a button called “Get Status” on the application screen. } Log. } return SynchronizationAction.SyncStatusListener class is used for debugging and performance measures when monitoring stages of a synchronization session. break.APPLICATION_DATA_UPLOADING: infoMessage = “DATA UPLOADING [" + interval + "] " + statusData. break.getCurrentMBO() + ": (S>" + statusData.sybase. case SyncStatusState. break. For example.getReceivedRowCount() + ")". case SyncStatusState. break. } The Synchronize Listener The com.

including location and page size. The SUP messaging engine communicates with the application by using an AppID + Activation Code + Device ID. break. case RegistrationStatus. break. The connection profile also contains UltraLiteJ runtime tuning values. break. break. After registration. private void showRegistrationStatus(int step. break.” So.NOTIFICATION_WAIT: debugString = "NOTIFICATION_WAIT". Every Messaging application is uniquely identified by it’s physical device ID. the application is now in a suitable state for database subscriptions and/or synchronization. case RegistrationStatus. When the application is registered. case ConnectionStatus. The documentation states that “when the ConnectionStatus of CONNECTED has been reached and the application’s applicationSettings have been received from the server. MOBILITY AUGUST 2012 25 .CONNECTED: debugString = "CONNECTED".REGISTERED: debugString = "REGISTERED". If the application is not registered. the Server cross checks the device ID every time a connection is made by the application. case ConnectionStatus.i(DEBUG_TAG. break.REGISTERING: debugString = "REGISTERING".DISCONNECTING: debugString = "DISCONNECTING". a CONNECTED state is the required state for transferring data to and from the server: private void showConnectionStatus(int step.GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS data replication can take place.UNREGISTERING: debugString = "UNREGISTERING". "(" + step + ") connection status=" + status + "(" + debugString + ")").CONNECTION_ERROR: debugString = "CONNECTION_ERROR". you will see an “Online” status for the Application Connection in the Sybase Control Center as shown in Figure 4: DECEMBER 2011 .findAll").REGISTRATION_ERROR: debugString = "REGISTRATION_ERROR". } Log. } Log. so this requires the application to register with the Server and send a device ID. default: debugString = "Invalid Registration Status". case ConnectionStatus.i(DEBUG_TAG. default: debugString = "Invalid ConnectionStatus". My Android Device Status is “Online” Figure 4: Sybase Control Center and Registration The showDatabaseStatus Class For a database status.DISCONNECTED: debugString = "DISCONNECTED". "(" + step + ") Database exists"). break. break. The databaseExists function is perfect for this: private void showDatabaseStatus(int step) { if (TestDB. break.UNREGISTERED: debugString = "UNREGISTERED". case ConnectionStatus. we’d like to know if the local database exists. we can dump the MBO instances from the Customer MBO stored in the database.databaseExists()) { Log. int status) { String debugString. then no messages can flow. switch (status) { case RegistrationStatus. break.i(DEBUG_TAG. break. "(" + step + ") application status=" + status + " (" + debugString + ")"). } Once the application is REGISTERED and CONNECTED.JANUARY 2012 PB } The showConnectionStatus Class The Connection Profile stores information detailing where and how the local database is stored. If so. list all Customer MBO instances Log. case RegistrationStatus.i(DEBUG_TAG. case RegistrationStatus. case ConnectionStatus. break. you will see this as a REGISTERED status from showRegistrationStatus. switch (status) { case ConnectionStatus.CONNECTING: debugString = "CONNECTING". int status) { String debugString. break. // If database exists. "Customer.

i(DEBUG_TAG. ++i ) { Customer customer = customers. } } }). We want it to persist.databaseExists()) { // TestDB.setServerName(HOST). super.this).getLname() + ")"). super. Without explicitly creating the database. TestDB.onRestart(). super.i(DEBUG_TAG. Log. app. createApplication(). app. "onRestart"). 26 ISUG TECHNICAL JOURNAL . } } private Runnable createApplicationTask = new Runnable() { public void run() { Log.onCreate(savedInstanceState). app = Application.i(DEBUG_TAG. //} } @Override public void onResume() { Log. // if (!TestDB. thread. although the documentation does seem to skip around. The following code shows the onCreate Activity logic as well as the sequence of database generation and synchronization when the “Start Application” button is pressed: package com. } Button startAppBtn = (Button) findViewById(R. app.java roughly follows the sequence in the Sybase Unwired Platform documentation. } @Override public void onDestroy() { Log.setApplicationCallback(appCallback). For example. super.app.1.findAll(). "(" + step + ") Database does not exists").testclient.138".synchronize(new MySyncStatusListener()).size(). as I was on several occasions.onResume(). "Last Name = (" + customer.i(DEBUG_TAG.getRegistrationStatus()). This seems to hide logic that should really be exposed by an explicit createDatabase() call.OnClickListener() { public void onClick(View v) { // Start a thread to create database and sync with SUP Thread thread = new Thread(createApplicationTask. Application app.getConnectionStatus()). private static final int TIMEOUT = 20. you are left wondering when a local database is available. for (int i = 0.i(DEBUG_TAG. private static final String PASSWORD = "wk60#ls".i(DEBUG_TAG.start().GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS GenericList<SUP101. private static final String HOST = "192. @Override public void onCreate(Bundle savedInstanceState) { Log.setApplicationIdentifier("Test22"). } }.id. TestDB. showDatabaseStatus(1). } @Override public void onRestart() { Log.setApplicationContext(MainActivity.i(DEBUG_TAG. "onDestroy"). startAppBtn.setOnClickListener(new View.getSynchronizationProfile(). private static final int PORT = 5001. statusBtn. Log.i(DEBUG_TAG.example. // Here is a button to start the application // Here is a button to show the status Button showStatusBtn = (Button) findViewById(R. private void createApplication() { try { Log.getInstance(). showConnectionStatus(1.button1).button2).activity_main). "Customer.get(i).i(DEBUG_TAG.Customer> customers = Customer. private static final String USERNAME = "supAdmin".layout. the local database is automatically created when needed by the Object API. There are some variations in the calling sequences which I chose not to follow. TestDB. } else { Log. "application is null"). int n = customers. i < n.registerCallbackHandler(new MyDatabaseCallback()). // Do not delete database.168. "createApplicationTask").id.i(DEBUG_TAG. "onResume").deleteDatabase(). setContentView(R.app.setApplication(app). } }). } } else { Log.findAll returned (" + n + ") customers"). } Putting it All Together The sequence of code in MainActivity. "onCreate"). "createApplication"). "Background"). ApplicationCallback appCallback = new MyApplicationCallback().onDestroy().setOnClickListener(new View. TestDB.OnClickListener() { public void onClick(View v) { if (app != null) { showRegistrationStatus(1. public class MainActivity extends Activity { private static final String DEBUG_TAG = "TRACE".

612: createApplication.startConnection").752: SyncListener (RECEIVING UPLOAD ACK [287]) 07-22 21:39:37. When the “Start Application” button is pressed.372: SyncListener (DATA DOWNLOADING[17]) 07-22 21:39:37.openConnection 07-22 21:39:35.833: Customer.setLoginCredentials(login).302: (0) connection status=103 (CONNECTED) 07-22 21:39:34.openConnection"). This is a blocking call.883: onResume When the Start Application button is pressed: 07-22 21:39:21.i(DEBUG_TAG.createDatabase"). showDatabaseStatus(1).createDatabase(). } catch (Exception e) { Log. it transmits client messages to this pane. if (app. if (!TestDB. the database creation and synchronization executes.732: SyncListener (DONE [4]) 07-22 21:39:37. The local database can also be deleted if no longer needed.synchronize"). the application waits for further input.152: createApplication.startConnection(TIMEOUT).onSynchronize 07-22 21:39:37.822: MyDatabaseCallback.312: SyncListener (START [120]) 07-22 21:39:35.382: SyncListener (DATA DOWNLOADING[12]) 07-22 21:39:37. TestDB.602: (0) application status=203 (REGISTERED) 07-22 21:39:34.onSynchronize 07-22 21:39:35. Log. When the Android Emulator runs. we have local database results: When the application is launched: 07-22 21:37:44.733: (0) application status=202 (REGISTERING) 07-22 21:39:26. activity starts with onCreate and ends with onResume. MOBILITY AUGUST 2012 27 .registerApplication 07-22 21:39:21.442: SyncListener (DATA UPLOADING [3]) 07-22 21:39:35.printStackTrace(). // Synchronize and listen for status. Although we do not do this in the sample application because we’d like the local database to persist. "createApplication could not start connection"). so show it. we should have a database which // contains data.552: SyncListener (DATA DOWNLOADING[4]) 07-22 21:39:37.synchronize status = 6 (ASYNC_REPLAY_UPLOADED) 07-22 21:39:37.383: createApplication.202: (0) connection status=105 (DISCONNECTED) 07-22 21:39:27.442: SyncListener (DATA UPLOADING [3]) 07-22 21:39:35.892: onCreate 07-22 21:37:46. } catch (Exception e) { Log.i(DEBUG_TAG.i call to log client messages to the LogCat pane of the Unwired Workspace. } else { Log. } // Open the database connection Log.databaseExists()) { TestDB.i(DEBUG_TAG.setServerName(HOST). LoginCredentials login = new LoginCredentials(USERNAME.REGISTERED) { Log.434: SyncListener (DATA UPLOADING [4]) 07-22 21:39:35. The Unwired Workspace is the Eclipse-base IDE provided with the Sybase Unwired Platform and is used to build and deploy the Android-based mobile applications.registerApplication"). } } // Create the local database if it does not exist Log.822: MyDatabaseCallback. // When we get here. "createApplication. app.442: SyncListener (DATA UPLOADING [1]) 07-22 21:39:35.222: MyDatabaseCallback.833: Last Name = (Gearhart) } } } We use the Log.462: SyncListener (UPLOAD DONE [1] ) 07-22 21:39:35. "createApplication fatal error").154: createApplication 07-22 21:39:21.732: SyncListener (DISCONNECTING [143]) 07-22 21:39:37.434: SyncListener (SENDING HEADERS [72]) 07-22 21:39:35. conn.getRegistrationStatus() != RegistrationStatus. conn. we could call the deleteDatabase() function in the onDestroy activity if desired. Then. PASSWORD).352: SyncListener (DATA DOWNLOADING[1433]) 07-22 21:39:37.142: (0) connection status=102 (CONNECTING) 07-22 21:39:26.i(DEBUG_TAG. conn.192: createApplication.registerApplication(TIMEOUT). TestDB.i(DEBUG_TAG. "createApplication.822: Customer. try { app.openConnection().883: onActivityResult 07-22 21:37:46.findAll 07-22 21:39:37. "createApplication.setPortNumber(PORT).154: createApplicationTask 07-22 21:39:21.synchronize status = 1 (STARTING) 07-22 21:39:35.822: (0) Database exists 07-22 21:39:37.GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS ConnectionProperties conn = app.i(DEBUG_TAG. "createApplication.222: MyDatabaseCallback.588: SyncListener (COMMITTING DOWNLOAD [35]) 07-22 21:39:37.createDatabase 07-22 21:39:35. } Running the Application Online When the application is launched. At the completion of synchronization. "createApplication.synchronize 07-22 21:39:35.synchronize(new MySyncStatusListener()).findAll returned (3) customers 07-22 21:39:37.getConnectionProperties(). e.i(DEBUG_TAG.

Therefore.startConnection 07-22 22:03:32. The first is the installation of the product. As the UI takes shape and we find ourselves simultaneously rendering screens.432: Last Name = (Manthorpe) 07-23 09:56:51.createApplication.175: createApplication.openConnection 07-22 22:03:32.872: Customer.onSynchronize 07-22 22:03:32.432: Customer.353: createApplication 07-22 22:02:27.892: onCreate 07-22 21:37:46.createDatabase 07-22 22:03:32.263: SyncStatusListener (START [88]) 07-22 22:04:29.222: Last Name = (Jordan) 07-22 22:04:29.492: MyDatabaseCallback. Finding compatible versions of everything is an ongoing task since the pieces come from different vendors.GETTING STARTED WITH OFFLINE-ENABLED ANDROID APPLICATIONS Running the Application Offline When the EIS is offline.822: (0) application status=203 (REGISTERED) 07-22 21:41:25. I have found that there are three difficult parts of the development which have nothing to do with the object API’s or the concept of offline versus online.422: (0) application status=203 (REGISTERED) 07-23 09:56:51.222: Customer.872: Last Name = (Gearhart) 07-22 21:41:25.552: (0) connection status=105 (DISCONNECTED) 07-22 21:39:45.2. other then to execute the code for the “Start Application” button.432: Last Name = (Gearhart) 07-23 09:56:51. When the application is offline. In the meantime.822: Customer. The second thing is the choice of which Android SDK to use. This can be frustrating unless you have the benefit of trial and error behind you. remained unaffected.872: (0) connection status=103 (CONNECTED) When the application is offline: 07-23 09:56:51.822: (0) Database exists 07-22 21:41:25.353: createApplicationTask 07-22 22:02:27.192: (0) application status=203 (REGISTERED) 07-22 22:04:29.872: Last Name = (Manthorpe) 07-22 21:41:25.222: (0) connection status=101 (CONNECTION_ERROR) 07-22 22:06:41. When the application is online.findAll 07-22 22:04:29.883: onResume 07-22 21:39:37. threading will become very important. and executing other tasks.findAll returned (3) customers 07-22 21:41:25. synchronizing data.175: createApplication. the application status is still REGISTERED and the connection status could be either CONNECTION_ERROR or CONNECTING.synchronize status = 1 (STARTING) 07-22 22:03:32. I would have coded these as threads. The third thing is the threading model within Android. manage to get everything working with version 2. or Toast messages.872: Last Name = (Jordan) 07-22 21:41:25.122: (0) connection status=103 (CONNECTED) For either mode of operation.findAll 07-23 09:56:51.synchronize status = 5 (ERROR) Getting the Application Status Here are the results when the “Get Status” button is pressed. I discovered that the it was not possible to successfully register the application with the Sybase Unwired Platform when using the latest version 4. If I had wanted to launch Alert Dialogs.findAll returned (3) customers 07-23 09:56:51. I did. 28 ISUG TECHNICAL JOURNAL Conclusion Developing offline-enabled Android applications has been fairly straightforward.432: Last Name = (Jordan) 07-23 09:56:51.833: Last Name = (Manthorpe) 07-22 21:39:44.193: MyDatabaseCallback.399: createApplication. however. It wasn’t used much in this simple application. however.synchronize 07-22 22:03:32.833: Last Name = (Jordan) 07-22 21:39:37.175: createApplication could not start connection 07-22 22:03:32. at least so far. We also get a synchronization error as expected. we still have local database results: When the application is launched: 07-22 21:37:44.193: MyDatabaseCallback.findAll returned (3) customers 07-22 22:04:29.153: (0) connection status=101 (CONNECTION_ERROR) 07-22 22:03:32. the application status is REGISTERED and the connection status is CONNECTED.492: MyDatabaseCallback. good luck getting started with your offline-enabled Android applications. at the completion of the sequence.422: (0) Database exists 07-23 09:56:51.832: Customer.222: Last Name = (Gearhart) 07-22 22:04:29. or you have a very good installation guide. the local database is available and contains data as shown below: When the application is online: 07-22 21:41:25. After much debugging. The local database.443 (0) connection status=101 (CONNECTION_ERROR) When the Start Application button is pressed: 07-22 22:02:27. the connection process produces an error as expected. .175: createApplication.883: onActivityResult 07-22 21:37:46.192: (0) Database exists 07-22 21:39:37.432: Customer.findAll 07-22 21:41:25.222: Last Name = (Manthorpe) 07-22 22:04:29.1 of the Android SDK.onSynchronize 07-22 22:06:41.