📜 ⬆️ ⬇️

Self-mapping of C ++ classes to JVM via JNI

It so happened that recently at work I needed to port an old native Android application. The application is written mainly in C / C ++. I wanted to do it correctly and in a civilized way. Actually about it under the cut


The application was written mainly in C and C ++. But in some places even an assembler was used. Since I myself prefer to always use the object language, there was a desire to bind Java code to a positive library.

But then there were difficulties. The officially supported way to bind the native code in both the original jvm and in Dalvik is JNI, and as you know it is implemented through C functions. In this connection, I had to digress a little and convert the execution flow twice: from Java objects into a callback and from a code into positive objects.
')
Of course, I was looking in the direction of ready-made solutions, but as a rule, their license did not allow them to be used in a proprietary product. The task was facilitated by the fact that the Java part and the interface for it in the nativ I did myself, and therefore could simplify my life for myself.

As a result, this is what I invented (of course, you can see the old wheel in this). All Java objects, when binding them to the native, are arranged in a hierarchy, that is, they are inherited from the same ancestor class NativeObject. This class carries the main logic for the maintenance of functions common to all associated classes. He goes to the class with the same name in the plus part. Priority methods for implementation:


The latter method is necessary, since the reverse methods for accessing the fields of an object are 3 times less productive than a direct call. After that, in each successor class, we implement the constructor() (by itself) and the static initializer to load the class's metadata into the native, it also maps

In C ++, we implement a constructor with a list of parameters pre-negotiated for all heirs, among them the easiest way is to immediately transfer a reference to a Java object. In general, for all the hider we have prepared a template function-factory for native objects. We also define it (specialize).

Now the whole process of creating an object goes like this:


After that, we have a Java object ready for work, its native methods automatically go through JNI, and there, in turn, gets a pointer to a native object, it is cast and calls the corresponding C ++ method.

This is how the binding works in general terms.

Now I’ll tell you a little about other aspects of working with pluses in Java in general and in Android in particular.
In Java, erroneous situations are accepted to be sent via the exception channel. Therefore, C-style error handling is initially very difficult to combine with such an approach. Naturally, support for exceptions in the native was suggested. What was done. Unfortunately, the pluses in Android are still very poorly supported. The same exceptions are not supported by the default library. Therefore, I immediately recommend switching to gnu-libstdc ++. If you use the GNU toolkit, then its license is quite suitable for any projects.

So, all the exceptions in the native are caught and wrapped by the heirs of std :: exception. Similarly, all sishnye errors are wrapped in std :: exception and sent out. Here, on the way to sishny callbacks, the anti-aircraft catch interrupts their flight and turns it into a Java exception. It is necessary to remind those who write mostly in Java, that with positive exceptions it is necessary to apply very carefully, otherwise the anti-aircraft installations will undermine themselves.

And finally, I wanted to scold Android support, or rather its absence, for string classes with wide characters. Here I had to simply override std :: wstring by re-enabling the standard headers. After this surgery, there were no problems with wide strings.

And of course when using pluses, the management of java-machine resources is greatly simplified, and this is not only memory, but also references to objects - analogues of different handles in different systems. The use of a boost for managing the lifetime has helped a lot: as you know, objects in Java should live until they are killed by the garbage collector.

Good luck.

Source: https://habr.com/ru/post/165165/


All Articles