Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2108669
  • 博文数量: 413
  • 博客积分: 10926
  • 博客等级: 上将
  • 技术积分: 3862
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-09 18:14
文章分类

全部博文(413)

文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(6)

2011年(138)

2010年(85)

2009年(42)

2008年(46)

2007年(26)

2006年(59)

分类: Java

2009-03-05 13:17:06

  1. Docs
    1. Java Native Interface Specification—Contents
    2. Java Native Interface: Programmer's Guide and Specification
      (include more details and some usefule sample)
    3. Jni Tips
      http://developer.android.com/guide/practices/design/jni.html

      Including:
      • Local and Global References
      • Threads
      • UTF-8 and UTF-16 Strings
      • Primitive Arrays
      • Exception
      • Native Libraries

    4. xxx
  2. Basic
    • what's JNI
      The JNI(Java Native Interface) is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly. For Android, JNI can be used from 1.5 and later, and Android 2.3 introduces NativeActivity that enable you to implement an activity purely in native code.
    • why to  use JNI
      • The standard Java class library does not support the platform-dependent features needed by the application.
      • You already have a library written in another language, and wish to make it accessible to Java code through the JNI.
      • You want to implement a small portion of time-critical code in a lower-level language such as assembly.
    • what can be done in JNI
      • Create, inspect, and update Java objects (including arrays and strings).
      • Call Java methods.
      • Catch and throw exceptions.
      • Load classes and obtain class information.
      • Perform runtime type checking.
    • prototype of native method

      Java__
      (
           JNIEnv *env,        /* JNI interface pointer */
           jobject obj,        /* "this" pointer */
           jint i,             /* argument #1 */
           jstring s           /* argument #2 */
      )     

      Special comments:
      a) The first argument - JNI interface pointer
      The JNI interface is organized like a C++ virtual function table or a COM interface. An JNI interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function. Every interface function is at a predefined offset inside the array. Figure at http://download.oracle.com/javase/6/docs/technotes/guides/jni/spec/images/designa.gif illustrates the organization of an interface pointer. The JNI interface pointer is only valid in the current thread. A native method, therefore, must not pass the interface pointer from one thread to another.

      b) The second argument - "this" pointer:
      this argument differs depending on whether the native method is static or nonstatic. For a nonstatic native method, it is a reference to the instance object of it Java class. For a static native method is a reference to its Java class.   

      c) Argument referencing style
      Primitive types, such as integers, characters, and so on, are copied between Java and native code. Arbitrary Java objects, on the other hand, are passed by reference. The VM must keep track of all objects that have been passed to the native code, so that these objects are not freed by the garbage collector.
    • xxx
  3. Develop procedure
    1. Create a Java class that declares the native method.

    Notes:
    You need to completes the following tasks in the wrapper class:
       
    * Declare native interfaces exported from shared library
        * Load the shared library implemented in C/C++ will

    ~/AndroidTest/JNITest$ cat com/mydomain/jprint.java
    package com.mydomain;
    public class jprint
    {
        public native void print();  ////Keyword 'native' means the method is exported from JNI library
        static
        {
            //the following statement will load shared library libjnilibs.so on Linux (or libjnilibs.dll on Windodws). For Android, the library must be unpacked into /data/data//lib/libjnilibs.so at installation time by package manager。
            System.loadLibrary("jnilibs");
        }
    }

    2. Generate header file for native code
    Compile the .java file to .class
    ~/AndroidTest/JNITest$ cd src/com/mydomain && javac jprint.java
    or
    ~/AndroidTest/JNITest$ cd src/com/mydomain && javac -classpath /path/to/android.jar gen/path/to/R.java jprint.java
    A file named "jprint.class" is generated.

    Generate header file
    ~/AndroidTest/JNITest$ javah -jni jprint
    A file named "com_mydomain_jprint.h" is generated,you can rename it to "jprint.h".

    or
    ~/AndroidTest/JNITest$ cd src
    ~/AndroidTest/JNITest$ javah -jni com.xxx.xxx.JPrint

    3. Implement all interfaces declared in the header files "jprint.h" with C/C++
    gliethttp@Leith:~/Android$ cat hello.c
    #include
    #include "jprint.h"

    JNIEXPORT void JNICALL Java_jprint_print(JNIEnv *env, jobject obj)
    {
        printf("hello,Android!\n###Leith_gliethttp.\n");
    }

    5. Build native shared library
    First, add the binary directory of Android NDK "ANDROID_NDK_PATH>/build/prebuilt/linux-x86/arm-eabi-4.2.1/bin" to environment
    Then compile the native code as following:
    ~/AndroidTest/JNITest$ arm-eabi-gcc -fPIC -I/build/platforms/android-1.5/arch-arm/usr/include -nostdlib -Wl,-soname,libjnilibs.so -Wl,-Bsymbolic -Wl,--whole-archive -Wl,--no-whole-archive -Wl,--no-undefined -shared -o libjnilibs.so hello.c


    for new ndk, you should use arm-linux-androideabi-gcc or arm-linux-androideabi-g++

    Comments:
    CFLAGS += -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -O2 -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID

    7. Implement a java code to call native interface via wrapper class "jprint"
    gliethttp@Leith:~/Android$ cat gliethttp_hello.java
    public class gliethttp_hello
    {
        public static void main(String[] args)
        {
            jprint p = new jprint();
            p.print();
        }
    }

    8, Build Java package
      * Create libs/armeabi/ under project directory("libs" is under the same directory with directory "res","src");
      * Copy native shared library libjnilibs.so to direcotry libs/armeabi/
      * Press F5 to update files on Package Explorer Window of Eclipse
      * Build project via Eclipse

    Note: If you build JNI shared libraries for x86, you need to copy thoese libraries to libs/x86 rather than libs/armeabi
    9. Run
      * Install the installation package
      * Launch the application


    10, Tips
    10.1 Use class type defined in Java application in C++ native code
      * Get a class type according to class name
    JNIEXPORT void JNICALL com_mydomain_utils_MyUtils(JNIEnv *env, jobject thisObj)
    {
        ......
        jclass cls = env->FindClass(thisObj, "com.mydomain.MyClass");
        ......
    }
      * Get a class type according to object
    JNIEXPORT void JNICALL com_mydomain_utils_MyUtils(JNIEnv *env, jobject thisObj, jobject myObject)
    {
        ......
        jclass cls = env->GetObjectClass(myObject);
        ......
    }

    10.2 Transfer data from native code to Java via parameters
    * In Java code
    1) Define result class
    package com.mydomain.utils;
    public class MyResults
    {
       public int iData;
       public MyResults()
       {
       }

       public void setData(int iData)
       {
            this.iData = iData;
       }
    }
    2) Call
       MyResults retValue = new MyResults();
       xxx->CallFunc(retValue); //Call native interface which will transfer data back to Java code via parameter
       System.println("Data is = %d\r\n", retValue.iData);

    * In native code
    JNIEXPORT void JNICALL com_mydomain_utils_CallFunc(JNIEnv *env, jclass thisObj, jobject objRet)
    {
        jclass cls = env->GetObjectClass (objRet);  
        jmethodID mid = env->GetMethodID (cls, "setValue", "(I)V");
        env->CallVoidMethod (objRet, mid, 20);  //call setValue of objRet which of type MyResults to set iData
    }

    11, Trouble Shooting11.1
    Issue:
       ERROR: failed to link libnativehelper.so
    Solution:
       A) Maybe more than one copies of library files exist, please check /data/data//lib and /system/lib to confirm that.
       B) Relaunch the emulator, or flash correct fireware(Fireware verions is not compatible).

    11.2
    Issue:
        ERROR: could not load "libxxx.so"
    Issue:
        A) The library file format is not arm eabi elf, in other words, you didn't build the library with arm-eabi-gcc;
        B) You forgot to install the native shared library;
        C) A library with the name of your library file exists in the system, such as libutils.so, but the original library doesn't export the interfaces you called in application.

    11.3
    For cracked device, or emulator, except package library files, you can transmit them to /system/lib directly:
    $ adb remount
    $ adb push libxxx.so /system/lib

    11.4 Depended library
    If liba.so is loaded in Java App, and liba.so depends on libb.so, but libb.so must be installed to /system/lib, otherwise, liba.so can't be loaded in java application, there are two solutions:
      * Copy libb.so to /system/lib/ manually.
      * Install liba.so and libb.so to /data/data//lib/, but invoke interfaces exported in libb.so using dlopen/dlclose/dlsym.
     
    11.5
    Error
       App can't run, it crushed at loading libraries
    Solution:
       1) The library file format is not arm eabi elf, in other words, you didn't build the library with arm-eabi-gcc;

    11.6
    Error:
        Application crushes when calls a interface via jni
    Solution:
        1) The library doesn't export the interface, or the interface is not implemented, or the interface implementation contains critical error.

    12. There is sample in Android NKD, just run "make -f GNUMakefile APP=hello-jni V=1" to build the sample.
  4. Data Type
    In native library, in order to pass some arguments to Java layer, or get value from Java layer, you should use native type(such as jint, jlong, jbyte) rather than common c/c++ type (such as int, long, char). for example, to invoke the method "int read(byte[] buf, int offset, int len)" in native library:
    char buf[100];
    jmethodID midRead;
    jobject obj;
    jbyteArray bytes = env->NewByteArray(sizeof(buf));
    jint len = env->CallIntMethod(obj, midRead, bytes, 0, (jint)(sizeof(buf))); //cast the type of sizeof(buf) to jint, and return type should be jint
    if (len > 0)
    {
        env->GetByteArrayRegion(bytes, 0, len, (jbyte *)buf);
    }

    More data type, please refer to
  5. JNI function
    How to invoke JNI funtion in C/C++
    =======================================
    Suppose there is an JNI function
    const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
    Invoked in C: 
        jstring s = xxxxxxxx;
        const char *str = (*env)->GetStringUTFChars(env, s, 0);
        (*env)->ReleaseStringUTFChars(env, s, str); //(*env)->A_JNI_FUN(env,...)

    Invoked in C++:
        jstring s = xxxxxxxx;
        const char *str = env->GetStringUTFChars(s, 0);//env->A_JNI_FUN(...)
        env->ReleaseStringUTFChars(s, str);

    Usefule JNI functions
    =======================================
    Class Operations
    ----------------------------------
      FindClass: loads a class with the spedified name.
      GetSuperclass:
      IsAssignableFrom: Determines whether an object of clazz1 can be safely cast to clazz2.

     
    Global and Local References
    ----------------------------------
      NewLocalRef: Creates a new local reference that refers to the same object as ref. GC will feed the obj automatically, you can also invoke DeleteLocalRef to release it explicitly
      DeleteLocalRef:
      NewGlobalRef: Creates a new global reference to the object referred to by the obj argument. The obj argument may be a global or local reference. Global references must be explicitly disposed of by calling DeleteGlobalRef().
      DeleteGlobalRef:
      NewWeakGlobalRef: Creates a new weak global reference
      DeleteWeakGlobalRef:
      PushLocalFrame: Creates a new local reference frame, in which at least a given number of local references can be created
      PopLocalFrame: Pops off the current local reference frame, frees all the local references


    Object Operations
    ----------------------------------
      AllocObject: Allocates a new Java object without invoking any of the constructors for the object
      NewObject/NewObjectA/NewObjectV: Constructs a new Java object. The method ID indicates which constructor method to invoke. This ID must be obtained by calling GetMethodID() with as the method name and void (V) as the return type. eg: cid = (*env)->GetMethodID(env, stringClass, "", "()V");  (*env)->NewObject(env, stringClass, cid, elemArr);
      GetObjectClass: Returns the class of an object.
      IsInstanceOf: Tests whether an object is an instance of a class.
      IsSameObject: Tests whether two references refer to the same Java object.

     
    Accessing Fields of Objects
    ----------------------------------
    1, Object fields
      GetFieldID:
      GetField: Returns the value of an instance (nonstatic) field of an object. The field to access is specified by a field ID obtained by calling GetFieldID().
      SetField: Sets the value of an instance (nonstatic) field of an object. The field to access is specified by a field ID obtained by calling GetFieldID().
    2, Static fields
      GetStaticFieldID:
      GetStaticField:
      SetStaticField:



    Calling Instance Methods
    ----------------------------------
    1, Object fields
      GetMethodID: Returns the method ID for an instance (nonstatic) method of a class or interface. To obtain the method ID of a constructor, supply as the method name and void (V) as the return type.
      CallMethod/CallMethodA/CallMethodV: Call a Java instance method from a native method
      CallNonvirtualMethod/CallNonvirtualMethodA/CallNonvirtualMethodV:invoke an instance (nonstatic) method on a Java object, according to the specified class and method ID
    2, Static fields
      GetStaticMethodID
      CallStaticMethod/CallStaticMethodA/CallStaticMethodV


    String Operations
    ----------------------------------
    1, UNICODE characters:
      NewString: Constructs a new java.lang.String object from an array of Unicode characters.
      GetStringLength: Returns the length (the count of Unicode characters) of a Java string.
      GetStringRegion: Copies len number of Unicode characters beginning at offset start to the given buffer buf.
      GetStringChars: Returns a pointer to the array of Unicode characters of the string, This pointer is valid until ReleaseStringchars() is called.
      ReleaseStringChars:

    2, UTF-8 characters 
      NewStringUTF: Constructs a new java.lang.String object from an array of characters in modified UTF-8 encoding.
      GetStringUTFLength:
      GetStringUTFChars:
      ReleaseStringUTFChars:
      GetStringUTFRegion:


    Array Operations
    ----------------------------------
      GetArrayLength:
      NewObjectArray:
      GetObjectArrayElement: Returns an element of an Object array.
      SetObjectArrayElement: Sets an element of an Object array.
      NewArray: Construct a new primitive array object, ex: NewBooleanArray/NewShortArray
      GetArrayElements: Return the body of the primitive array, the result is valid until the corresponding ReleaseArrayElements() function is called
      ReleaseArrayElements: Informs the VM that the native code no longer needs access to
      GetArrayRegion: Copies a region of a primitive array (Java array, such as int[] ) into a buffer (Native buffer, such int *).
      SetArrayRegion: Copies back a region of a primitive array from a buffer.


    Exception
    ----------------------------------
      Throw: Causes a java.lang.Throwable object to be thrown.
      ThrowNew: Constructs an exception object from the specified class with the message specified by message and causes that exception to be thrown.

  6. How to process if JNI library depends on other libraries
    • Scenario 1
      libjni.so -> liba.so -> libb.a
      - Link libb.a when to build liba.so (append -lb to LDFLAG)
      - Link liba.so when to build libjni.so (append -la to LDFLAG)
      - Copy liba.so and libjni.so to libs/armeabi/
      - Load libraries in .java in the following order
             static
             {
                 System.loadLibrary("a");  //Must load liba.so before libjni.so
                 System.loadLibrary("jni");
             }
    • Scenario 2
      libjni.so -> libx.so and liby.so
      libx.so -> liby.so
      liby.so -> libz.so
      - Link libz.so when to build liby.so (append -lz to LDFLAG)
      - Link liby.so when to build libx.so (append -ly to LDFLAG)
      - Link libx.so and liby.so when to build libxjni.so (append -lx -ly to LDFLAG)
      - Copy libx.so, liby.so, libz.so and libjni.so to libs/armeabi/
      - Load libraries in .java in the following order
             static
             {
                 System.loadLibrary("z");
                 System.loadLibrary("y");
                 System.loadLibrary("x");
                 System.loadLibrary("jni");
             }
  7. The signature of type 'void' is 'V'
  8. NewString/NewStrinUTF
    Must check if the char/jchar argument is NULL, if so, don't call these APIs, since the return value is wrong (the return value is not NULL);
  9. Refer to inner class in jni
    Use the class name 'OuterClass$InnerClass' rether than 'OuterClass.InnerClass'.
  10. Use exception
    You can't use try-catch in jni, you must use exceptionXXX functions instead
  11. Register Natives: Binde Java method with JNI function by yourself, that means the name of JNI function needs not to be Java_com_xxx_xxx_your_java_method_name
    ///////////////////
    //In Jni code
    static jint
    add(JNIEnv *env, jobject thiz, jint a, jint b) {
    int result = a + b;
        LOGI("%d + %d = %d", a, b, result);
        return result;
    }

    static const char *classPathName = "com/example/android/simplejni/Native";

    static JNINativeMethod methods[] = {
      {"add", "(II)I", (void*)add },
    };

    /*
     * Register several native methods for one class.
     */
    static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods)
    {
        jclass clazz;

        clazz = env->FindClass(className);
        if (clazz == NULL) {
            LOGE("Native registration unable to find class '%s'", className);
            return JNI_FALSE;
        }
        if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
            LOGE("RegisterNatives failed for '%s'", className);
            return JNI_FALSE;
        }

        return JNI_TRUE;
    }

    /*
     * Register native methods for all classes we know about.
     *
     * returns JNI_TRUE on success.
     */
    static int registerNatives(JNIEnv* env)
    {
      if (!registerNativeMethods(env, classPathName,
                     methods, sizeof(methods) / sizeof(methods[0]))) {
        return JNI_FALSE;
      }

      return JNI_TRUE;
    }


    /////////////////////////////////////
    //in Java Code
    package com.example.android.simplejni;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;

    public class SimpleJNI extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            TextView tv = new TextView(this);
            int sum = Native.add(2, 3);
            tv.setText("2 + 3 = " + Integer.toString(sum));
            setContentView(tv);
        }
    }

    class Native {
        static {
            // The runtime will add "lib" on the front and ".o" on the end of
            // the name supplied to loadLibrary.
            System.loadLibrary("simplejni");
        }

        static native int add(int a, int b);
    }
  12. Build execubatle file
    1. Link shared libraries
      LDALGS += -nostdlib -Wl,-dynamic-linker,/system/bin/linker \ /build/prebuilt/linux-x86/arm-eabi-4.2.1/arm-eabi/lib/ldscripts/armelf.x \             /build/platforms/android-1.5/arch-arm/usr/lib/crtbegin_dynamic.o \             /build/platforms/android-1.5/arch-arm/usr/lib/crtend_android.o

      LIBS += -lc -ldl

    2. Link static libraries
      LDALGS += -nostdlib -static  \
      /build/prebuilt/linux-x86/arm-eabi-4.2.1/arm-eabi/lib/ldscripts/armelf.x \
      /build/platforms/android-1.5/arch-arm/usr/lib/crtbegin_dynamic.o \
      /build/platforms/android-1.5/arch-arm/usr/lib/crtend_android.o
         
      LIBS += -lc -lgcc

    3. Tips
      • When to build .cpp code with XXX-g++
        If you will build shared library for NDK r4 and previous version, or for compatibility reason with previous version,  please add CXXFLAGS: -fno-exceptions -fno-rtti
      • Build for Atom
        GCC=i686-android-linux-gcc
        GXX=i686-android-linux-g++
        CFLAGS +=-march=i686 -msse3 -mstackrealign -mfpmath=sse --sysroot=$(ANDROID_NDK_ROOT)
        LIBS += crtbegin_so.o
      • xxx
    4. xxx
  13. Build issue & solution:
    1. error:
      undefined reference to `__cxa_end_cleanup' 
      undefined reference to `__gxx_personality_v0'
      Solution:
      Add compile flags -fno-exceptions
    2. error:
      undefined reference to 'vtable for __cxxabiv1::__class_type_info'
      undefined reference to 'vtable for __cxxabiv1::__si_class_type_info'
      Solution:
      Add compile flags -fno-rtti
    3. [Issue] crt0.o: No such file: No such file or directory collect2: ld returned 1 exit status
      [Solution] add building flag "-nostdlib"
    4. [Issue] warning: cannot find entry symbol _start; defaulting to....
      [Solution] link crtbegin_dynamic.o or crtbegin_static.o; or ignore this warning. since if you add crtbegin_dynamic.o, the application may be dead-locked.
    5. [Issue] when run binary file,error message "binary_file: not found
      [Solution] add ldflags "-Wl,-dynamic-linker,/system/bin/linker" during building
    6. [Issue] When main function returns (after executing 'return 0'), segmentation fault signal is triggered
      [Solution 1st] add "exit(0);" just before "return 0;" in source code.
      [Solution 2nd] add ldflags "/build/prebuilt/linux-x86/arm-eabi-4.2.1/arm-eabi/lib/ldscripts/armelf.x /build/platforms/android-1.5/arch-arm/usr/lib/crtbegin_dynamic.o /build/platforms/android-1.5/arch-arm/usr/lib/crtend_android.o" during building
    7. [Issue]
      undefined reference to __aeabi_unwind_cpp_pr1
      [Solution]
      Add libs -lgcc -lc -lgcc
    8. Cannot load library: link_image[2023]: failed to link xxx.so
      May be caused by one of the the following reason:
      • The library doesn't exist
        • the library name or path doesn't exist
        • You load library with System.loadLibrary without 'lib' prefix stripped. ie, for a lib libutils.so, you need to load it with System.loadLibrary("utils"), but neigher System.loadLibrary("libutils") nor System.loadLibrary("libutils.so");
      • The flags to build shared library is wrong:
        • '-fno-exception -fno-rtti' is not specified when to build .cpp for old NDK
      • xxx
    9. xxx
  14. Debug
    • How to debug
      - Write log
      - Call __android_log_print and other andoid log interfaces declared in android/log.h, the interface is exported in liblog.so.
    • xxx
  15. Refs
    • Android Atom
      • Using the Android-x86 Port as your Emulator
        http://androiddevnotes.com/2011/03/08/1299521520000.html
      • android-x86 (Atom)
      • Android adb连接VirtualBox方式
        http://blog.sina.com.cn/s/blog_4de067e40100povf.html
      • xxx
    • xxx
  16. ...
阅读(5594) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~