ANDROID JNI TUTORIAL . If you want to know about ANDROID JNI TUTORIAL , then this article is for you.

ANDROID JNI TUTORIAL


Complete Android JNI Tutorial: Calling Native C/C++ Code from Java

In Android development, the Java Native Interface (JNI) allows Java code to interact with native code written in languages such as C or C++. JNI is often used when you need to optimize performance-intensive parts of your app, reuse existing native code libraries, or directly interact with hardware features that aren't accessible through Java APIs.

This tutorial will guide you step-by-step through the process of setting up JNI in an Android project, writing some simple native C++ code, and calling it from your Java code.


Why Use JNI?

JNI provides a bridge between Java and native code (written in C or C++). Some common reasons to use JNI in Android apps include:

  • Performance: Native code can perform faster than Java for certain tasks, such as image processing or computationally intensive calculations.
  • Reuse Existing Code: If you already have C or C++ code, you can use JNI to reuse that code in your Android application without rewriting it in Java.
  • Low-level Access: JNI gives access to platform-specific libraries and APIs that might not be available in the Java SDK.

Step 1: Create a New Android Project

  1. Open Android Studio and create a new project.
  2. Choose an Empty Activity template.
  3. Select Java as the programming language (or Kotlin if preferred).
  4. Enter your project name and choose a suitable minimum SDK.
  5. Finish the setup, and Android Studio will create a basic project for you.

Step 2: Add C++ Support to Your Project

  1. Enable C++ Support:
    • During project setup, you can choose C++ support. If you missed this step, you can add it later manually.
    • If you didn't enable C++ support initially, go to File > New > New Module, and select C++ under the Phone & Tablet section. This will create necessary files for JNI support.
  2. Folder Structure:
    • Android Studio creates a cpp directory under app/src/main/ where you will place your C/C++ source code. The project structure should look like this:
      app/
      └─ src/
          └─ main/
              ├─ java/
              ├─ cpp/
              └─ CMakeLists.txt
      

Step 3: Write Native C++ Code

Now, let's write some simple C++ code that we will call from Java using JNI. We’ll create a function that returns a simple string from C++ to Java.

  1. Create a New C++ Source File:

    • In cpp folder, create a new file called native-lib.cpp.
  2. Write the Native Function:

    Inside native-lib.cpp, define a simple C++ function to be accessed from Java:

    #include <jni.h>
    #include <string>
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv* env, jobject /* this */) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
    • JNIEXPORT and JNICALL are macros used to define the function signature in a way that JNI can recognize.
    • Java_com_example_myapplication_MainActivity_stringFromJNI is the function name following JNI naming conventions:
      • Java_ prefix
      • Package path: com_example_myapplication
      • Class name: MainActivity
      • Function name: stringFromJNI
    • JNIEnv* env is used to interact with Java objects.
    • NewStringUTF creates a new Java String from the C++ string hello.

Step 4: Declare the Native Method in Java

In MainActivity.java (or MainActivity.kt if using Kotlin), declare the native method that will be implemented in the C++ code.

  1. Open MainActivity.java and add the following:

    package com.example.myapp;
    
    import android.os.Bundle;
    import androidx.appcompat.app.AppCompatActivity;
    
    public class MainActivity extends AppCompatActivity {
    
        // Declare the native method
        public native String stringFromJNI();
    
        // Load the native library
        static {
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // Call the native method and print the result
            String result = stringFromJNI();
            System.out.println(result);  // Output: "Hello from C++"
        }
    }
    
    • native: The keyword native is used in Java to declare a method whose implementation is in native code.
    • System.loadLibrary("native-lib"): This loads the shared library (compiled .so file). It should match the name of the .so file that we will generate from the C++ code.
    • The stringFromJNI() method will call the C++ code and return the string to Java.

Step 5: Configure CMake to Build Native Code

CMake is used to build native C++ code. To configure CMake for building the C++ code, we need to edit the CMakeLists.txt file.

  1. Open CMakeLists.txt and ensure it contains the following:

    cmake_minimum_required(VERSION 3.4.1)
    
    # Add your native source files here
    add_library(native-lib SHARED
                src/main/cpp/native-lib.cpp)
    
    # Find the log library for Android logging
    find_library(log-lib log)
    
    # Link the log library with your native-lib
    target_link_libraries(native-lib
                          ${log-lib})
    
    • add_library(native-lib SHARED src/main/cpp/native-lib.cpp) tells CMake to compile native-lib.cpp and generate a shared library (native-lib.so).
    • find_library(log-lib log) finds the Android log library to link with your native code for logging purposes.
    • target_link_libraries(native-lib ${log-lib}) links the native library with the Android log library.

Step 6: Build and Run the App

  1. Build the Project:
    • Click Build > Make Project to compile the C++ code into the shared library.
  2. Run the App:
    • Click Run to launch the app on a connected device or emulator.
    • The app will display "Hello from C++" in the console, as the stringFromJNI() method calls the C++ function and returns the result to Java.

Step 7: JNI Directory Structure

To better understand how the JNI integration works in Android Studio, here is the expected project structure:

app/
 ├─ src/
 │   ├─ main/
 │   │   ├─ java/
 │   │   │   └─ com/
 │   │   │       └─ example/
 │   │   │           └─ myapplication/
 │   │   │               └─ MainActivity.java
 │   │   ├─ cpp/
 │   │   │   └─ native-lib.cpp
 │   │   └─ CMakeLists.txt
 └─ build.gradle
  • native-lib.cpp: The C++ source file with the JNI method.
  • CMakeLists.txt: The CMake configuration file to build the native code.
  • MainActivity.java: The Java activity that declares and calls the native method.

Step 8: Additional JNI Features and Tips

  1. Pass Data Between Java and Native Code:

    • JNI allows you to pass data between Java and native code. For example, you can pass primitive types like int, boolean, float, or even complex types like arrays and objects.
    • You can use JNI functions like GetIntArrayElements, GetStringUTFChars, and SetObjectField to work with Java objects from C++.
  2. Handle Errors in JNI:

    • When dealing with JNI, always check for errors. For example, if you call a JNI function and it fails, you can use env->ExceptionCheck() and env->ExceptionClear() to handle exceptions.
  3. Use the Android NDK:

    • If you’re working with more complex native libraries or need to perform low-level tasks like interfacing with hardware, the Android NDK (Native Development Kit) is a useful tool. The NDK allows you to write and compile native code using tools such as CMake or ndk-build.

Conclusion

JNI provides a powerful way to use C/C++ code in Android applications. By following this tutorial, you learned how to set up JNI in an Android project, write native C++ code, call it from Java, and configure your project using CMake. JNI is especially useful for performance-critical code or reusing existing libraries, but it does require careful handling of memory and exceptions to avoid issues.

Now that you've mastered the basics, you can start building more complex JNI features and take full advantage of native code in your Android apps.