您的位置:首页 > 移动开发 > Android开发

Android Studio 通过JNA调用 Clang编译的so库,以及Java JNA 调用x64dll,C# dllimport调用x86dll

2016-12-21 15:37 375 查看
转自:http://lib.csdn.net/article/android/43451

JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native
library的函数与结构,JNA将自动实现Java接口到native function的映射。


优点

JNA可以让你像调用一般java方法一样直接调用本地方法。就和直接执行本地方法差不多,而且调用本地方法还不用额外的其他处理或者配置什么的,也不需要多余的引用或者编码,使用很方便。  JNA最大的好处是避免了重复造轮子的浪费时间的行为.

 用过JNI的大神都知道,JNI是一种很变态的设计,JNI需要导入专门为Java生成的头文件,是一种侵入式的设计,这样被强行改造的源码编译的dll将不能被C#调用


JNA描述

JNA类库使用一个很小的本地类库sub 动态的调用本地代码。程序员只需要使用一个特定的java接口描述一下将要调用的本地代码的方法的结构和一些基本属性。这样就省了为了适配多个平台而大量的配置和编译代码。因为调用的都是JNA提供的公用jar
包中的接口。


目标

编辑

JNA是为了程序员调用本地代码更方便,跑的更快以及减少出错。

JNA的使用:

好了废话不多说,下面开始介绍JNA怎么样,并且以一个有实际意义的Ecc椭圆曲线加密算法作为介绍的例子,而不是

以经典的Hello world,大家可以按照步骤,生成一个hello world类库.

大家都知道Android一般都是作为客户端,服务器端都是PC机,

Ecc椭圆曲线不对称加密算法一般是用来加密 对称密钥

然后把对称密钥通过网络安全的传输给对方, 密文只能用私钥解密,

只要保证了私钥的安全性也就保证了 对称密钥的安全

这里我先以Windows平台为例,在PC端先实现Ecc加密算法,之后再介绍在Android中使用Ecc.so库

     最少所需的开发工具是 Android Studio 2.0, Visual Studio 2015, Eclipse mars版,

      建议使用vs2010生成windows平台的dll文件. 

1.首先下载 开源的JNA框架  链接已经提供好了 拿好不谢 链接:http://pan.baidu.com/s/1bpJrrJL 密码:ygod

2. 第二步开始为Windows 7 x64平台编译 ecc.dll加密算法,

我安装的Java JDK是 1.8 x64 我使用的编译器的 vs2010,

不要使用visual studio 2010 以上版本,以及 codeblocks和Dev 等编译器,

不然到时候你会调用dll的时候有好多坑等着你.

      Tips:

      如果你没有安装visual studio 2010,只安装了vs2015,而且实在不想再去安装vs2010.

       这里有个解决的办法,那就是使用静态编译,之前visual studio 各版本默认都是动态编译

       依赖的运行库都是在系统目录中的dll,如果编译出来的应用程序可以在开发者的电脑中运行,

       那么依赖的运行库可以在 C:\Windows\System32 或者 C:\Windows\SysWOW64 中找到

       高版本的visual studio动态编译可能带来一个问题,在别人电脑中就不能正常运行了.

       只需要设置静态编译,一般就能在别人的电脑中正常运行了.如果大家设置了无效,那还是

       使用低版本的vs开发工具吧.静态编译也有不好的地方,那就是生成的exe或者dll文件相比于

       原来的动态编译大好几倍,这不利于网络传输,并且整个项目编译期间产生的的其他文件都会

       相当大.非常占电脑的硬盘空间.所以建议能不用静态编译还是不用为好.

       只需要把运行库的选项从 /MDd 改成 /MTd 就行了.

       静态编译怎么设置如下图所示:

       


      接下来在项目的属性页中设置 C/C++ -> 预编译头 -> 不使用预编译头

因为我的代码中使用tommath是C语言数学函数库,ecc加密部分是C++代码,不这样设置会编译失败的

然后编译的时候分别为x86和x64平台编译32位和64位dll文件

至于为什么这么做  因为 Java JDK 1.8 x64  JNA调用DLL只认识 64位的 ecc.dll文件

而 C# 中Dllimport特性调用ecc.dll 只认识 32位的dll,这就尴尬了 不得不为C#和Java JNA生成不同的dll

在vs2010中怎么生成x86和x64的动态链接库如下图



从此处复制设置一定要选择为空

然后每种架构编译一次ecc.dll.目录结构如下图 



Debug中的是32位dll文件给C#调用的 , x64文件夹中的是64位dll给java调用的

这是我编译好的ecc.dll下载链接:
 http://pan.baidu.com/s/1pLEjdhD

为了能证明这是我原创的文章,暂时不公布源码,等我找到工作我就把源码发出来

大神们就不要去反编译我的dll文件了

我确实不是用JNI调用的dll  如下图所示,大家别急,过一段时间我就发出来源码

因为我安装了 VAssistX插件,导致那些中文出现红色波浪线,不用管它,这是正常情况


 

 既然有了dll,那么我开始要用Eclipse用JNA调用 ecc.dll了

  首先导入之前下载的Jna.jar   Jna.jar所在的目录是:



我已经帮大家准备好了,不要导错包和 jnidispatch.dll64位JDK用64位的dll

然后在Eclipse中导入jna.jar  并且把 64位 ecc.dll和 jnidispatch.dll放在Eclipse项目的根目录下

如下图所示:   这个Eclipse项目测试代码下载地址是:http://pan.baidu.com/s/1hsPSFcg



好了,我在Windows平台运行的结果算是正常的,关于在Windows平台通过JNA调用C/C++ dll有两点需要特别注意

的,第一点是需要声明一个调用Native本地方法的接口,如下图所示



这些方法都是我已经在C/C++类库中已经写好的,这里只需要声明相同的方法名即可,数据类型的位数必须对应好

例如,C/C++中 char是一个字节,而Java中char是两个字节,但是Java中的byte是一个字节,于是用byte[]代替char*

第二点需要注意的是: 在使用自己之前编写的C/C++ dll动态链接库之前先要加载JNA提供的一个dll.如下图所示:



如果你还是看不懂也没关系, 你可以把我的代码下载了慢慢看,地址是:http://pan.baidu.com/s/1hsPSFcg代码里我

写了不少注释.

关于Windows平台Java通过JNA调用C/C++ dll的示范就到这里了.

简单提一下C#调用C/C++ dll的方法,C#里调用就更简单了只需要几行代码就行了.

首先导入命名空间  using System.Runtime.InteropServices; 

然后声明Native方法的接口,不必实现这个方法,直接调用就行了,如下图所示:



这里有一点需要注意,即使我的操作系统是64位的,C#只能识别出来32位的dll,所以我分别为Java生成了64位的dll

然后又为C#生成32位的dll,看不懂没关系,C#调用C/C++的源代码下载地址:http://pan.baidu.com/s/1qYEMZmc

3.关于Windows平台的调用就到这里了,马上就到了关键部分了,Android平台怎么在

Android Studio中调用我编译的so库呢,之前我在这里遇到不少坑,跪了无数次,最开始

我是用NDK的,结果发现NDK tmd 居然是在用落后的JNI调用C/C++ 然后弄得都快吐

血了,最后机智的我想到了在Android Studio中使用JNA

绕了个弯,最后我另想了个办法,于是我使用Clang编译arm和x86的so库,

让我花了不少心思

要想使用Clang,首先你得安装 Visual Studio 2015,我用的是专业版,创建一个项目,如

下图所示:



有时候visual studio 2015 会卡住,这个是几率性的事件.  暂时别急,休息一下马上回来....

这是Clang项目的一个小bug,一般不会卡住超过30秒,如果两分钟还没好,保存你正在做的工作和重要文件

然后注销计算机,再次打开这个项目,还不行你就重启电脑

等我找到工作我会把ecc加密算法的源码放在github中,暂时就不上传了

也许你会说我会用NDK为什么还要去花费时间学习Clang这些陌生的东西,Clang可不仅仅是用于Android还能再

Linux中使用,并且编译效率比Gcc更好,更快. 为Android编译的so库完美的在真机和模拟器中通过运行测试

Android Studio 2015 还能使用XCode为 ios编译动态链接库,如果你用过 Clang和Android JNA就不会再想使用

NDK + JNI了, 接下来该干嘛呢,当然是复制粘贴代码文件了,聪明的程序员永远都不会浪费时间去多些一行代码

把之前我在visual studio 2010中为Windows平台编写ecc.dll的代码文件夹打开,然后复制到visual studio 2015中

如下图所示:



这时候visual studio 2015中不会显示这些代码,接下来就简单了,右键当前项目 -> 添加 -> 现有项  如下图所示:





这里先添加了tommath数学函数库的头文件和ecc.cpp加密算法,然后重复此操作把 c文件夹下面的所有 .c后缀文

件添加到项目中,这些.c文件是tommath数学函数库的实现.

你是不是心急的就开始编译了,这当然是不行的跨平台还是需要对代码进行一些小小的改动,如下图所示:



这时候要把专门为Windows平台的 _declspec(dllexport)  去掉,其他的就不一一列举

了,请自行百度,跨平台也不是那么难,用的多了自然就慢慢会了,大家不要心急,学习是

需要足够的耐心的,还有一个坑这里顺便提一下

即使同样是Android平台 arm架构的处理器 (int) char(255) 的值是 255 ,然而在 x86或

者x64架构的处理器在却他喵的蛋疼的 -1 ,为了这个坑我花了好长时间调试代码,最后

才发现即使是最简单的基本数据类型,不同的处理器运算结果都不一定是相同的, 即使

是同样的代码为不同的CPU生成的代码运算结果居然是不同的.

我使用的Android模 拟器是x86架构的,因为Google说x86架构的模拟器开启了VT-x之

后,启动速度是arm模拟器的10倍,基本上启动模拟器不到10秒就开机了,确实很好用.

在x86架构的CPU中我的运算结果是正常的.

我的Android手机的系统是Android4.2.2,所以在我的Android真机中调试的时候, -1 永

远也不会等于 255,自然得不到正确的运算结果了,大家如果遇到了同样的坑,也不用怪

编译器,也不要怪操作系统,只能说这些CPU各自使用不同的标准和指令集有点坑,这时

候你就要慢慢调试了, 我的那个getchar255() 方法就是为了调试而写的. 所以在x86中

我把 变量的值和 -1 判断是否相等,到了arm中这个变量就只能判断是否和 255 是否相

等了.

下面开始编译 ecc4a.so库了,这时候又会遇到一些坑,如下图所示:



别激动,我会告诉你为什么,因为这是因为这个项目中既有C语言代码,又有C++代码,这样混合编译必定会出现这个

问题,解决的办法就是取消预编译头.如下图所示:



在常规选项中还能更改平台工具集的选项以使用不同的编译器,如下图所示:



可以看到这里默认是Clang3.6,上面的CPU平台默认是x86,GCC 4.9 和Clang3.6生成的so库都没有问题,在Android

模拟器中运行效果是一样的,建议使用默认值 Clang3.6就行.然后就是生成 so库了.天了噜,又出现了一个报错,别着

急,有错误慢慢解决,一回生,二回熟,只要下次遇到这个错误能离开解决就行了,如图所示:



这个错误是因为缺少头文件,双击这个错误信息,跳转到出现错误的源文件中,在头部输入一行代码:

 #include <android\log.h>然后再次重新编译就可以了.重要提示,在x86架构和arm架构中 char(255) 的值是不同的,  

如果你是为x86架构处理器编译就要修改为 -1,若是为arm架构处理器编译就要修改为255.

这种情况一般都是在头文 件中分别为x86架构和arm架构定义一个宏,然后使用这个宏,到时候只需要修改这个宏的

值就行了,例如吧 -1 改成 255 不然代码里到处都要修改,这就有点尴尬了,为了演示这种问题,代码里没有使用宏,如

下图所示:



千万要注意这些细节上的问题,否则到时候不会有任何错误提示,你会陷入无限的纠结之中.这时候编译出来的so库

才是正常的. x64和x86都是一个系列的直接编译就行.若是arm架构则要特别注意这些问题.

编译好的so库如图所示:



x86和x64的so库已经生成好了,分别在这两个文件夹中,之后就是arm系列架构的CPU了,把-1改成255就行.大家千

万别学我这种强行修改代码的方式,而是使用宏,这里只是为了演示这个坑.

所有CPU架构的so库编译出来之后是这样的,如图所示: so库在这4个文件夹中



好了,所有的准备工作都已经做完了,下面开始介绍怎么调用这些so库了,我使用的是Android Studio 2.0,

建议大家不要再使用Eclipse了,Google已经在不断提供Android Studio的更新,做的越来越好,是Android开发的首选

用Android Studio 创建一个项目来测试能否成功调用这些so库,项目结构和之前在Eclipse中使用有相同的地方,也

有一些不同的地方.目录结构如图所示:



特别提醒:  jnidispatch前面的lib前缀去掉,后面的.so后缀也去掉, 同

理libecc4a.so也是,在main目录中创建jniLibs目录,
下一级目录的名字必须严格按照这种命名方式,不然到时候报错,找不到so库. libs目录一般是用来放jar包的.

这个Android项目的源码链接是:
http://pan.baidu.com/s/1boPMCJP
最后还要两个坑顺便提一下,在Java中 String  s="esrtewt";

然后把这个 s 转换为字节流也就是 byte[] , byte[] b=s.getBytes[];

看上去好像没什么问题,但是C/C++中,是以 \0 作为字符串终止符的,

    而字节流 b 中不包含 \0   使用 b 这个字节流作为参数调用C/C++方 

    法肯定会跪.

只要给这个字节流补上 \0 即可,解决办法如下图所示:



关于Android Studio 通过JNA调用Clang生成的so库就介绍到这了.

如果大家觉得写一个类似于Ecc椭圆曲线加密so库比较难,那么可以

先尝试着写个Hello World出来试试手.

跨平台跨语言跨CPU架构的编程肯定会遇到一大堆问题的,大家要有

耐心来一个个解决这些问题.

为了能证明这个文章是我原创的,Ecc椭圆曲线加密算法的源码就不

发出来了,编译好的dll和so文件都已经在测试项目了.

至于这个项目的意义是什么,比如Android自带的Media播放器支持的

视频格式不多,

如果你需要为Android制作一个播放器,这时候你会考虑一些开源的

视频/音频C/C++视频编解码框架,例如非常著名的FFMpeg就是C/C++

写的.

如果你用Visual Studio Clang编译器生成so库,你会发现这个

NDK+JNI用起来愉快多了,并且不用怎么修改源文件,代码拿到Linux平

台重新编译,甚至都能够直接编译为Linux平台的so库了.

我会的技术有C#,Java,C/C++也会一些,

  ASP.NET MVCJava EE,Nhibernate,Entity 

Framework 6,等等Java框架会一些,c# 中tcp和udp网

络编程,以及C#/Java多线程也会一些,3大数据库MS 

SQL Server,MySql,Oracle都会一些,还有Winform等

也会.一些前端技术css,jquery,html也会,我的自学能

力比较不错,我申请过百度地图的开发者账号,并且成

功调用百度地图API获取当前地理位置,也做过一些简

单的二维码扫描测试项目,以及Android的图案解锁之

类的功能.

这是我的个人 github主页,里面有一些我写的部分代码
https://github.com/gesneriana?tab=repositories
我想找一个Android,或者ASP.NET,Java EE 开发的工作,求有多年

  C/C++,C#,Java,Python编程经验的老司机带我,

  有意向的可以联系我的邮箱,我会优先考虑条件好的公司,我的邮箱是:

 s694060865@163.com

如果邮箱联系不上可以扣扣联系我:  QQ 694060865

QQ邮箱 694060865@qq.com

整个项目的源码链接是: 链接:https://pan.baidu.com/s/1bpJrrJL 密码:ygod

除了Ecc椭圆曲线加密算法的源码没有,Java,C#,Android都有源码.

为了能证明这个项目是我原创的,Ecc加密的源码等我找到工作了之后在发出来.

这是我第一次写blog,如果有写的不够详细或者写的不好的地方,欢迎大家指正,

如果大家表示看了源码还是不怎么明白,在自己进行尝试的时候不断出一些坑,

有空的时候我会做一个视频,视频比较简单,就是调用Jna 调用C/C++本地方法输出

Hello World的简单的小程序.

欢迎大家转载,但是转载请注明出处.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: