GreenFlash
GreenFlash

Reputation: 163

How to use .so file generated from one Android application, and use it in another app?

I am new in NDK development, I created project with C++ support. However i just added 1 cpp class and generated .SO file. And tried to access that so file in another project. I refer many stackoverflow links, Blogs and Google developer site. But not able to resolve it. Here is my code.

local.properties

ndk.dir=/home/android/Android/Sdk/ndk/20.1.5948944
sdk.dir=/home/android/Android/Sdk

src/main/cpp/CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        hello_world_lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        hello_world_lib.cpp
        HelloWorld.cpp
        )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        hello_world_lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

src/main/cpp/hello_world_lib.cpp

#include <jni.h>
#include <string>
#include <bitset>
#include "HelloWorld.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_dc_testapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jint JNICALL
Java_com_dc_testapplication_MainActivity_getSumOfValues(JNIEnv *env,
                                                        jobject,jint a ,jint b,jstring sign){

    // working Solution
    const char *nativeString = env->GetStringUTFChars(sign, 0);


    HelloWorld helloWorld;
    helloWorld.setValues((int)a,(int)b);
    helloWorld.setOperationType(*nativeString);

    jint mData = (jint)helloWorld.getSumOf2Values();
    return mData;
}

I got so files from here after build/run application. app/build/intermediates/cmake/debug/obj/{abis}/{nameoflib}.so

Now i need to access these .so files in another project. And its package name is com.dc.usendklib. Now here is my new project Code.

app/libs/{abi folder names}/.so files. src/main/jniLibs/{abi folder names}/.so

src/main/jniLibs/Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello_world_lib-prebuilt
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libhello_world_lib.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)

src/main/jniLibs/Application.mk

APP_ABI := armeabi-v7a arm64-v8a x86

app's gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"

    sourceSets.main {
        jniLibs.srcDir 'src/main/jniLibs' //set .so files location to libs
        jni.srcDirs = [] //disable automatic ndk-build call
    }

    defaultConfig {
        applicationId "com.dc.usendklib"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            moduleName "hello_world_lib"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

//    sourceSets {
//        main {
//            // let gradle pack the shared library into apk
//            jniLibs.srcDirs = ['src/main/jniLibs','src/main/CPP']
//        }
//    }

//    sourceSets.main {
//        jniLibs.srcDir 'src/main/jniLibs'
//    }

//    externalNativeBuild {
//        cmake {
//            path "src/main/cpp/CMakeLists.txt"
//            version "3.10.2"
//        }
//    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

And when i run this, i am getting this error.

2020-01-03 11:44:54.946 17235-17235/com.dc.usendklib E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.dc.usendklib, PID: 17235
    java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.dc.usendklib.MainActivity.stringFromJNI() (tried Java_com_dc_usendklib_MainActivity_stringFromJNI and Java_com_dc_usendklib_MainActivity_stringFromJNI__)
        at com.dc.usendklib.MainActivity.stringFromJNI(Native Method)
        at com.dc.usendklib.MainActivity.onCreate(MainActivity.kt:14)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

MainActivity.kt

package com.dc.usendklib

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Example of a call to a native method
        sample_text.text = stringFromJNI()
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    external fun stringFromJNI(): String

    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("hello_world_lib")
        }
    }
}

Now my questions are,

  1. The way i generate and pick .so files, is that a correct way?

  2. JNI class methods uses Java_{package}_method, So after generate .so, is that possible to use that files in another project?

  3. If yes, please tell me how. Any help will be appreciated. Thanks in advance.

Upvotes: 1

Views: 5632

Answers (2)

Mukesh Wondersoft
Mukesh Wondersoft

Reputation: 434

Yes you can access your so file to other project. but your project directory must be same as your old project. in your case project directory must be like this :

com/dc/testapplication/MainActivity.class And your MainActivity.class must have getSumOfValues method.

Upvotes: 2

Bruno
Bruno

Reputation: 4007

Java_com_dc_testapplication_MainActivity_stringFromJNI

It supposed that your "wrapper" class in MainActivity from the package com.dc.testapplication

But, as far as I can see in your post, your MainActivity is located in com.dc.usendklib. That's why you have an UnsatisfiedLinkError.

You can have a look at my case to understand how it works

Upvotes: 1

Related Questions