JNI使用的一些细节和用法

本文章分二部分:
.首先看如何绑定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)里的函数名

至于签名我这里有一张表可以对应:
JNI使用的一些细节和用法_第1张图片
注意
遇到特殊的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

你可能感兴趣的:(Android进阶)