概述
什么是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("hellon");
}
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 :%sn", 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 (frameworksbasemediajavaandroidmedia)
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 (frameworksbasemediajni)
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 (frameworksbasemediajni)
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 failedn");
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 failedn");
goto bail;
}
if (register_android_media_MediaRecorder(env) < 0) {
ALOGE("ERROR: MediaRecorder native registration failedn");
goto bail;
}
if (register_android_media_MediaScanner(env) < 0) {
ALOGE("ERROR: MediaScanner native registration failedn");
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);
}
最后
以上就是欣喜春天为你收集整理的Android JNI的全部内容,希望文章能够帮你解决Android JNI所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复