jQuery鼠标指针特效

JNI 基础教学

JNI是一种Java开发中使用到的技术手段,而NDK则是谷歌为Android提供的一种使用JNI的开发工具套件.

JNI的代码是个.c/.cpp文件,可以写c/cpp的函数,也可能用jni的函数,然后二者产生联系,jni又可以和java沟通

实际关系:c/c++ <--->jni<--->java

JNI/NDK入门指南

域描述符

域描述符是JNI中对Java数据类型的一种表示方法(就是对Java类中的变量,在JNI世界的定义),
即在JVM虚拟机中,存储数据类型的名称时,是使用指定的描述符来存储,而不是我们习惯的 int,float 等

/*常用引用数据类型对比
 * All object              jobject
 * java.lang.Class         jclass
 * java.lang.String        string
 * int[]                   jintArray
 * Object[]                jobjectArray
 *
 * JNI 函数签名信息
 * java支持函数重载,因此仅仅根据函数名是没法找到对应的JNI函数。
 * 为了解决这个问题,JNI将参数类型和返回值类型作为函数的签名信息
 *
 * JNI规范定义的函数签名信息格式: (参数1类型字符…)返回值类型字符
 *
 * 例子:Java函数 对应的 函数签名
 *  String sayHello2();           "()Ljava/lang/String;"
 *  long fun(int i,Class c)        "(ILjava/lang/Class;)J"
 *  void fun(byte[] bytes)         "([B)V"
 *
 *  JNI常用的数据类型及对应字符:
 *
 *  Java类型        对应字符
 *  void            V
 *  boolean         Z
 *  int             I
 *  long            J
 *  double          D
 *  float           F
 *  byte            B
 *  char            C
 *  short           S
 *  int[]           [i
 *  String          Ljava/lang/String;(后面一定要加;)
 *  Object[]        [Ljava/lang/object;
 * 
  多维数组则是 n个[ +该元素域描述符 , N代表的是几维数组。例如:
  Java类型: int[][]
  JNI域描述符:[[I
    
  Java Method	                        Method Description
  String fun()	                    ()Ljava/lang/String;
  int fun(int i, Object object)	    (ILjava/lang/Object;)I
  void fun(byte[ ] bytes)	            ([B)V
  int fun(byte data1, byte data2)	    (BB)I
  void fun()	                        ()V

 *
 *  JNIEnv 是个啥? 它是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境。通过JNIEnv可以调用到一系列JNI系统函数,
 *  用于访问Java成员变量何成员方法,调用Java构造方法创建Java对象等
 *
 *  JNIEnv线程相关性: 每个线程中都有一个 JNIEnv 指针。JNIEnv只在其所在线程有效, 它不能在线程之间进行传递
 *
 * */

void的处理比较特殊,如果返回值为void,那么方法描述符中必须使用V表示,当void作为参数的时候,忽略

static JNINativeMethod gMethods[] = {
	{"getJavaBeanFromNative", "()Lcom/xxx/object2struct/JavaBean;",(void*)Java_com_xxx_object2struct_JniTransfer_getJavaBeanFromNative },
	{"transferJavaBeanToNative", "(Lcom/xxx/object2struct/JavaBean;)V",(void*)Java_com_xxx_object2struct_JniTransfer_transferJavaBeanToNative },
};	

jobject/jclass

jobject与jclass通常作为JNI函数的第二个参数,有何不同?

实例引用和java.lang.Object类或它的子类的实例对应。
类引用与java.lang.Class实例对应,它代表着类的类型。

	//下述函数都是类引用有关系,所以参数都是jclass
    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
    jobject     (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
    jobject     (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);

	//下述函数都是和实例引用有关系,所以参数都是jobject
	jobject     (*GetObjectField)(JNIEnv*, jobject, jfieldID);
	jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
	object     (*GetObjectField)(JNIEnv*, jobject, jfieldID);
	

当Java中定义的native方法为静态方法时,则第二个参数为jclass,jclass代表native方法所属类的class本身
当Java中定义的native方法为非静态方法时,则第二个参数为jobject,jobject代表native方法所属类的实例对象

javah和javap的使用和集成
JNI的字符串处理

动态注册/静态注册

静态注册:优点非常简单,用Android studio 新建的ndk-jni demo 就是默认用的这种方式.
但是有一个最致命的缺点程序运行效率低,耗时.但是使用jni就是为了提供效率,所以不推荐.

动态注册:Android源码使用.
原理:直接通过 JNIEnv中提供的函数RegisterNatives方法手动完成 Java中Native 方法和JNI中相关函数的的绑定,
虚拟机可以通过这个函数映射表直接找到相应的方法

frameworks\base\services\core\jni\com_android_server_AlarmManagerService.cpp

static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, jlong nativeData)
{
    AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
    int result = 0;

    do
    {
        result = impl->waitForAlarm();
    } while (result < 0 && errno == EINTR);

    if (result < 0)
    {
        ALOGE("Unable to wait on alarm: %s\n", strerror(errno));
        return 0;
    }

    return result;
}

static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
    ...
    {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
    ...
};

int register_android_server_AlarmManagerService(JNIEnv* env)
{
    return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",
                                    sMethods, NELEM(sMethods));
}

步骤:

1.利用结构体JNINativeMethod保存Java Native函数和JNI函数的对应关系;

2.在一个JNINativeMethod数组中保存所有native函数和JNI函数的对应关系;

3.在Java中通过System.loadLibrary加载完JNI动态库之后,调用JNI_OnLoad函数,开始动态注册;

4.JNI_OnLoad中调用通过调用JNIEnv中的函数RegisterNatives函数进行函数注册;

 MainActivity.java

public class MainActivity extends Activity {

    // Used to load the 'myapplication' library on application startup.
    static {
        System.loadLibrary("myapplication");
    }

    private ActivityMainBinding binding;

    private int code = 8;
    private String msg = "hello JNI";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        //java 调用 Native函数
        tv.setText(stringFromJNI());
        tv.setText(sayHello());
        tv.setText(sayHello2());
        tv.setText(sayAloveB(5,2)+"");

        testCallJava(MainActivity.this);
    }

    public void cCallJava(String str){
        Log.i("TAG","cCallJava:" +str);
        Toast.makeText(this,str,Toast.LENGTH_SHORT).show();
    }

    public native void testCallJava(MainActivity activity);

    /**
     * A native method that is implemented by the 'myapplication' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native String sayHello();

    public native String sayHello2();

    public native int sayAloveB(int a,int b);
}
native-lib.cpp

#include <jni.h>
#include <string>
#include "android/log.h"

#define TAG "jni_t"
#define LOGE(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"tww" ,__VA_ARGS__)


extern "C" JNIEXPORT jstring JNICALL
Java_com_autochips_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    LOGE(TAG,"ssssssfwefsfsfw");
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_autochips_myapplication_MainActivity_sayAloveB(JNIEnv *env, jobject thiz, jint a, jint b) {
    // TODO: implement sayAloveB()
    jint ab = a + b;
    return ab;
}


//静态注册
extern "C"
JNIEXPORT jstring JNICALL
Java_com_autochips_myapplication_MainActivity_sayHello(
        JNIEnv *env, jobject /* this */) {
    std::string hello = "tang say Hello JNI";
    return env->NewStringUTF(hello.c_str());
}

//动态注册
jstring sayHello2(JNIEnv *env, jobject /* this */) {
    std::string hello = "tang sayHello2 ----------";
    //获得一维数组的类引用,即jintArray类型
    jclass intArrayClass = env->FindClass("[I");
    //构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为length,类型为 jsize
    jobjectArray jobjectArray1 = env->NewObjectArray(6, intArrayClass, NULL);
    return env->NewStringUTF(hello.c_str());
}

static const JNINativeMethod gMethods[] = {
        {"sayHello2",//对应java交互类的名字
         "()Ljava/lang/String;",//对应方法名的函数签名
         (jstring *) sayHello2}//对应 cpp交互类的指针函数
};

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    __android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
    JNIEnv *env = NULL;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK)//从JavaVM获取JNIEnv,一般使用1.4的版本
        return -1;
    //注意:这里 FindClass 必须要和交互类的 包名对应上,并换成[/]符号
    jclass clazz = env->FindClass("com/autochips/myapplication/MainActivity");

    if (!clazz) {
        __android_log_print(ANDROID_LOG_INFO, "native", "cannot get class,"
                                                        "com/autochips/myapplication/MainActivity");
        return -1;
    }

    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
        __android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
        return -1;
    }

    return JNI_VERSION_1_4;
}


//native函数调用Java函数
extern "C"
JNIEXPORT void JNICALL
Java_com_autochips_myapplication_MainActivity_testCallJava(JNIEnv *env, jobject thiz,
                                                           jobject activity) {
    // TODO: implement testCallJava()
    //获取MainActivity
    jclass cls = env->GetObjectClass(activity);
    //拿成员变量ID
    jfieldID codeId = env->GetFieldID(cls,"code","I");
    jfieldID msgId = env->GetFieldID(cls,"msg","Ljava/lang/String;");

    //通过id获取其值
    jint code = env->GetIntField(activity,codeId);
    jstring msg = (jstring)env->GetObjectField(activity,msgId);
    //获取java.lang.String对象中的内容
    const char *cMsg = env->GetStringUTFChars(msg,JNI_FALSE);//C++里面没有

    LOGI("code = %d,msg = %s",code,cMsg);
    env->ReleaseStringUTFChars(msg,cMsg);//用完String之后释放

    //找到函数ID
    jmethodID callJavaMethodId = env->GetMethodID(cls,"cCallJava","(Ljava/lang/String;)V");
    jstring  nativeMsg = env->NewStringUTF("java method cCallJava Go!!!");
    //调用java中的cCallJava函数
    env->CallVoidMethod(activity,callJavaMethodId,nativeMsg);

    // 这里的DeleteLocalRef可以不执行,在函数执行完毕后LocalRef会自动释放,
    // 但是在循环次数较多的循环中需要Delete,否则可能会溢出
    env->DeleteLocalRef(msg);
    env->DeleteLocalRef(nativeMsg);
    env->DeleteLocalRef(cls);
}

JNI 基础
native函数调用Java函数
阿豪JNI

posted @   僵小七  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示