欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Android JNI,androidjni

来源: javaer 分享于  点击 31754 次 点评:85

Android JNI,androidjni


什么是JNI,Java Native Interface ,Java 本地调用。Java 虽然具有跨平台的特性,但是Java和具体的平台之间的隔离是通过JNI层来实现的,Android 中 Java 通过 JNI 层调用 Linux 中的接口来实现对应的功能。JNI 层一般是由 C C++ 文件编写。


Java 程序 1、加载对应的JNI库,同行的做法是放在类的 static 语句中加载 2、声明 native 函数,表示这个函数在 JNI 层实现
public class JNIDemo{
        static {
                /* load */
                System.loadLibrary("native");/* libnative.so */
        }
        public native void hello();
        public native int add(int a, int b);
        public native String str(String str);
        public native int [] array(int [] a);
        public static void main(String args[]){
                JNIDemo d = new JNIDemo();
                /* hello */
                d.hello();
                /* int */
                System.out.println(d.add(1,2));
                /* string */
                System.out.println(d.str("hello world"));
                /* array */
                int [] a = {1,2,3};
                int [] b = d.array(a);
                for (int i = 0; i < b.length; i++)
                        System.out.println(b[i]);
        }
}


JNI C 程序 1、实现一个 JNI_OnLoad 函数,JNI_OnLoad 函数将在 java System.loadLibrary 后在该库中寻找并调用 JNI_OnLoad 2、一般在JNI_OnLoad 函数中注册一个 JNINativeMethod 类型的数组来表示 Java 与 JNI 中函数的映射关系(实际上只要在调用函数之前注册就行) 3、JNINativeMethod 中的数据类型可通过以下代码来获取,编译程序.class文件,在生成.h头文件

/* javac JNIDemo.java */
/* javah -jni JNIDemo */

#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
/* java - c */
#if 0
typedef struct{
	char *name;		   	//name in java
	char *signature;	//params
	char *fnPtr;		//name in C
}JNINativeMethod;
#endif
void c_hello(JNIEnv *env, jobject cls)
{
	printf("hello\n");
}
jint c_add(JNIEnv *env, jobject cls, jint a, jint b)
{
	return a + b;
}
jstring c_str(JNIEnv *env, jobject cls, jstring str)
{
	const jbyte *cstr;
	cstr = (*env)->GetStringUTFChars(env, str, NULL);
	if (cstr == NULL) {
		return NULL; /* OutOfMemoryError already thrown */
	}
	printf("Get string from java :%s\n", cstr);
	(*env)->ReleaseStringUTFChars(env, str, cstr);
	return (*env)->NewStringUTF(env, "return from c");	
}

jintArray c_array(JNIEnv *env, jobject cls, jintArray arr)
{
	jint *carr;
	jint *oarr;
	jintArray rarr;
	
	jint i, n = 0;
	carr = (*env)->GetIntArrayElements(env, arr, NULL);
	if (carr == NULL) {
		return 0; /* exception occurred */
	}
	n = (*env)->GetArrayLength(env, arr);
	oarr = malloc(sizeof(jint) * n);
	if (oarr == NULL)
	{
		(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
		return 0;
	}
	for (i = 0; i < n; i++)
	{
		oarr[i] = carr[n-1-i];
	}
	
	(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
	/* create jintArray */
	rarr = (*env)->NewIntArray(env, n);
	if (rarr == NULL)
	{
		return 0;
	}
	(*env)->SetIntArrayRegion(env, rarr, 0, n, oarr);
	free(oarr);
	
	return rarr;
}
static const JNINativeMethod methods[] = {
	{"hello", "()V", (void *)c_hello},
	{"add", "(II)I", (void *)c_add},
	{"str", "(Ljava/lang/String;)Ljava/lang/String;", (void *)c_str},
	{"array","([I)[I", (void *)c_array},
};
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)){
		return JNI_ERR;
	}
	//function of class in java
	cls = (*env)->FindClass(env, "JNIDemo");
	if (cls == NULL){
		return JNI_ERR;
	}
	(*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
	return JNI_VERSION_1_4;
}


MediaScanner 分析: MediaScanner.java (frameworks\base\media\java\android\media)
public class MediaScanner
{
    static {
        System.loadLibrary("media_jni");  //libmedia_jni.so
        native_init();
    }
    ...
    //声明本地方法
    private native void processDirectory(String path, MediaScannerClient client);
    private native void processFile(String path, String mimeType, MediaScannerClient client);
    public native void setLocale(String locale);
    public native byte[] extractAlbumArt(FileDescriptor fd);
    private static native final void native_init();
    private native final void native_setup();
    private native final void native_finalize();
    ...
}
搜索一个 native 方法,processDirectory 来查找 JNI 层的代码位置: android_media_MediaScanner.cpp (frameworks\base\media\jni)
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
    },
};
这个文件中并没有 JNI_OnLoad 函数,但是实现了一个注册函数:
int register_android_media_MediaScanner(JNIEnv *env)
{
    return AndroidRuntime::registerNativeMethods(env,
                kClassMediaScanner, gMethods, NELEM(gMethods));
}
android_media_MediaPlayer.cpp (frameworks\base\media\jni)
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    JNIEnv* env = NULL;
    jint result = -1;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);
    if (register_android_media_ImageReader(env) < 0) {
        ALOGE("ERROR: ImageReader native registration failed");
        goto bail;
    }
    if (register_android_media_MediaPlayer(env) < 0) {
        ALOGE("ERROR: MediaPlayer native registration failed\n");
        goto bail;
    }
    if (register_android_media_MediaRecorder(env) < 0) {
        ALOGE("ERROR: MediaRecorder native registration failed\n");
        goto bail;
    }
    if (register_android_media_MediaScanner(env) < 0) {
        ALOGE("ERROR: MediaScanner native registration failed\n");
        goto bail;
    }
    ....
}
在另一个文件中的 JNI_OnLoad 函数中,统一注册了一些 JNINativeMethods . 在 JNI 文件中可以获得 Java 中的成员变量、成员方法
static void
android_media_MediaScanner_native_init(JNIEnv *env)
{
    ALOGV("native_init");
    jclass clazz = env->FindClass(kClassMediaScanner);
    if (clazz == NULL) {
        return;
    }
    // java: private long mNativeContext;
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");//获得成员变量
    if (fields.context == NULL) {
        return;
    }
}
static void
android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
{
    ALOGV("native_setup");
    MediaScanner *mp = new StagefrightMediaScanner;
    if (mp == NULL) {
        jniThrowException(env, kRunTimeException, "Out of memory");
        return;
    }
    env->SetLongField(thiz, fields.context, (jlong)mp);//设置java中成员变量的值
}

成员方法:
static void
android_media_MediaScanner_processDirectory(
        JNIEnv *env, jobject thiz, jstring path, jobject client)
{
    ALOGV("processDirectory");
    MediaScanner *mp = getNativeScanner_l(env, thiz);
    
    //unicode -> utf-8
    const char *pathStr = env->GetStringUTFChars(path, NULL);
    MyMediaScannerClient myClient(env, client);
    ...
    env->ReleaseStringUTFChars(path, pathStr);
}
class MyMediaScannerClient : public MediaScannerClient
{
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 {
            //获得 java 中的成员方法
            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");
        }
    }
    ....
    virtual status_t scanFile(const char* path, long long lastModified,
            long long fileSize, bool isDirectory, bool noMedia)
    {

        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
                fileSize, isDirectory, noMedia);

        mEnv->DeleteLocalRef(pathStr);
        return checkAndClearExceptionFromCallback(mEnv, "scanFile");
    }
private:
    JNIEnv *mEnv;
    jobject mClient;
    jmethodID mScanFileMethodID;
    jmethodID mHandleStringTagMethodID;
    jmethodID mSetMimeTypeMethodID;
};

mClient -> android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path,jobject client) private native void processDirectory(String path, MediaScannerClient client);
public interface MediaScannerClient
{    
    public void scanFile(String path, long lastModified, long fileSize,
            boolean isDirectory, boolean noMedia);
    /**
     * Called by native code to return metadata extracted from media files.
     */
    public void handleStringTag(String name, String value);
    /**
     * Called by native code to return mime type extracted from DRM content.
     */
    public void setMimeType(String mimeType);
}





相关文章

    暂无相关文章
相关栏目:

用户点评