I have done some research and would like to present some findings and get feedback from you:
Api and vision
The idea of course is to bring Nim the ability to target the mobile platform, previous efforts in the community were the nimx gui and android bindings I want to do something more comprehensive, and allow Nim programs to use all Android functionality.
Now in the mobile land there are a few gotchas – one is that you target a different cpu architecture, so in essence you cannot test and run on your own machine – though today there is a mocking library for local testing, tools for fast deployment, and emulators.
Another issue is that android is built on Java, so tiny java code, just for initialization is the minimal requirement – it is the main function in java land, calling your Nim code. But once we’re past that we should have a Nim only experience. That’s where this library comes – aiming to abstract all of the features and create a nice idiomatic API.
More about android – in the build process you link against the latest version – which supports all the previous API functions, but you need to choose the minimal supported version – that is based on the hardware features of each mobile phone – for example some have low latency audio, and special camera features that the older hardware couldn’t support.
But – it seems you can query the hw version at runtime, so a feature for this library is to mix and match – if api version is above 26 use this modern function, else use the legacy one. This gives us a single code base, without needing separate modules for each api that 90% repeat themselves – it allows us to choose the best code path based on the actual hardware it runs on.
Scope
I plan on supporting starting from Api version 24 and onwards until the latest.
We want to support both NDK and the Java SDK
NDK – the native more performant library – with is normal ffi bindings, probably with fast gen via futhark,
and the Java android SDK – because the NDK is limited in functionality – it provides fast alternatives only to some of the services. For example all the gui related like notifications are sdk only.
In order to support the SDK we need to initialize a context – it is essentially a bridge to the java code. We’ll use the excellent jnim extensively for this purpose.
I already have a tool for breadth-first style generating the android jclass interfaces, I ran it for javabase (java io utils etc) and for an advanced java sdk version (the latest I found: api level 36)
But notice it will need manual tweaking, the generated code can be considered a draft for the complete java api.
In android the java sdk does support all the features, but the ndk enhances some of them and is more natural for Nim to work with, so we want to have access to both of them.
More about scope: I don’t intend to handle any GUI parts. The scope is already huge and I think it’s best to focus on just direct access and other projects – like nimx – can handle that.
Or you can use a gui from a different language as a thin layer and call your app’s dll for all the functionality.
I am still learning all that Android has to offer, and possibly reduce the scope further.
Special Features
These include what we spoke of earlier: runtime detection and choosing the most suitable functions available.
And I also tested the feasability of a static compile time feature: trackFeature.
Basically when we call playAudio in the api (that’s not something the user of the library needs to do) we trackFeature(afAudio) of the android audio system. We can know at compile time all the features the app will use based on actual usage. This means we know which services and subsystems we need to initialize and can even compile less stuff.
Architecture
The architecture is very simple
four layers:
at this stage of managers or roles, we already handle the hardware api versions and match the correct functions, but since we have two platforms, NDK and SDK, they handle this stuff internally.
If you choose ndk only for example, and then you opt to use a notify function in your code – at compile time you will get an error (due to feature tracking) that you have to use the sdk as well.
We are trying to abstract the ndk/sdk differences and just use the most optimal version based on your needs.
You can use that fourth layer, or choose to go with the more flexible managers third layer (which requires some android understanding), or even drop to the second layer if you need to access the direct functions. So only the fourth layer tries to hide some mechanisms, for the benefit of making it simpler for the average Nim coder.
After the Android Runtime is initialized, and if uses sdk will have a context, the managers have it as a node has a parent – just a field reference. Or perhaps we’ll make it a global object.
The fourth layer user can simply write: playAudio and inside that function, it’ll use the global object, fetch the initialized AudioManager and that will execute the actual code.
I would like feedback about this api and architecture:
for example, if you’re making a podcast player, and you want to make audio playing faster or slower, you’ll probably integrate another library or your own code for this purpose.
So you want the AudioStream exposed, and your other library can write PCM data directly to it (with the correct format of course)
Another example is exposing the canvas, or painting surface.
Do we want these integration points exposed for maximum flexibility?
Or should the developer drop to the second layer and handle it from there, or even create his own mechanism?
What are your thoughts here?
Testing and other tools
So this is less related, but I found there is an awkwardly named Robolectric – it’s a mocking test lib for android, so we can verify we init and do the steps correctly without reaching for the emulator every moment. With this quite vast expanse it may be useful.
Especially since we use automated generating tools to get quick breadth yet we'd like to know which of those works properly and which don’t, so I can give you a working product. So this may fit well with unit testing or a type of integration testing.
I have just one physical hardware phone to test on, so on this front i haven't done any research - emulator land etc. if you know, please share.
Other tools
Android has some command line tools for building and deployment, and it would likely need gradle - a build system from java – to create the app’s APK. (apk is the file that gets run as a smartphone application)
When I used Flutter, I first saw the doctor tool – it checks that you have everything installed correctly, and even downloads the necessary stuff - this can be really nice.
So we can use those tools (or write a frontend with exec) to create a really nice integrated and hassle-free feeling.
These are optional and really only a future focus. (very far from current position - we're still vaporware)
However, this one we’ll need – a Nim tool to create the basic needed java directory structure and boilerplate files – a scaffolding tool (because android apps require at least a thin java bridge to initialize the code). It’s best if we have one prefs file (yaml) and it generates all we need from this file – this way you can modify the file and it will overwrite accordingly. A single source of truth.
Final thoughts
So, there you have it, my approach for this project, the best feasible ideas I could come up with, and what I found out so far.
Development can happen from multiple layers, but probably - get something running, get skeleton and mechanisms operational, then module by module provide more and more functionality.
If you wish to donate or fund this project, it will give me more motivation and needed financial assistance to tackle this.
I tried to think this thoroughly because it’s a larger project than it seems.
Don’t forget to give feedback.
Thanks, Kobi
Wait a minute, i'll upload to github.
There you go: https://github.com/kobi2187/jclass2nim
it does a good job but the output is simple, u need to tweak it - import the relevant types etc.
but in general it's a great accelerator.
feel free to use for whatever you need.