Things that you need to know if you want to develop an SAP Kapsel hybrid app for modern smartphone
You will never stop learning new things. This should be something engraved and highlighted everywhere.
We’re developing a hybrid SAPUI5 application with Kapsel support for one of our client. To be honest this project is a little bit old and is not leveraging the newest feature of SAP WebIDE that allows you to create the hybrid build on the cloud. Out team stepped into the development of the project only in the final phase where the structure and the build environment was already decided months ago (and this is the main problem).
Anyway, this project is made of a SAPUI5 application subdivided in 4 different applications (4 tiles). As I said it has started months ago so they didn’t use WebIDE mobile services. Instead, they used MDK (Mobile Development Kit) 3.0.
The problem was in the offline synchronization phase. When you start the app you see a login page where the app auth with the IdP connected to our client’s on-premise landscape though SAP Mobile Services via the Cloud Connector.
When the user “Sign in” the app auths you and the synchronization with the XSA OData service start. This part is all automatically handled by the Kapsel’s OData plugin that downloads all the data to be used offline.
On our test device, a Samsung Galaxy S4 mini, everything works like a charm. The problem happened when I switched to test the app on my Samsung S8. After the login phase, the app creates a BusyDialog while it syncs with the XSOData service. On my S8 the app loops on that BusyDialog forever.
Debug Debug Debug
E/SMP_LOGON: Certificate info of certificate retrieved from Logon Manager is null E/SMP_AUTH_PROXY: Getting certificate from Logon Manager failed due to InvocationTargetException: null I/com.sap.smp.authflows: [group: com.sap.smp.sdk.android] [artifact: HttpConvAuthFlows] [version: 3.15.3] [buildTime: 2017:07:19:12:13] [gitCommit: 48bd516ede62773b0ecb863646c41130c7767c29] [gitBranch: n/a] D/shim: loading shared libraries W/System.err: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/base.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_dependencies_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_resources_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_0_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_1_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_2_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_3_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_4_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_5_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_6_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_7_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_8_apk.apk", zip file "/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/lib/arm64, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/base.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_dependencies_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_resources_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_0_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_1_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_2_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_3_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_4_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_5_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_6_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_7_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_8_apk.apk!/lib/arm64-v8a, /data/app/com.techedge.mfspp-H-1jvpRUjO-CftPguTjTvA==/split_lib_slice_9_apk.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]]] couldn't find "libodataofflinejni.so" W/System.err: at java.lang.Runtime.loadLibrary0(Runtime.java:1011) at java.lang.System.loadLibrary(System.java:1657) W/System.err: at com.sap.smp.client.odata.offline.common.OfflineStore.load(OfflineStore.java:78) W/System.err: at com.sap.smp.client.odata.offline.common.OfflineStore.<clinit>(OfflineStore.java:67) W/System.err: at com.sap.mp.cordova.plugins.odata.OData.openOfflineStore(OData.java:264) W/System.err: at com.sap.mp.cordova.plugins.odata.OData.execute(OData.java:130) W/System.err: at org.apache.cordova.CordovaPlugin.execute(CordovaPlugin.java:98) W/System.err: at org.apache.cordova.PluginManager.exec(PluginManager.java:132) W/System.err: at org.apache.cordova.CordovaBridge.jsExec(CordovaBridge.java:57) W/System.err: at org.apache.cordova.engine.SystemExposedJsApi.exec(SystemExposedJsApi.java:41) W/System.err: at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:325) W/System.err: at android.os.Looper.loop(Looper.java:142) at android.os.HandlerThread.run(HandlerThread.java:65)
Ok. Now we know it the problem is on the native side. But we also have another two hints if we look at the error carefully:
- couldn’t find “libodataofflinejni.so”: it means that Android didn’t find a native library (c++ libs)
- lib/arm64-v8a: it means that Android is looking for that library in the arm64-v8a folder
Perfect, we can now follow a path! I’ve expanded all the source code folders and looked inside the JNI subfolders and I found the problem.
Kapsel has not installed the .so libraries needed by the arm64-v8a CPU architecture used by my S8 and by many other new phones.
At this point I had two possible solutions:
- Try to update the Kapsel libraries and check out if they added support to 64bit CPU architectures
- Build our Android application forcing the 32bit version of the app
The first solution was the perfect one because if so we could have a different build for each different architecture and this means an optimized code for each of them and less MB in the final product. The solution has also some downside, like upgrading on the fly the Kapsel plugins and re-testing everything but the tradeoff was still ok.
We searched on SAP documentation for the updated version of Kapsel but we found an horrible news: Support note 2464028 — Android Offline Odata 64 bits limitations
On 64-bit Android devices (such as Samsung Galaxy S7), OData Offline Store based application crashes.
Reason and Prerequisites
SAP Mobile Platform SDK 3.0 SP14 PL08 OData Offline library for Android and its dependencies only support 32 bit applications. Android Studio automatically builds for the target architecture of the connected device. On 64-bit devices it is required to use 64-bit native libraries.
Application developer has to force the 32-bit build. Applications built for 32-bit target will be able to run on 64-bit devices without any known incident.
In order to force 32-bit build, edit the build gradle file of the application module.
Please find the documentation about how to configure APK for different ABIs(Application Binary Interfaces).
Well, I was devastated. I lost days debugging this problem and the perfect solution to support the 64bit architecture has vanished in seconds. SAP should have advertised this problem in a huge way in each Kapsel post. But I had no time for complaints and the project’s deadline was too near so I started researching how to solve this problem updating the build.gradle build file on my Android project.
I just followed the two steps:
- Add into the defaultConfig the default configuration for the NDK build supporting both “x86” and “armeabi-v7a”
- Comment out the previous buildFlavors configuration that was splitting the build based on the flavor name
With this configuration, that I still need to optimize (sorry but mastering Gradle takes a lot of time, a resource I’m laking a lot lately), our application is now working the client without a problem and our client is happy again 😉
Using Cordova and developing an app for multiple devices can be a pain: write once, debug everywhere.
I'm working on a Fiori Client app for iOS and everything was running just fine and then Apple decided to release Xcode 10 (which is not yet supported by Cordova). After losing a few hours trying to fix the issue I had to downgrade to Xcode 9...
I feel you, just today I've upgraded to the new Android Studio and I got an error upgrading the gradle plugin 😀
Also, Kapsel (or at least the MDK we've used for this project) is not supporting the latest version of Cordova so we had to downgrade Cordova for this project.
I'm a long time Android Developer (I've developed for more than 4 years) and I would love to switch back to Android/iOS native programming. I'm waiting the moment I will be able to create an app with the iOS/Android SCP SDK to test it out!
Is it easy to use?
I've not used the Android SDK yet but the iOS SDK is quite good and easy to use... if you already are a good iOS developer. IMO it's very difficult to be a good Web/iOS/Android/Fullstack developer at the same time. It's not only different programming languages but also different tools, APIs, application lifecycle etc.
Plus you really have to think about the use case and see if it makes sense to develop native apps for your customers. Going native when you have only one app used by millions of users is a no brainer but when you have to develop a lot of small simple apps for different users it's a different story. Sometimes it's just better to re-use your existing skills and develop a Fiori app (pure web, Cordova/Kapsel or Fiori Cient).
Well, we're always talking about the same topics:
It's still clear as the sky that native development is better if we're talking about performance and integration with the device. It's more reliable, debuggable, testable and you have better tools/service to debug with if compared to hybrid dev.
I still believe that you could do both web/native as a developer but you need more time to master the development and the design pattern.
The main issue is to persuade the client to invest time and money to create it. I would really like to try to create the native version of the Web apps that I'm building to understand the actual state of the art of the iOS/Android Fiori SDK.
Alternatives (I'm commenting this argument in this Linkedin Comment) are still not viable if we're talking about enterprise app / SAPUI5 apps.
Thanks for sharing your solution, Emanuele. FYI the cloud-build from Web IDE works well, so you should try it out.
It’s also not using the latest version of Cordova and there are a few issues like not being able to install apps on Android 9 if the debug is enabled.
I concur with Dominique that the SDK for iOS works well too. It’s quite mature now. It is quite a steep learning curve however if you’re new to iOS development. If you want to find out more you might like to attend my session (‘SAP Cloud Platform SDK for iOS: What ABAP and SAPUI5 Devs Need to Know‘) at Tech Ed Las Vegas 2018.
Maybe I should rename it to ‘What ABAP, SAPUI5 and Android devs…..’
You might want to look into the SAP Mobile Development Kit (or MDK). SAP have been dropping some hints lately that we can expect Android support very soon. It uses a more modern multi-platform approach.
Hi Mark, thanks for the hints! I'm always happy to learn new things and be updated to the latest bleeding edge techs offered by SAP and others 😉
I know that with WebIDE you can already build that, but unfortunately, as I said in the post this is not a project from scratch and we were forced to use the actual version of the app with the current Kapsel structure/build. I cannot say how much it hurts to develop in this condition 😀
I've already started in parallel to configure a WebIDE project to leverage those features. Our client has SAP Mobile Services integrated with their on-premise system via Cloud Connector and the integration between all of those services with the WebIDE app configuration is awesome.
In the next days, I'm going to take some spare time to configure my TechEd journey and I will add those talks if they are also in the Barcelona event 😉