JNA實(shí)戰(zhàn)筆記匯總<二> JNA和C/C++的數(shù)據(jù)類型轉(zhuǎn)換

一、JNA技術(shù)的難點(diǎn)

上篇文章我們成功實(shí)現(xiàn)了Java使用JNA調(diào)用C/C++的函數(shù)代碼:

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

上面的代碼非常簡(jiǎn)單,在控制臺(tái)打印輸入"Hello World",并返回整數(shù)型。然后我們?cè)贑Library中也是定義了一個(gè)對(duì)應(yīng)的函數(shù)

int sayHello();

由于C/C++語(yǔ)言和Java語(yǔ)言中的int類型對(duì)應(yīng),所以這里并沒(méi)有復(fù)雜的類型轉(zhuǎn)換,也就大大降低了調(diào)用JNA和C/C++代碼對(duì)接的難度。

有過(guò)跨語(yǔ)言、跨平臺(tái)開(kāi)發(fā)的程序員都知道,跨平臺(tái)、語(yǔ)言調(diào)用的難點(diǎn),就是不同語(yǔ)言之間數(shù)據(jù)類型不一致造成的問(wèn)題。絕大部分跨平臺(tái)調(diào)用的失敗,都是這個(gè)問(wèn)題造成的。關(guān)于這一點(diǎn),不論何種語(yǔ)言,何種技術(shù)方案,都無(wú)法解決這個(gè)問(wèn)題。JNA也不例外。

上面說(shuō)到接口中使用的函數(shù)必須與鏈接庫(kù)中的函數(shù)原型保持一致,這是JNA甚至所有跨平臺(tái)調(diào)用的難點(diǎn),因?yàn)镃/C++的類型與Java的類型是不一樣的,你必須轉(zhuǎn)換類型讓它們保持一致,比如printf函數(shù)在C中的原型為:

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

你不可能在Java中也這么寫,Java中是沒(méi)有char *指針類型的,因此const char * 轉(zhuǎn)到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

附帶一個(gè)很重要的API接口文檔地址:傳送門

三、具體實(shí)例

接下來(lái)我們寫一個(gè)復(fù)雜一點(diǎn)的C++函數(shù)

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

然后在Java中調(diào)用該方法,傳入具體參數(shù),并獲取到返回值。

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類型的加密結(jié)果
 *
 * @Returns           加密是否成功
 */
/* ----------------------------------------------------------------------------*/

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

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

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

    do {
        const int16_t* opt_data = reinterpret_cast<const int16_t*>(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<const int8_t*>(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函數(shù)需要用戶傳入一個(gè)char*指針類型的參數(shù)src_data和一個(gè)short&引用類型的check_ret,代碼對(duì)stc_data進(jìn)行
加密操作之后,把加密結(jié)果返回到參數(shù)check_ret中,函數(shù)返回加密是否成功的標(biāo)記。由于代碼中使用了string,bool等和C++相關(guān)的元素,
所以我們的文件后綴一定要使用.cpp,并且在整個(gè)函數(shù)外部使用了 extern "C" 給C++代碼做標(biāo)記,否則在Java調(diào)用該方法的時(shí)候會(huì)提示無(wú)法找到。

2、然后是CLibrary對(duì)象和MainActivity對(duì)象

編寫完畢代碼,使用Ndk-build命令行進(jìn)行編譯,成功生成libcheck.so文件,配置完畢JNA的相關(guān)jar包和庫(kù)文件,把他們添加到已經(jīng)準(zhǔn)備好的jniLibs文件夾中,然后創(chuàng)建按一個(gè)CLibrary類:

//繼承Library,用于加載庫(kù)文件
public interface Clibrary extends Library {
    //加載libhello.so鏈接庫(kù)
    Clibrary INSTANTCE = (Clibrary) Native.loadLibrary("check", Clibrary.class);
    //此方法為鏈接庫(kù)中的方法
//    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的返回標(biāo)記:"+flag);
        Log.d("MainActivity","checksum的返回結(jié)果:"+check_ret.getValue());

    }
}

代碼定義了一個(gè)點(diǎn)擊事件,點(diǎn)擊按鈕調(diào)用 int flag = Clibrary.INSTANTCE.checksum("123",check_ret),并輸出checksum的返回標(biāo)記和加密結(jié)果。

結(jié)果

項(xiàng)目地址:傳送門

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容