JNA实战笔记汇总二 JNA和C/C++的数据类型转换

本文接上一篇文章 JNA实战笔记汇总一 简单认识JNA|成功调用JNA

一、JNA技术的难点

上篇文章我们成功实现了Java使用JNA调用C/C++的函数代码:

int sayHello(){
    printf("Hello World!");
    return 1;
}

上面的代码非常简单,在控制台打印输入"Hello World",并返回整数型。然后我们在CLibrary中也是定义了一个对应的函数

int sayHello();

由于C/C++语言和Java语言中的int类型对应,所以这里并没有复杂的类型转换,也就大大降低了调用JNA和C/C++代码对接的难度。

有过跨语言、跨平台开发的程序员都知道,跨平台、语言调用的难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。关于这一点,不论何种语言,何种技术方案,都无法解决这个问题。JNA也不例外。

上面说到接口中使用的函数必须与链接库中的函数原型保持一致,这是JNA甚至所有跨平台调用的难点,因为C/C++的类型与Java的类型是不一样的,你必须转换类型让它们保持一致,比如printf函数在C中的原型为:

void printf(const char *format, [argument]);

你不可能在Java中也这么写,Java中是没有char *指针类型的,因此const char * 转到Java下就是String类型了。

二、JNA的常用类型映射(Type Mappings)如下:

Native Type Java Type Native Representation
char byte 8-bit integer
wchar_t char 16/32-bit character
short short 16-bit integer
int int 32-bit integer
int boolean 32-bit integer (customizable)
long, __int64 long 64-bit integer
long long long 64-bit integer
float float 32-bit FP
double double 64-bit FP
pointer Buffer/Pointer
pointer array [] (array of primitive type)
char* String
wchar_t* WString
char** String[]
wchar_t** WString[]
void* Pointer
void ** PointerByReference
int& IntByReference
int* IntByReference
struct Structure
(*fp)() Callback
varies NativeMapped
long NativeLong
pointer PointerType

附带一个很重要的API接口文档地址:传送门

三、具体实例

接下来我们写一个复杂一点的C++函数

bool checksum(const char* src_data, unsigned short&  check_ret)

然后在Java中调用该方法,传入具体参数,并获取到返回值。

1、首先是check.h文件和check.cpp文件:

  • check.h
#ifndef TASK_CHECKSUM_H
#define TASK_CHECKSUM_H

typedef signed char int8_t;
typedef short int  int16_t;
typedef int        int32_t;


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis          checksum加密算法
 *
 * @Param src_data    被加密的字符串 
 *
 * @Param check_ret   int16_t类型的加密结果
 *
 * @Returns           加密是否成功
 */
/* ----------------------------------------------------------------------------*/

extern "C" bool checksum(const char* src_data, unsigned short& check_ret);

#endif
  • check.cpp
#include "check.h"
#include 
extern "C"{

bool checksum(const char* src_data, unsigned short&  check_ret) {
    bool ret = true;

    do {
        const int16_t* opt_data = reinterpret_cast(src_data);
        if (opt_data == NULL || src_data == NULL) {
            ret = false;
            /*TODO*/
            // warn_log();
            break;
        }
        int32_t accu_sum = 0;
        int32_t data_len = strlen(src_data);

        while (data_len > 1) {
            accu_sum += *(opt_data);
            opt_data++;
            data_len = data_len - 2;

            if (accu_sum & 0x80000000) {
                accu_sum = (accu_sum >> 16) + (accu_sum & 0xFFFF);
            }
        }

        if (data_len == 1) {
            accu_sum += *(reinterpret_cast(opt_data));
        }

        while (accu_sum >> 16) {
            accu_sum = (accu_sum >> 16) + (accu_sum & 0xFFFF);
        }
        check_ret = 0xFFFF & accu_sum;
        
//        check_ret = (accu_sum == 0xFFFF)?~accu_sum:accu_sum;

//        check_ret = (accu_sum == 0xFFFF)?accu_sum:~accu_sum;
    }while(0);

    return ret;
}
}

上述代码中checksum函数需要用户传入一个char*指针类型的参数src_data和一个short&引用类型的check_ret,代码对stc_data进行
加密操作之后,把加密结果返回到参数check_ret中,函数返回加密是否成功的标记。由于代码中使用了string,bool等和C++相关的元素,
所以我们的文件后缀一定要使用.cpp,并且在整个函数外部使用了 extern “C” 给C++代码做标记,否则在Java调用该方法的时候会提示无法找到。

2、然后是CLibrary对象和MainActivity对象

编写完毕代码,使用Ndk-build命令行进行编译,成功生成libcheck.so文件,配置完毕JNA的相关jar包和库文件,把他们添加到已经准备好的jniLibs文件夹中,然后创建按一个CLibrary类:

//继承Library,用于加载库文件
public interface Clibrary extends Library {
    //加载libhello.so链接库
    Clibrary INSTANTCE = (Clibrary) Native.loadLibrary("check", Clibrary.class);
    //此方法为链接库中的方法
//    checksum(const char* src_data, unsigned short&  check_ret)
    int checksum(String src_data, IntByReference  check_ret);
}

MainActivity

package com.afinalstone.androidstudy.myjna_02;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClick(View view){
        IntByReference check_ret = new IntByReference();
        int flag = Clibrary.INSTANTCE.checksum("123",check_ret);
        Log.d("MainActivity","checksum的返回标记:"+flag);
        Log.d("MainActivity","checksum的返回结果:"+check_ret.getValue());

    }
}

代码定义了一个点击事件,点击按钮调用 int flag = Clibrary.INSTANTCE.checksum(“123”,check_ret),并输出checksum的返回标记和加密结果。

项目地址:传送门

你可能感兴趣的:(JNA开发)