{"id":141,"date":"2012-04-29T22:39:43","date_gmt":"2012-04-30T02:39:43","guid":{"rendered":"http:\/\/jeffq.com\/blog\/?p=141"},"modified":"2012-04-29T22:40:21","modified_gmt":"2012-04-30T02:40:21","slug":"an-introduction-to-native-backed-objects-with-the-android-ndk","status":"publish","type":"post","link":"https:\/\/jeffq.com\/blog\/an-introduction-to-native-backed-objects-with-the-android-ndk\/","title":{"rendered":"An introduction to native backed objects with the Android NDK"},"content":{"rendered":"<p>As many Android developers know the <a href=\"http:\/\/developer.android.com\/sdk\/ndk\/index.html\">Android NDK<\/a> is used to cross-compile native (C\/C++) code to run in Android programs. Unfortunately, because it uses <a href=\"http:\/\/en.wikipedia.org\/wiki\/Java_Native_Interface\">JNI<\/a> we&#8217;re limited to a C-style call interface. Tools like <a href=\"http:\/\/www.swig.org\/\">SWIG<\/a> can be used to automatically generate wrappers for existing code including C++ classes. In this post I&#8217;ll provide an introduction to native backed objects in Java, which are objects whose implementation\/resources are primarily implemented in a native language. I won&#8217;t be using SWIG since it might muddle everything a bit because of what it takes to get it to wrap standard library classes on the NDK.<\/p>\n<p>What are we trying to accomplish? Our goal here is to have the ability to write Java code that operates on normal Java objects, but underneath all of the &#8220;magic&#8221; is happening in C\/C++ land. There are two main use cases for this: speed-ups from using faster native code for memory intensive\/processor intensive operations, and for leveraging existing code bases into a new Android project. Especially in the second case, native backed objects give you the ability to write UI code for a new Android project that uses your existing C++ classes as the main base of functionality; we simply create &#8220;wrapper&#8221; classes that can be used in Java land the same way you&#8217;ve been using them in C++ land, but now have the added benefit of being directly\u00a0inter-operable\u00a0with the Android framework. <!--more--><\/p>\n<p>In this small example, we&#8217;ll create a wrapper for std::string that implements the\u00a0<a href=\"http:\/\/docs.oracle.com\/javase\/1.5.0\/docs\/api\/java\/lang\/CharSequence.html\">CharSequence <\/a>interface. CharSequence is a scaled-down version of <a href=\"http:\/\/docs.oracle.com\/javase\/1.5.0\/docs\/api\/java\/lang\/String.html\">String<\/a>. While String is immutable and offers many helper functions, CharSequence boils this down to the simple functionality requirement of giving access to, well, a sequence of characters. Many Android framework functions take CharSequences instead of Strings to add flexibility to how exactly the strings are stored and represented in memory.\u00a0Here is a basic game plan for constructing your own native backed object classes:<\/p>\n<ul>\n<li>Write allocator functions for any constructors you may wish to call when newing objects.<\/li>\n<li>Write a deallocator function.<\/li>\n<li>For each member you wish to call, write a function that takes a pointer parameter as well as all parameters needed to call that function.<\/li>\n<\/ul>\n<p>Here is the source code for a simple wrapper for std::string:<\/p>\n<pre lang=\"cpp\">#include \r\n#include \r\n\r\n#define JNI(X) JNIEXPORT Java_std_string_##X\r\n#define CAST(X) reinterpret_cast(X)\r\n\r\nextern \"C\" \r\n{\r\n\r\njlong JNI(alloc)(JNIEnv*,jclass*)\r\n{\r\n\treturn reinterpret_cast(new std::string());\r\n}\r\n\r\nvoid JNI(delete)(JNIEnv*, jclass*, jlong ptr)\r\n{\r\n\tdelete CAST(ptr);\r\n}\r\n\r\njchar JNI(charAtImpl)(JNIEnv*, jclass*, jlong ptr, jint index)\r\n{\r\n\treturn CAST(ptr)->at(index);\r\n}\r\n\r\njint JNI(lengthImpl)(JNIEnv*, jclass*, jlong ptr)\r\n{\r\n\treturn CAST(ptr)->size();\r\n}\r\n\r\njint JNI(subSequenceImpl)(JNIEnv*, jclass*, jlong ptr, jint start, jint end)\r\n{\r\n\treturn reinterpret_cast(new std::string(CAST(ptr)->substr(start,end)));\r\n}\r\n\r\njstring JNI(toStringImpl)(JNIEnv* env, jclass*, jlong ptr)\r\n{\r\n\treturn env->NewStringUTF(CAST(ptr)->c_str());\r\n}\r\n\r\n}<\/pre>\n<p>What we&#8217;ve done here is essentially unroll the implicit &#8220;this&#8221; pointer inside C++ function calls. This pointer (passed as a jlong) is then cast to our target type and the appropriate member function called. The matching Java class looks like this:<\/p>\n<pre lang=\"java\">package std;\r\n\r\npublic class string implements CharSequence {\r\n\r\n\tprivate native static long alloc();\r\n\tprivate native static void delete(long ptr);\r\n\tprivate native static char charAtImpl(long ptr, int index);\r\n\tprivate native static int lengthImpl(long ptr);\r\n\tprivate native static long subSequenceImpl(long ptr, int start, int end);\r\n\tprivate native static String toStringImpl(long ptr);\r\n\r\n\tprivate final long ptr;\r\n\tprivate final boolean alloced;\r\n\r\n\tpublic string() {\r\n\t\tptr = alloc();\r\n\t\talloced = true;\r\n\t}\r\n\r\n\tpublic string(long _ptr, boolean _alloced) {\r\n\t\tptr = _ptr;\r\n\t\talloced = _alloced;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void finalize() {\r\n\t\tif(alloced)\r\n\t\t\tdelete(ptr);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char charAt(int index) {\r\n\t\treturn charAtImpl(ptr, index);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int length() {\r\n\t\treturn lengthImpl(ptr);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic CharSequence subSequence(int start, int end) {\r\n\t\treturn new string(subSequenceImpl(ptr, start, end), true);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn toStringImpl(ptr);\r\n\t}\r\n\r\n\tpublic long getPtr() {\r\n\t\treturn ptr;\r\n\t}\r\n\r\n}<\/pre>\n<p>The crux of the native backed object is the ptr member of the class. This holds the actual address (stored simply as a number; it&#8217;s cast in C++ land) to the instance of the object inside the C++ heap. For existing objects, this can be obtained by returning address-of as a long from a JNI function.<\/p>\n<p>An important distinction needs to be made when dealing with your already existed code: you must differentiate between objects that were allocated and are being managed by your underlying code that you are &#8220;spying&#8221; on, and &#8220;new&#8221; objects that were created inside Java land. This distinction is required if you leverage the <a href=\"http:\/\/docs.oracle.com\/javase\/1.5.0\/docs\/api\/java\/lang\/Object.html#finalize()\">finalize()<\/a> method to automatically free the C++ memory you created in Java land. In our above code this is solved by having two constructors: one that does a new and marks that we should delete the underlying memory when finalized, and a parameterized version that can explicitly be told if the the memory should be freed. If you are using this class to access an automatic (&#8220;stack&#8221;) member of an existing instance of a C++ class that will eventually go out of scope\/be deleted by your normal C++ code, you must use the second constructor (with the second parameter set to false) to avoid a double delete.<\/p>\n<p><strong><a href=\"http:\/\/jeffq.com\/jnistring.zip\">Example std::string native backed object project source<\/a><\/strong><\/p>\n<p>The above example source contains the wrapper class and C++ code, as well as a tiny test Activity that will perform some equivilent sprintf\/String.format work. If the C++ button is clicked, we do this:<\/p>\n<pre lang=\"cpp\">\t\r\nchar buff[32];\r\nstd::string* obj = CAST(ptr);\r\nsprintf(buff,\"%d\",rand());\r\n*obj = buff;\r\nif(obj->size() > 2)\r\n    *obj = obj->substr(1);\r\n*obj += \"!\";<\/pre>\n<p>While when in pure Java mode this code is called:<\/p>\n<pre lang=\"java\">\r\nfor(int i = 0 ; i < data.length ; ++i) { \r\n    final String s = String.format(\"%d\", rand.nextInt()); \r\n    if(s.length() > 2)\r\n        data[i] = \"!\" + s.substring(1);\r\n}<\/pre>\n<p>In either case, a CharSequence implementation can be assigned directly to a <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/TextView.html\">TextView<\/a>, resulting in such prettiness:<\/p>\n<p><a href=\"http:\/\/jeffq.com\/blog\/wp-content\/uploads\/2012\/04\/jnistring.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-medium wp-image-149\" title=\"jnistring\" src=\"http:\/\/jeffq.com\/blog\/wp-content\/uploads\/2012\/04\/jnistring-300x180.png\" alt=\"\" width=\"300\" height=\"180\" srcset=\"https:\/\/jeffq.com\/blog\/wp-content\/uploads\/2012\/04\/jnistring-300x180.png 300w, https:\/\/jeffq.com\/blog\/wp-content\/uploads\/2012\/04\/jnistring-500x300.png 500w, https:\/\/jeffq.com\/blog\/wp-content\/uploads\/2012\/04\/jnistring.png 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>If you&#8217;re looking to port over a large C++ project, definitely check out SWIG. I don&#8217;t personally use it much because a lot of the code I work on requires some massaging before it&#8217;s Android ready, but it&#8217;s certainly an execute tool that will save you lots of repetitive writing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As many Android developers know the Android NDK is used to cross-compile native (C\/C++) code to run in Android programs. Unfortunately, because it uses JNI we&#8217;re limited to a C-style call interface. Tools like SWIG can be used to automatically generate wrappers for existing code including C++ classes. In this post I&#8217;ll provide an introduction [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[10,13,14,15,11],"_links":{"self":[{"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/posts\/141"}],"collection":[{"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/comments?post=141"}],"version-history":[{"count":15,"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/posts\/141\/revisions"}],"predecessor-version":[{"id":157,"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/posts\/141\/revisions\/157"}],"wp:attachment":[{"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/media?parent=141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/categories?post=141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jeffq.com\/blog\/wp-json\/wp\/v2\/tags?post=141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}