Search code examples
javaandroidc++android-ndkjava-native-interface

Android not able to find my JNI native library function


I used javah to generate a native JNI function:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ttm_zpay_zPayTestTool */

#ifndef _Included_com_ttm_zpay_zPayTestTool
#define _Included_com_ttm_zpay_zPayTestTool
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ttm_zpay_zPayTestTool
 * Method:    KiziStartTransaction
 * Signature: ()[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_ttm_zpay_zPayTestTool_KiziStartTransaction
  (JNIEnv * env, jobject)
{
   return env->NewByteArray(10);
}

#ifdef __cplusplus
}
#endif
#endif

For the following Java class:

package com.ttm.zpay;

public class zPayTestTool
{
   public native byte[] KiziStartTransaction();
}

I verified that the native function is successfully compiled into my final *.so file packaged with my APK. I did so by using readelf -Ws lib.so (readelf provided by the NDK):

5: 0015fa15    10 FUNC    GLOBAL DEFAULT    8 Java_com_ttm_zpay_zPayTestTool_KiziStartTransaction

In the logcat output, I get the following:

01-17 01:06:02.306  7017  7017 W dalvikvm: No implementation found for native Lcom/ttm/zpay/zPayTestTool;.KiziStartTransaction:()[B
01-17 01:06:02.306  7017  7017 D AndroidRuntime: Shutting down VM
01-17 01:06:02.311  7017  7017 W dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x40ccd930)
01-17 01:06:02.316  3556  3758 D AudioHardware: openPcmOut_l() mPcmOpenCnt: 0
01-17 01:06:02.321  7017  7017 E AndroidRuntime: FATAL EXCEPTION: main
01-17 01:06:02.321  7017  7017 E AndroidRuntime: java.lang.UnsatisfiedLinkError: Native method not found: com.ttm.zpay.zPayTestTool.KiziStartTransaction:()[B
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at com.ttm.zpay.zPayTestTool.KiziStartTransaction(Native Method)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at com.ttm.zpay.zPayActivity.OnKiziStartTransaction(zPayActivity.java:97)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at com.ttm.zpay.zPayActivity.access$1(zPayActivity.java:95)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at com.ttm.zpay.zPayActivity$1.onClick(zPayActivity.java:90)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at android.view.View.performClick(View.java:4204)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at android.view.View$PerformClick.run(View.java:17355)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at android.os.Handler.handleCallback(Handler.java:725)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:92)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:137)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:5041)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at java.lang.reflect.Method.invokeNative(Native Method)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Method.java:511)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
01-17 01:06:02.321  7017  7017 E AndroidRuntime:        at dalvik.system.NativeStart.main(Native Method)
01-17 01:06:02.331  3837  3848 W ActivityManager:   Force finishing activity com.ttm.zpay/.zPayActivity

What's even more odd is I already have another native method in this library using the same naming convention but for a different java class that works perfectly fine. It's only the one above that causes problems.

See the code for the working JNI function below.

Java:

package com.ttm.zpay;
public class zPayService extends Service
{
   public native boolean Initialize();
}

C++:

extern "C"
{
    JNIEXPORT bool JNICALL Java_com_ttm_zpay_zPayService_Initialize(JNIEnv* env, jobject obj)
    {
        // Do stuff
    }
}

So at the end of the day: Native methods mapped for my zPayTestTool java class do not work, but the one native method mapped to zPayService java class works fine.

What on earth am I doing wrong? Is this an issue with my AndroidManifest.xml? I'm out of ideas at this point and results on Google and other seemingly duplicate questions on SO aren't helping.


Solution

  • I figured out what the issue was. In my AndroidManifest.xml, I had the process attribute set to my <service> element but not <application>:

    <application
        android:name="com.ttm.zpay.zPayApplication"
        android:allowBackup="true"
        android:label="@string/app_name" 
        android:persistent="true" >
    
        <service
            android:name="com.ttm.zpay.zPayService"
            android:exported="true"
            android:process="com.ttm.zPayService" >
        </service>
    
        <activity
            android:name="com.ttm.zpay.zPayActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
    

    For whatever reason, this causes any JNI methods defined in the activity class or classes it uses to not work. I moved the process attribute to <application> and it seems to work now:

    <application
        android:name="com.ttm.zpay.zPayApplication"
        android:allowBackup="true"
        android:label="@string/app_name" 
        android:persistent="true"
        android:process="com.ttm.zPayService">
    
        <service
            android:name="com.ttm.zpay.zPayService"
            android:exported="true">
        </service>
    
        <activity
            android:name="com.ttm.zpay.zPayActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>