In this blog, I would like to share my experience in designing an offline application and the lessons learnt during this journey.
SAP provides various offerings to enable this feature. With SAP BTP in existence and the SAP Mobile Service to consume, below were the list of possibilities we considered.
SAP these days doesn’t recommend their customers to implement new applications with SAP Hybrid Application Toolkit (HAT). Hence, we went for alternatives.
The SAP Mobile Development Kit (MDK) is a tool to build hybrid applications(mobile and web). The code will be written once in SAP BAS and can be rendered on multiple devices as native mobile application on iOS and Android and as web application in a browser. MDK provides a wide range of UI styling. MDK documentation can be referred for the same. As our customer is brand focused, we observed deviations from the possible styling opportunities provided by MDK, so we opted for SAP BTP SDK for Android.
To better understand the approach of the online/offline scenario, I pasted the screenshot of the process flow diagram :-
It is a scenario of Delivery PGI in warehouse management process where the entire open shipment and delivery details for a plant gets downloaded onto the mobile device with which the user can transact. Referred to as initial or full sync. The offline updated data is posted to ECC. SAP Mobile Service delta sync kicks in, to sync the local store with modified records from ECC.
While designing the offline application, I recommend to segregate the master data APIs and the transactional APIs. The reason being, we call the master data API during the initial login for reusability purposes in comparison to the transactional data which is called when the respective application gets triggered.
Another point to consider, is the size of data that the application is expected to download. It’s a good practice to bring only that fragment of data which you expect to work with. Adding date filters to the API is the best way to target these records and will improve performance.
SAP framework doesn’t allow the utilization of the navigation property of the oData service with offline delta sync functionality. Hence, we split the oData call into multiple APIs and created our local store.
The response format of an oData delta sync call is ATOM+XML whereas the standard SAP oData response call is JSON format. SAP Mobile Service Offline configuration needs alteration to support both.
Value added services to consider:
Below are few code snippets which I would like to share.
implementation group:'com.sap.cloud.android', name:'odata', version: sdkVersion
implementation group: 'com.sap.cloud.android', name: 'offline-odata', version: sdkVersion
val offlineDELPGIDataProvider = OfflineODataProvider(
URL("$serviceUrl$GW_OFS_Dest/Z_OFS_DELIVERY_PGI_OFFLINE_SRV_01"),
offlineODataParameters,
getClient(),
delegate
)
val offlineODataParameters = OfflineODataParameters().apply {
isEnableRepeatableRequests = false
storeName = OFFLINE_DEL_PGI_DATASTORE
currentUser = FlowContextRegistry.flowContext.getCurrentUserId()
isForceUploadOnUserSwitch = runtimeMultipleUserMode
isEnableIndividualErrorArchiveDeletion = true
val encryptionKey = if (runtimeMultipleUserMode) {
UserSecureStoreDelegate.getInstance().getOfflineEncryptionKey()
} else { //If is single user mode, create and save a key into user secure store for accessing offline DB
if (UserSecureStoreDelegate.getInstance().getData<String>(
OFFLINE_DATASTORE_ENCRYPTION_KEY
) == null
) {
val bytes = ByteArray(32)
val random = SecureRandom()
random.nextBytes(bytes)
val key = Base64.encodeToString(bytes, Base64.NO_WRAP)
UserSecureStoreDelegate.getInstance().saveData(
OFFLINE_DATASTORE_ENCRYPTION_KEY, key
)
Arrays.fill(bytes, 0.toByte())
key
} else {
UserSecureStoreDelegate.getInstance().getData<String>(
OFFLINE_DATASTORE_ENCRYPTION_KEY
)
}
}
Log.d("ResponseEncryption", encryptionKey.toString())
storeEncryptionKey = encryptionKey
}
private fun getClient(): OkHttpClient {
return ClientProvider.get().newBuilder().connectTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS).writeTimeout(300, TimeUnit.SECONDS).build()
}
offlineDELPGIDataProvider = OfflineODataProvider(
URL("$serviceUrl$GW_OFS_Dest/Z_OFS_DELIVERY_PGI_OFFLINE_SRV_01"),
offlineODataParameters,
getClient(),
delegate
).apply {
val deliveryQuery = DataQuery()
deliveryQuery.filter(GetDeliveryDetails.plant.equal(localSharedStorage?.getPlant()!!))
Log.d("Responded", deliveryQuery.toString())
addDefiningQuery(OfflineODataDefiningQuery("GetDeliveriesForShipmentSet", "GetDeliveriesForShipmentSet$deliveryQuery", false))
addDefiningQuery(OfflineODataDefiningQuery("GetDeliveryDetailsSet", "GetDeliveryDetailsSet$deliveryQuery", false))
addDefiningQuery(OfflineODataDefiningQuery("GetDeliveryItemDetailsSet", "GetDeliveryItemDetailsSet$deliveryQuery", false))
z_DEL_PGI_OFFLINE_SRV_Entities_Offline = Z_DELIVERY_PGI_SRV_Entities(this)
}
Step 4: SAP Store actions
provider.download({
countDownLatch.countDown()
}, {
errorMessage = it.message ?: "Unknown offline sync error when downloading data."
result = getErrorCode(it)
countDownLatch.countDown()
})
provider.upload({
startPointForSync = progressListener.totalStepsForTwoProgresses / 2
}, {
countDownLatch.countDown()
errorMessage = it.message ?: "Unknown offline sync error when uploading data."
})
val data = z_delivery_pgi_srv_entities_offline?.getGetDeliveriesForShipmentSet(query)
if (data != null && data.isNotEmpty()) {
Log.d("Response", "Size " + data.size)
view?.onGetShipments(data)
view?.onDismissLoader()
} else {
view?.onDismissLoader()
view?.onError(context.resources.getString(R.string.no_data_found))
}
Hopefully, you found this blogging helpful, and I wish it helps you avoid some of the common mistakes while designing an offline mobile application. Appreciate your feedback, comments.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
13 | |
10 | |
7 | |
7 | |
7 | |
6 | |
5 | |
5 | |
5 | |
5 |