Porting a library as a static library module with the Android NDK build system

The previous recipe discussed how to port a library as a shared library module with the libbmp library as an example. In this recipe, we will demonstrate how to port the libbmp library as a static library.

Getting ready

Readers are recommended to read the Building an Android NDK application at the command line recipe in Chapter 3, Build and Debug NDK Applications, before going through this one.

How to do it...

The following steps describe how to create our sample Android project that demonstrates porting the libbmp library as a static library:

  1. Create an Android application named PortingStatic with native support. Set the package name as cookbook.chapter8.portingstatic. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface, if you want more detailed instructions.
  2. Add a Java file MainActivity.java under the cookbook.chapter8.portingstatic package. This Java file simply loads the shared library PortingStatic, and calls the native method naCreateABmp.
  3. Follow step 3 of the Porting a library as shared library module with the Android NDK build system recipe to download the libbmp library and make changes.
  4. Create an Android.mk file under the libbmp folder to compile libbmp as a static library libbmp.a. The content of this Android.mk file is as follows:
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := libbmp
    LOCAL_SRC_FILES := bmpfile.c
    include $(BUILD_STATIC_LIBRARY)
  5. Create another folder libbmptest under the jni folder. Add the mylog.h and PortingStatic.c files to it. Note that the code for it is the same as the naCreateABmp method in previous chapter except that the .bmp file name is changed from test_shared.bmp to test_static.bmp.
  6. Create another Android.mk file under the libbmptest folder to compile the PortingStatic.c file as a shared library libPortingStatic.so. The content of this Android.mk file is as follows:
    LOCAL_PATH := $(call my-dir
    include $(CLEAR_VARS)
    LOCAL_MODULE    := PortingStatic
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libbmp/
    LOCAL_SRC_FILES := PortingStatic.c
    LOCAL_STATIC_LIBRARIES := libbmp
    LOCAL_LDLIBS := -llog
    include $(BUILD_SHARED_LIBRARY)
  7. Create an Android.mk file under the jni folder with the following content:
    LOCAL_PATH := $(call my-dir)
    include $(call all-subdir-makefiles)
  8. Add the WRITE_EXTERNAL_STORAGE permission to the AndroidManifest.xml file as follows:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  9. Build and run the Android project. A bitmap file test_static.bmp should be created at the sdcard folder of the Android device. We can use the following command to get the file:
    $ adb pull /sdcard/test_static.bmp .

    This file is the same as the test_static.bmp file used in the previous recipe.

How it works...

In the sample project, we build libbmp as a static library libbmp.a, which can be found under obj/local/armeabi/ folder. We called the functions defined in libbmp in the native code PortingStatic.c.

A static library is simply an archive of object files compiled from the source code. They are built as files ending with ".a" suffix at Android NDK. A static library is copied into a targeted executable or library at build time by a compiler or linker. At Android NDK, static libraries are only used to build the shared libraries, because only shared libraries are packaged into the apk file for deployment.

Our sample project builds a static library libbmp.a and a shared library libPortingStatic.so. The libPortingStatic.so shared library is located under the libs/armeabi folder, and will be copied to the application's apk file. The libbmp.a library is used to build the libPortingStatic.so shared library. If you examine the symbols of the libPortingStatic.so library with the Eclipse project explorer, you will find that the symbols for the functions defined at libbmp are included. This is shown in the following screenshot:

How it works...

The functions bmp_create, bmp_destroy, and so on, are defined in libbmp and are included in the shared library libPortingStatic.so.

In our Java code, we will need to load the shared library with the following code:

static {
       System.loadLibrary("PortingStatic");
}

Understand the Android.mk files: The previous recipe already describes most of the predefined variables and macros used in the three Android.mk files in this recipe. Therefore, we only cover the ones that we have not seen in the previous recipe:

  • BUILD_STATIC_LIBRARY: The variable points to a build script which will collect the information about the module and determine how to build a static library from the sources. The module built is usually listed in LOCAL_STATIC_LIBRARIES of another module. This variable is normally included in Android.mk as follows:
    include $(BUILD_STATIC_LIBRARY)

    In our sample project, we include this variable in the Android.mk file, under jni/libbmp folder.

  • LOCAL_STATIC_LIBRARIES: This is a module description variable, which provides a list of static libraries the current module should be linked to. It only makes sense in shared library modules.

    In our project, we used this variable to link to the libbmp.a static library, as shown in the Android.mk file under the jni/libbmptest/ folder.

    LOCAL_STATIC_LIBRARIES := libbmp
  • LOCAL_WHOLE_STATIC_LIBRARIES: This is a variant of the LOCAL_STATIC_LIBRARIES variable. It indicates that the static libraries listed should be linked as whole archives. This will force all object files from the static libraries to be added to the current shared library module.

Static versus shared: Now that you have seen how to port an existing library as either a static or shared library, you may ask which one is better. The answer, as you might have expected, depends on our needs.

When you port a big library and only use a small portion of the functions provided by the library, then a static library is a good option. The Android NDK build system can resolve the dependencies at build time and only copy the parts that are used in the final shared library. This means a smaller library size and subsequently smaller apk file size.

Note

Sometimes, we need to force the entire static library to be built into the final shared library (for example, there are circular dependencies among several static libraries). We can use the LOCAL_WHOLE_STATIC_LIBRARIES variable at Android.mk or the "--whole-archive" linker flag.

When you port a library, which will be used by several Android apps, then shared library is a better choice. Suppose you want to build two Android apps, a video player and a video editor. Both apps will need a third-party codec library, which you can port to Android with NDK. In this case, you can port the library as a shared library in a separate apk file (for example, MX Player puts the codecs library in separate apk files) and the two apps can load the same library at runtime. This means that the users only need to download the library once to use both the apps.

Another case in which you may need the shared library is that a library L is used in multiple shared libraries. If L is a static library, each shared library will include a copy of its code and cause problems because of code duplication (for example, duplicated global variables).

See also

We have actually ported a library as a static library with Android NDK build system before. Recall how we ported libpng as a static library in the Managing assets at Android NDK recipe in Chapter 5, Android Native Application API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset