本文章分二部分:
一.首先看如何绑定JNI和JAVA的方法
我们要特别留意下JNI_OnLoad,百度下之后:
当Android的VM(Virtual Machine)执行到C组件(即so档)里的System.loadLibrary()函数时,
首先会去执行C组件里的JNI_OnLoad()函数。 它的用途有二: . 告诉VM此C组件使用那一个JNI版本。
如果你的.so档没有提供JNI_OnLoad()函数,VM会默认该*.so档是使用最老的JNI 1.1版本。
其实Android中的so文件就像是Windows下的DLL一样,JNI_OnLoad和JNI_OnUnLoad函数
就像是DLL中的PROCESS ATTATCH和DEATTATCH的过程一样,可以同样做一些初始化和反初始化的动作。
/* get #of elements in a static array */
#ifndef NELEM
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
static const char* const kClassDeviceControl = "com/yfvet/server/dm/DeviceControl";
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
ALOGV("---->JNI_OnLoad");
JNIEnv* env = NULL;
jint result = JNI_FALSE;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("Can not get env");
return result;
}
if (env == NULL) {
ALOGE("env is null");
return result;
}
jclass clazz = env->FindClass(kClassDeviceControl);
if (clazz == NULL) {
ALOGE("can not find class -> %s", kClassDeviceControl);
return result;
}
if (env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
ALOGE("register error");
return result;
}
result = JNI_VERSION_1_4;
return result;
}
关键就在env->FindClass,以及env->RegisterNatives,用来绑定对应的java的,并且对应java中的每个方法的。而RegisterNatives中的gMethods是关键。
①.第一个参数就是指绑定的java类
②.RegisterNatives中第二个参数 gMethods是一个二维数组,代表着这个class里的每一个native方法所对应的实现的方法。
③.第三个参数代表要指定的native的数量。
static JNINativeMethod gMethods[] = {
// {"_getAuthStatus", "(II)I", (void*)com_android_Media_IOSMgr_getAuthStatus},
{"_sendLocationInfo", "(Landroid/os/Parcel;)V", (void*)com_android_Media_IOSMgr_sendLocationInfo},
{"_requestPlayHID", "(IZ)V", (void*)com_android_Media_IOSMgr_requestPlayHID},
{"native_setup", "(Ljava/lang/Object;)V", (void*)com_android_Media_IOSMgr_native_setup},
{"native_finalize", "()V", (void*)com_android_Media_IOSMgr_native_finalize},
{"native_init", "()V", (void*)com_android_Media_IOSMgr_native_init},
};
这些数组里面的参数分三个:
①java中的方法名
②中间是签名
③最后一个是jni方法(.cpp)里的函数名
至于签名我这里有一张表可以对应:
注意:
遇到特殊的object 比如:Parcel 签名是:Landroid/os/Parcel;带分号
如果参数是Parcel,返回值是Void:"(Landroid/os/Parcel;)V "
如果参数是int或float,返回值是Parcel:"(I/F)Landroid/os/Parcel;"
实在是不知道怎么对应的话,或者说一直签名报错:试试自动生成签名把:
javap -s -p MainActivity.class
例子:
【Android工具的Terminal :】
1️. cd Android_Studio_3.0.1_WorkSpace\ZZ_AS23\AS23_Interface\app\build\intermediates\classes\debug\com\yfvet\iosmgr\iapctrl\as23_interface> javap -s -p MainActivity.class
2. cd Android_Studio_3.0.1_WorkSpace\ZZ_AS23\AS23_Interface\app\build\intermediates\classes\debug> javap -s com.yfvet.iosmgr.iapctrl.as23_interface.MainActivity.class
以上就是JAVA调用JNI。下面谈一谈JNI调用JAVA发送消息,
二:如何绑定,其实也简单,主要是绑定一个java的方法,下面用到的就是GetStaticMethodID
先看看例子把:
该函数对应native_init方法
static void com_android_Media_IOSMgr_native_init(JNIEnv* env) {
ALOGV("com_android_Media_IOSMgr_native_init");
jclass clazz;
clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
return;
}
IOSMgrfields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (IOSMgrfields.context == NULL) {
return;
}
IOSMgrfields.post_event =
env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (IOSMgrfields.post_event == NULL) {
return;
}
env->DeleteLocalRef(clazz);
}
static void com_android_Media_IOSMgr_native_setup(JNIEnv* env, jobject thiz, jobject weak_this) {
ALOGV("native_setup");
sp<IOSMgrClient> client = new IOSMgrClient();
if (client == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
client->attachIOSMgr();
// create new listener and give it to IOSMgr
sp<JNIIOSMgrClientListener> listener = new JNIIOSMgrClientListener(env, thiz, weak_this);
client->setListener(listener);
// Store our new C++ IOSMgrClient in an opaque field in the Java object.
setIOSMgrClient(env, thiz, client);
}
JAVA中写法:
private EventHandler mEventHandler;
private static Object mLock = null;
static {
System.loadLibrary("iosmgr_jni");
native_init();
}
private static native final void native_init();
/*在构造函数里初始化***/
private void initIapControl() {
Slog.d(TAG, "initIapControl");
mLock = new Object();
mListenerVector = new Vector();
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
Slog.w(TAG, "initIapControl Looper not exist, create one");
Looper.prepare();
mEventHandler = new EventHandler(this, Looper.myLooper());
}
native_setup(new WeakReference<IapControl>(this));
}
private static void postEventFromNative(Object ipodview_ref,
int what, int arg1, int arg2, Object obj) {
IapControl dv = (IapControl) ((WeakReference) ipodview_ref).get();
if (dv == null) {
return;
}
Slog.d(TAG, "postEventFromNative message :" + what);
synchronized (mLock) {
if (dv.mEventHandler != null) {
Message m = dv.mEventHandler.obtainMessage(what, arg1, arg2, obj);
if (dv.mEventHandler.sendMessage(m)) {
} else {
Slog.d(TAG, "sendMessage failed");
}
}
}
}
对应之后,则CPP里发送的消息会被postEventFromNative,收到,之后在EventHandler里面进行数据的解析和处理:
private class EventHandler extends Handler {
private IapControl mListener = null;
public EventHandler(IapControl dmct, Looper looper) {
super(looper);
mListener = dmct;
Slog.d(TAG, "EventHandler");
}
@Override
public void handleMessage(Message msg) {
if (mListener == null) {
Slog.w(TAG, "IapControl, do not handle events");
return;
}
switch (msg.what) {
case NATIVE_EVENT_IAPCTRL_SNED_DATA:
break;
case NATIVE_EVENT_IAPCTRL_PLAY_ELAPSED_TIME:
int curPlaytime = msg.arg1;
if (mStatusListener != null) {
mStatusListener.onIpodTrackInfoChange(mListener, curPlaytime);
}
break;
default:
break;
}
}
}
这里在Handler里面就可以直接操作数据了。
注意!注意!
1.JNI中 :JNI 中使用Parcel的时候,记得加tokenid
当JAVA中的String转换成Char类型的时候,如下
2.先 【String == String16】
3.转换为char 类型,再 char =String8(String16).string