深入理解JNI

  1. JNI概述
  2. 學習JNI實例:MediaScanner
  3. 注冊JNI函數(shù)
  4. 數(shù)據(jù)類型轉(zhuǎn)換
  5. JNIEnv介紹

一著角、JNI概述

JNI全稱,JavaNativeInterface——Java提供了Java層與Native層交互的橋梁。通過JNI技術(shù)我們可以做到如下:

  1. Java程序中的函數(shù)可以調(diào)用Native[C/C++]語言編寫的函數(shù)做院。
  2. Native層中的函數(shù)可以調(diào)用Java層的函數(shù),也就是說C/C++函數(shù)中可以調(diào)用Java函數(shù)。

二鼓黔、JNI學習實例:MediaScanner類

MediaScanner類中的部分函數(shù)由Native層實現(xiàn)央勒,JNI層中對應的是libmedia_jni.so,media_jni為JNI庫的名字澳化。libmedia.so庫完成了實際功能崔步。MediaScanner通過JNI庫libmedia_jni.so和Native層的libmedia.so進行交互。

#2.1 Java層的MediaScanner分析
  • MediaScanner
class MediaScanner {
    ......
    static {
        System.loadLibrary("media_jni");
        native_init();
    }
    ......
    private static native final void native_init();
    ......
}

Media類中的靜態(tài)代碼塊執(zhí)行了兩個操作:

  1. 加載media_jni庫缎谷。
  2. 調(diào)用native_init()完成native層初始化操作井濒。
    Java函數(shù)中調(diào)用native函數(shù),必須通過位于JNI層的動態(tài)庫來實現(xiàn)慎陵。一般采用的做法是在類的static代碼塊中眼虱,通過調(diào)用System.loadLibrary(String libraryName)來完成對動態(tài)庫的加載。
  • 加載JNI庫
    由System類的靜態(tài)成員函數(shù)loadLibrary負責加載動態(tài)庫席纽。
  • System
public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

loadLibrary方法中調(diào)用了Runtime類的成員函數(shù)loadLibrary0();

  • Runtime
synchronized void loadLibrary0(ClassLoader loader, String libname) {
    if (libname.indexOf((int)File.separatorChar) != -1) {
        throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
    }
    String libraryName = libname;

    //如果 loader不為空進入該分支
    if (loader != null) {
        //查找?guī)焖诘穆窂?        String filename = loader.findLibrary(libraryName);
        if (filename == null) {
            // It's not necessarily true that the ClassLoader used
            // System.mapLibraryName, but the default setup does, and it's
            // misleading to say we didn't find "libMyLibrary.so" when we
            // actually searched for "liblibMyLibrary.so.so".
            throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                           System.mapLibraryName(libraryName) + "\"");
        }
        //加載庫
        String error = doLoad(filename, loader);
        if (error != null) {
            throw new UnsatisfiedLinkError(error);
        }
        return;
    }

    String filename = System.mapLibraryName(libraryName);
    List<String> candidates = new ArrayList<String>();
    String lastError = null;
    for (String directory : getLibPaths()) {
        String candidate = directory + filename;
        candidates.add(candidate);

        if (IoUtils.canOpenReadOnly(candidate)) {
            String error = doLoad(candidate, loader);
            if (error == null) {
                return; // We successfully loaded the library. Job done.
            }
            lastError = error;
        }
    }

    if (lastError != null) {
        throw new UnsatisfiedLinkError(lastError);
    }
    throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}

loadLibrary0方法中所完成的操作是捏悬,如果ClassLoader不為空,則調(diào)用其成員函數(shù)findLibrary獲取到庫所在的路徑润梯,然后調(diào)用Runtime類的成員函數(shù)doLoad對庫進行加載过牙,doLoad函數(shù)則將具體的加載過程轉(zhuǎn)發(fā)給Runtime類中定義的native層函數(shù)nativeLoad,進而完成后續(xù)加載過程纺铭。

  • ClassLoader.findLibrary

獲取庫所在的本地路徑寇钉。

/**
 * Returns the absolute path name of a native library.  The VM invokes this
 * method to locate the native libraries that belong to classes loaded with
 * this class loader. If this method returns <tt>null</tt>, the VM
 * searches the library along the path specified as the
 * "<tt>java.library.path</tt>" property.
 *
 * @param  libname
 *         The library name
 *
 * @return  The absolute path of the native library
 *
 * @see  System#loadLibrary(String)
 * @see  System#mapLibraryName(String)
 *
 * @since  1.2
 */
protected String findLibrary(String libname) {
    return null;
}
  • Runtime.doLoad

調(diào)用native層函數(shù)nativeLoad完成對庫加載。

private String doLoad(String name, ClassLoader loader) {
    ......
    // internal natives.
    synchronized (this) {
        return nativeLoad(name, loader, librarySearchPath);
    }
}

nativeLoad后續(xù)的執(zhí)行步驟大致為:

  1. 調(diào)用dlopen函數(shù)舶赔,打開一個so文件并創(chuàng)建一個handle;
  2. 調(diào)用dlsym()函數(shù)扫倡,查看相應的so文件的JNI_OnLoad()函數(shù)指針,并執(zhí)行相應函數(shù)竟纳。
#2.2 JNI層的MediaScanner分析

MediaScanner的JNI層代碼在android_media_MediaScanner.cpp中撵溃,如下所示:

  • android_media_MediaScanner.cpp
static void android_media_MediaScanner_native_init(JNIEnv *env)
{
    ALOGV("native_init");
    jclass clazz = env->FindClass(kClassMediaScanner);
    if (clazz == NULL) {
        return;
    }

    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }
}

Java層的native_init對應JNI層的android_media_MediaScanner_native_init,下面詳細分析其綁定過程锥累。

當Java層調(diào)用native_init函數(shù)時缘挑,它會從對應的JNI庫中尋找Java_android_media_Media_Scanner_native_init函數(shù),如果沒有桶略,就會報錯语淘。如果找到,則會為這個native_init和Java_android_media_Media_Scanner_native_init建立一個關(guān)聯(lián)關(guān)系际歼,其實就是保存JNI函數(shù)的函數(shù)指針惶翻。以后調(diào)用native_init函數(shù)時,直接使用這個函數(shù)指針就可以了蹬挺,這項工作是由虛擬機完成的维贺。


三、注冊JNI函數(shù)

“注冊”是將Java層的native函數(shù)和JNI層對應的實現(xiàn)函數(shù)關(guān)聯(lián)起來巴帮。JNI函數(shù)注冊的方式有兩種:

  • 靜態(tài)注冊
  • 動態(tài)注冊
#3.1 靜態(tài)注冊

靜態(tài)注冊的大體流程如下:

  • 先編寫Java代碼溯泣,然后編譯生成.class文件虐秋。
  • 使用Java的工具程序javah,如javah -o output packagename.classname垃沦,這樣會生成一個叫output.h的JNI層頭文件客给。其中packagename.classname是Java代碼編譯后的class文件,而在生成的output.h文件里肢簿,聲明了對應的JNI層函數(shù)靶剑,只要實現(xiàn)里面的函數(shù)即可。
#3.1.1 :編寫Java代碼
package com.next.hhu.jnidemo;

public class StaticJNITest {
    public static native int add(int a, int b);
}
#3.1.2 編譯生成.class文件

執(zhí)行[1]操作進入到java目錄下池充,然后執(zhí)行[2]操作生成.class文件桩引。

//[1] 進入到java目錄下
cd app/src/main/java
//[2] 生成.class文件
javac com/next/hhu/jnidemo/StaticJNITest.java
#3.1.3 生成頭文件

然后利用javah命令生成頭文件,這樣會在/java目錄下生成com_next_hhu_jnidemo_StaticJNITest.h頭文件收夸。

javah com.next.hhu.jnidemo.StaticJNITest
#3.1.4 配置CMakeLists.text文件
# 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.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp)

add_library( # Sets the name of the library.
             native-lib1

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/com_next_hhu_jnidemo_StaticJNITest.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.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
#3.2 動態(tài)注冊

JNI中用JNINativeMethod結(jié)構(gòu)來記錄Java的Native方法和JNI方法的關(guān)聯(lián)關(guān)系坑匠。

  • jni.h
typedef struct {
    const char* name; //Java方法名
    const char* signature; //Java方法的簽名信息
    void*       fnPtr; //JNI中對應的函數(shù)指針
} JNINativeMethod;

MediaScanner JNI層中采用的是靜態(tài)注冊。

#3.2.1 定義JNINativeMethod[]

定義一個JNINativeMethod[]卧惜,其成員就是MediaScanner中所有成員函數(shù)的一一對應關(guān)系厘灼。

android_media_MediaScanner.cpp

static JNINativeMethod gMethods[] = {
    {
        "processDirectory",
        "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
        (void *)android_media_MediaScanner_processDirectory
    },

    {
        "processFile",
        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
        (void *)android_media_MediaScanner_processFile
    },

    {
        "setLocale",
        "(Ljava/lang/String;)V",
        (void *)android_media_MediaScanner_setLocale
    },

    {
        "extractAlbumArt",
        "(Ljava/io/FileDescriptor;)[B",
        (void *)android_media_MediaScanner_extractAlbumArt
    },

    {
        "native_init",
        "()V",
        (void *)android_media_MediaScanner_native_init
    },

    {
        "native_setup",
        "()V",
        (void *)android_media_MediaScanner_native_setup
    },

    {
        "native_finalize",
        "()V",
        (void *)android_media_MediaScanner_native_finalize
    },
};
#3.2.2 注冊JNINativeMetod[]
  • android_media_MediaScanner.cpp
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaScanner(JNIEnv *env)
{
    return AndroidRuntime::registerNativeMethods(env,
                kClassMediaScanner, gMethods, NELEM(gMethods));
}

調(diào)用AndroidRuntime的registerNativeMethods函數(shù)來完成注冊工作。

  • AndroidRuntime.cpp
/*
 * Register native methods using JNI.
 */
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

調(diào)用JNIHelp.c中的jniRegisterNativeMethods方法來完成注冊咽瓷。

  • JNIHelp.c
int jniRegisterNativeMethods(JNIENV *env,const char *className,
                            const JNINativeMethod *gMethods,int numMethods) 
{
    jclass clazz;
    clazz = (*env)->findClass(env,className);
    ......
    //實際上調(diào)用JNIEnv的RegisterNatives函數(shù)完成注冊的
    if((*env)->RegisterNatives(env,clazz,gMethods,numMethods) < 0) {
        return -1;
    }
    return 0;
}
#3.2.3 JNI_OnLoad函數(shù)中調(diào)用注冊函數(shù)

當Java層通過System.loadLibrary加載完成JNI動態(tài)庫后设凹,緊接著會查找該庫中一個叫JNI_OnLoad的函數(shù)。如果有茅姜,就調(diào)用它闪朱,動態(tài)注冊工作在這里完成。

//該函數(shù)的第一個參數(shù)類型為JavaVM钻洒,代表JNI層的Java虛擬機监透,每一個Java進程有且僅有一個
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserve) {
    JNIEnv *env = NULL;
    jint result = -1;
    
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        goto bail;
    }
    if (env == NULL) {
        return -1;
    }
    jclass clazz = env->FindClass("com/next/hhu/jnidemo/JNIHelper");
    if (clazz == NULL) {
        return -1;
    }
    ......
    //動態(tài)注冊MediaScanner的JNI函數(shù)
    if(register_android_media_MediaScanner(env) < 0) {
        goto bail;
    }
    .....
    return JNI_VERSION_1_4;//必須返回這個值,否則會報錯航唆。
}

四、數(shù)據(jù)類型轉(zhuǎn)換

#4.1 基本數(shù)據(jù)類型轉(zhuǎn)換
Signature格式 Java Native
B byte jbyte
C char jchar
D double jdouble
F float jfloat
I int jint
S short jshort
J long jlong
Z boolean jboolean
V void void
#4.2 數(shù)組數(shù)據(jù)類型
Signature格式 Java Native
[B byte[] jbyte
[C char[] jchar
[D double[] jdouble
[F float[] jfloat
[I int[] jint
[S short[] jshort
[J long[] jlong
[Z boolean[] jboolean
#4.3 引用數(shù)據(jù)類型轉(zhuǎn)換
Signature格式 Java Native
Ljava/lang/String; String jstring
L+classname+; 所有對象 jobject
[L+classname+; Object[] jobjectArray
Ljava.lang.Class; Class jclass
Ljava.lang.Throwable; Throwable jthrowable
#4.4 Signature
Java函數(shù) 對應的簽名
void foo() ()V
float foo(int i) (I)F
long foo(int[] i) ([I)J
double foo(Class c) (Ljava/lang/Class;)D
boolean foo(int[] i,String s) ([ILjava/lang/String;)Z
String foo(int i) (I)Ljava/lang/String;

五院刁、JNIEnv介紹

JNIEnv是一個與線程相關(guān)的代表JNI環(huán)境的結(jié)構(gòu)體糯钙,JNIEnv提供了一些JNI系統(tǒng)函數(shù)。通過這些函數(shù)可以做到:

  • 調(diào)用JAVA函數(shù)退腥。
  • 操作jobject對象等很多事情任岸。

JNIEnv是一個線程相關(guān)的變量。由于線程相關(guān)狡刘,所以不能在一個線程中使用另一個線程的JNIEnv結(jié)構(gòu)體享潜,有一種情況當后臺線程收到一個網(wǎng)絡消息,而又需要由Native層函數(shù)主動回調(diào)Java層函數(shù)時嗅蔬,JNIEnv該如何處理剑按?

//全進程只有一個JavaVM對象疾就,所以可以保存,并且在任何地方使用都沒有問題艺蝴。
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);

JavaVM和JNIEnv之間的關(guān)系:

  • 調(diào)用JavaVM的AttachCurrentThread函數(shù)猬腰,就可以得到這個線程的JNIEnv結(jié)構(gòu)體。這樣就可以在后臺線程中回調(diào)Java函數(shù)猜敢。
  • 另外姑荷,在后臺線程退出前,需要調(diào)用JavaVM的DetachCurrentThread的函數(shù)來釋放對應的資源缩擂。
/*
 * C++ object wrapper.
 *
 * This is usually overlaid on a C struct whose first element is a
 * JNINativeInterface*.  We rely somewhat on compiler behavior.
 */
struct _JNIEnv {
   /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)
......
#endif /*__cplusplus*/
};
#5.1 通過JNIEnv操作jobject

Java的引用類型除了少數(shù)幾個外鼠冕,最終在JNI層都會用jobject來表示對象的數(shù)據(jù)類型,操作jobject步驟:

#5.1.1 jfieldID和jmethodID介紹
    jfieldID    (*GetFieldID)(JNIEnv*, jclass clazz, const char *name, const char *sig);
    jmethodID   (*GetMethodID)(JNIEnv*, jclass clazz, const char *name, const char *sig);

其中胯盯,jclass代表Java類懈费,name表示成員函數(shù)或成員變量的名字,sig為這個函數(shù)和變量的簽名信息陨闹。

public:
    MyMediaScannerClient(JNIEnv *env, jobject client)
        :   mEnv(env),
            mClient(env->NewGlobalRef(client)),
            mScanFileMethodID(0),
            mHandleStringTagMethodID(0),
            mSetMimeTypeMethodID(0)
    {
        ALOGV("MyMediaScannerClient constructor");
        jclass mediaScannerClientInterface =
                env->FindClass(kClassMediaScannerClient);

        if (mediaScannerClientInterface == NULL) {
            ALOGE("Class %s not found", kClassMediaScannerClient);
        } else {
            mScanFileMethodID = env->GetMethodID(
                                    mediaScannerClientInterface,
                                    "scanFile",
                                    "(Ljava/lang/String;JJZZ)V");

            mHandleStringTagMethodID = env->GetMethodID(
                                    mediaScannerClientInterface,
                                    "handleStringTag",
                                    "(Ljava/lang/String;Ljava/lang/String;)V");

            mSetMimeTypeMethodID = env->GetMethodID(
                                    mediaScannerClientInterface,
                                    "setMimeType",
                                    "(Ljava/lang/String;)V");
        }
    }

將scanFile和handleStringTag函數(shù)的jmethodId保存為MyMediaScannerClient的成員變量楞捂,供后續(xù)使用。

#5.1.2 使用jfieldID和jmethodID
    virtual status_t scanFile(const char* path, long long lastModified,
            long long fileSize, bool isDirectory, bool noMedia)
    {
        ALOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
            path, lastModified, fileSize, isDirectory);

        jstring pathStr;
        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
            mEnv->ExceptionClear();
            return NO_MEMORY;
        }
        
        //調(diào)用JNIEnv的CallVoidMethod函數(shù)趋厉,注意CallVoidMethod的參數(shù):
        //第一個參數(shù)代表MediaScannerClient的jobject對象寨闹,
        //第二個參數(shù)是函數(shù)scanFile的jmethodID,后面是Java中scanFile的參數(shù)君账。
        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
                fileSize, isDirectory, noMedia);

        mEnv->DeleteLocalRef(pathStr);
        return checkAndClearExceptionFromCallback(mEnv, "scanFile");
    }

通過JNIEnv輸出CallVoidMethod繁堡,再把jobject、jmethodID和對應的參數(shù)傳進去乡数,JNI層就能夠調(diào)用Java對象的函數(shù)了椭蹄。

#5.2 jstring介紹

Java中String為引用類型,JNI規(guī)范中單獨創(chuàng)建一個jstring類型來表示Java中的String類型净赴。

  • 調(diào)用JNIEnv的NewString(JNIEnv *env,const jchar *unicodeChars,jsize len),可以從Native字符串得到一個jstring對象绳矩。可以把jstring對象看作是Java中String對象在JNI層的代表玖翅,也就是說jstring是一個Java String翼馆。由于Java String中存儲的是Unicode字符串,所以NewString函數(shù)的參數(shù)也必須是Unicode字符串金度。
  • 調(diào)用JNIEnv的NewStringUTF將根據(jù)Native的一個UTF-8字符串得到一個jstring對象应媚。
  • JNIEnv中GetStringChars和GetStringUTFChars函數(shù),它們可以將Java String對象轉(zhuǎn)換成本地字符串猜极。
  • 如果在代碼中調(diào)用了上面幾個函數(shù)中姜,在做完相關(guān)工作后,就需要調(diào)用ReleaseStringChars或ReleaseStringUTFChars函數(shù)來對應地釋放資源跟伏,否則會導致JVM內(nèi)存泄漏丢胚。
static void
android_media_MediaScanner_processFile(
        JNIEnv *env, jobject thiz, jstring path,
        jstring mimeType, jobject client)
{
    ALOGV("processFile");

    // Lock already hold by processDirectory
    MediaScanner *mp = getNativeScanner_l(env, thiz);
    if (mp == NULL) {
        jniThrowException(env, kRunTimeException, "No scanner available");
        return;
    }

    if (path == NULL) {
        jniThrowException(env, kIllegalArgumentException, NULL);
        return;
    }

    const char *pathStr = env->GetStringUTFChars(path, NULL);
    if (pathStr == NULL) {  // Out of memory
        return;
    }

    const char *mimeTypeStr =
        (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
    if (mimeType && mimeTypeStr == NULL) {  // Out of memory
        // ReleaseStringUTFChars can be called with an exception pending.
        env->ReleaseStringUTFChars(path, pathStr);
        return;
    }

    MyMediaScannerClient myClient(env, client);
    MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
    if (result == MEDIA_SCAN_RESULT_ERROR) {
        ALOGE("An error occurred while scanning file '%s'.", pathStr);
    }
    env->ReleaseStringUTFChars(path, pathStr);
    if (mimeType) {
        env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
    }
}
#5.3 JNI類型簽名介紹

Java中支持函數(shù)重載翩瓜,也就是說,可以定義同名但不同參數(shù)的函數(shù)嗜桌。因此奥溺,僅僅靠函數(shù)名沒辦法找到具體的函數(shù)。JNI技術(shù)中將參數(shù)類型和返回值類型的組合作為一個函數(shù)的簽名信息骨宠,有了簽名信息和函數(shù)名浮定,就能找到Java中的函數(shù)了。
Java中提供了javap的工具來幫助生成函數(shù)或變量的簽名信息层亿。

//XXX是編譯后的.class文件
javap -s -p XXX
C:\Users\hhu\Desktop\11\JNIDemo\app\src\main\java>javap -s -p com.next.hhu.jnidemo.StaticJNITest
Compiled from "StaticJNITest.java"
public class com.next.hhu.jnidemo.StaticJNITest {
  public com.next.hhu.jnidemo.StaticJNITest();
    descriptor: ()V

  public static native int add(int, int);
    descriptor: (II)I

  static {};
    descriptor: ()V
}
#5.3 垃圾回收

JNI提供了三種引用類型:

  • Local Reference:本地引用桦卒。在JNI層函數(shù)中使用的非全局引用對象都是Local Reference,它包括函數(shù)調(diào)用時傳入的jobject和在JNI層函數(shù)中創(chuàng)建的jobject匿又。Local Reference最大特點是方灾,一旦JNI層函數(shù)返回,這些object就可能被垃圾回收碌更。
  • Global Reference:全局引用裕偿,這種對象如不主動釋放,它永遠不會被垃圾回收痛单。
  • Weak Global Reference:弱全局引用嘿棘,一種特殊的Global Reference,在運行過程中可能會被垃圾回收旭绒,因此在使用它之前鸟妙,需要調(diào)用JNIEnv的IsSameObject判斷它是否被回收了。
public:
    MyMediaScannerClient(JNIEnv *env, jobject client)
        :   mEnv(env),
            mClient(env->NewGlobalRef(client)),
    
    {
       ......
    }

    virtual ~MyMediaScannerClient()
    {
        ALOGV("MyMediaScannerClient destructor");
        mEnv->DeleteGlobalRef(mClient);
    }
#5.4 JNI中的異常處理

如果調(diào)用JNIEnv的某些函數(shù)出錯了挥吵,則會產(chǎn)生一個異常重父,但這個異常不會中斷本地函數(shù)的執(zhí)行,直到從JNI層返回到Java層后忽匈,虛擬機才會拋出這個異常房午。雖然在JNI層中產(chǎn)生的異常不會中斷本地函數(shù)的運行,但一旦產(chǎn)生異常后丹允,就只能做一些資源清理工作歪沃。所以安全編碼顯得十分重要。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嫌松,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奕污,更是在濱河造成了極大的恐慌萎羔,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碳默,死亡現(xiàn)場離奇詭異贾陷,居然都是意外死亡缘眶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門髓废,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巷懈,“玉大人,你說我怎么就攤上這事慌洪《パ啵” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵冈爹,是天一觀的道長涌攻。 經(jīng)常有香客問我,道長频伤,這世上最難降的妖魔是什么恳谎? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮憋肖,結(jié)果婚禮上因痛,老公的妹妹穿的比我還像新娘。我一直安慰自己岸更,他們只是感情好鸵膏,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坐慰,像睡著了一般较性。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上结胀,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天赞咙,我揣著相機與錄音,去河邊找鬼糟港。 笑死攀操,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的秸抚。 我是一名探鬼主播速和,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剥汤!你這毒婦竟也來了颠放?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤吭敢,失蹤者是張志新(化名)和其女友劉穎碰凶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡欲低,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年辕宏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砾莱。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瑞筐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腊瑟,到底是詐尸還是另有隱情聚假,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布扫步,位于F島的核電站魔策,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏河胎。R本人自食惡果不足惜闯袒,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望游岳。 院中可真熱鬧政敢,春花似錦、人聲如沸胚迫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽访锻。三九已至褪尝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間期犬,已是汗流浹背河哑。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留龟虎,地道東北人璃谨。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像鲤妥,于是被迫代替她去往敵國和親佳吞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 此文章為《深入理解Android卷 I》的讀書筆記棉安,筆者已經(jīng)完成了卷一的第一遍閱讀底扳,第一遍時寫下了一些筆記,現(xiàn)在開...
    pokerWu閱讀 1,366評論 0 6
  • 什么是JNI贡耽? JNI 是java本地開發(fā)接口.JNI 是一個協(xié)議,這個協(xié)議用來溝通java代碼和外部的本地代碼(...
    a_tomcat閱讀 2,819評論 0 54
  • 我不管你用什么甜言蜜語 我不會被你吸引 愛不是做作的副產(chǎn) 它需要你用心去培育 我沒有什么高智商 去領(lǐng)會你的心意 我...
    啊Ben閱讀 220評論 0 2
  • 失戀了花盐,你可以傷心難過羡滑,但千萬別作踐自己,別總是痛哭流涕算芯,動不動呼天搶地,毫無原則地低到塵埃里凳宙,讓自己顯得廉價和低...
    不哭不鬧衒耀閱讀 164評論 0 0
  • 歷史上每次的金融變革都離不開技術(shù)的創(chuàng)新氏涩,從傳統(tǒng)金融到互聯(lián)網(wǎng)金融的發(fā)展届囚,都是基于安全的條件下,以金融業(yè)經(jīng)營成...
    煎餅果籽閱讀 268評論 0 0