Intro: I have a native library (C++) with Java (JNI) wrapper. The library engine is cpu intensive, we don’t want more than one app linking this lib to be running at the same time, and complex objects should be returned by the lib engine.
The question: What is the best way to engineer such an Android library?
So far, I could find just find 2 valuable examples: OpenCV manager and Connectbot ssh-agent.
I can think of a few solutions:
-
Solution 1: Make a (bound or AIDL) service that wraps the library functionality. (should the service run in its own space? or in the space of the application that links to it? how can the native lib be loaded if it’s in a different app space(
System.load("/data/data/com.company.myLib/lib.so")). How to return complex objects in AIDL?). This should be the Connectbot way. -
Solution 2: Divide the lib into 2 components:
- A standalone package which keeps the native libs + a manager service
- An android lib-project which only contains Java wrappers which users can use to build their apps.
This should be the OpenCV manager way. I don’t know exactly the details, but this way one does not need a service to interface with and can just
import com.company.myLib.LibWrapper. On the other side the LibWrapper class should performSystem.load("/data/data/com.company.myLib/lib.so"). Correct?
I would personally go for solution 2. Unfortunately Android is a new land and there are not many models yet on how to develop a library. Is there any other/better solutions? Is there other considerations to make?
Consider the following scheme: you build an “empty” app that contains no activities, no settings – only the manifest, the icon for “manage apps” list and the native lib that is installed by the system in /data/data/package/lib directory.
This native library may, but doesn’t have to expose JNI functions. In a typical situation, this lib will be a straightforward port of an opensource LGPL library – e.g. libdmtx.so.
The “client” apps will call loadLibrary() for the “external” lib, and after that it will call the usual load() for its JNI wrapper. This lib has the only purpose to translate Java methods to the public C APIs of the external lib.
The JNI wrapper and the corresponding Java class may be distributed as a .jar or as sources, they are not bound by LGPL license of the external lib.
Such scheme is, IMHO, the only way to ensure LGPL compliance on Android: anybody can recompile the “external” lib from the open source, package it as an “empty” app and install it on their device.
Regarding your concern about concurrent access to the lib, I actually doubt that it is so important: the high end devices have four cores more powerful each than one core on cheaper devices. OTOH, it’s easy to use Linux synchronisation methods, e.g. named pipes, to keep track of active instances.