多语言编程之 C and Java:JNI 30 分钟入门

2016/06/29

1 引言

1.1 简介

在实际项目中,很多时候是以多种语言混合开发的。常用的模式是使用 C/C++ 做底层支撑,然后上层辅之以高级语言。 JNI可以使 native 代码与 Java 对象交互,而不会像 Java 代码中的功能那样受到诸多限制, 往往充作 Java 与 C/C++/Assembly 语言的粘合剂。

JNI 明确分开了 Java 代码与 native 代码的执行,为两者的通信定义了一套清晰的API.避免 native 代码对 JVM 的直接内存引用。

2 JNI 入门

2.1 JNI 调用 C

2.1.1  java 中的 native 方法声明

HelloJNI.java

当HelloJNI类加载的时候,静态初始化调用 System.loadLibrary() 来加载本地库 hello.dll(或libhello.so)。如果不能加载 hello 库,会抛出 UnsatisfiedLinkError 异常。
然后我们使用 native 关键字声明了 native 方法 sayHello() ,这表明该方法是使用其它语言实现的。
最后 main() 方法实例化了 HelloJNI 并调用了 sayHello().
然后我们编译这个类:

2.1.2  生成 C/C++ 头文件

使用 javah 创建头文件:

会得到如下 .h 文件 HelloJNI.h

这份自动生成的头文件里声明一个 C 函数 Java_HelloJNI_sayHello:

这个方法名是由javah转换来的,转换规则为:

包名中的 点 会被转换为 下划线
函数中带有两个参数(虽然我们在本例中并没有使用这两个参数)

  • JNIEnv*: 指向 JNI environment ,提供对 JNI 方法的访问
  • jobject: 指向 this Java 对象

2.1.3 C 代码的实现

HelloJNI.c

这个实现将在控制台打印万能的 “Hello World!”
文件中包含了 jni.h 头文件。这个头文件由 java 提供,位于 %JAVA_HOME%/include 路径下(Windows下还需要引用 %JAVA_HOME%/include/win32).

2.1.4 编译 C 库

接下来,编译这个 c 文件:

2.1.5 检查成果

阅读全文…

Apache Thrift

2016/05/26

1 简介

1.1 概览

Apache Thrift官网
Thrift是一个跨语言的服务开发部署 框架,其最初由Facebook于2007年开发,于2008年加入Apache开源项目。
Thrift 通过编译thrift文件(由Thrift Types 组织的中间语言,用于定义RPC数据结构),生成不同语言的类。这些类负责RPC协议层和传输层的实现。

1.2  优点

相对于XML、Json,Thrift在性能和传输数据压缩上有绝对优势。

2 简单使用

2.1 过程

  1. 下载并编译 thrift编译器 .
  2. 编写thrift文件.需要用到 thrift自定义数据类型 .包括
    1. 基本数据类型
    2. 特殊数据类型(binary)
    3. 结构体
    4. 容器(list,set,map).容器元素可以是服务以外的任何thrift type .
    5. 异常(Exceptions)
    6. 服务
  3. 编译thrift文件,生成可供服务端和客户调用的源码.命令语法如下:

2.2 简单实例

2.2.1 编辑thrift文件

代码清单如下:

2.2.2 生成cpp代码

使用thrift.exe :

此时会在 “gen-cpp” 文件夹(默认文件夹)下生成相应的cpp代码:

其中 “msg_ types” 和 “msg_ constants” 对数据类型进行了定义,”transMsg” 实现了基本逻辑。”transMsg_ server.skeleton” 则简单实现了一个server端的实例。

2.2.3 实现业务逻辑

新建两个工程,一个做为服务端,一个做为用户端。 将 gen-cpp 文件夹下的文件分别添加到两个工程中,并在工程中添加thrift的静态库(或直接引用源码).
Server端的工程可以直接使用生成的代码。Client端的业务则需要自己实现。
下面简单实现Client端的业务:

其中strHost为服务器的IP.
阅读全文…

Coding 2,260

Unix线程基础

2016/05/24

1 线程标识

每个线程都有一个唯一标识:线程ID. 线程ID用 pthread_t 数据类型来表示,该结构体在不同的系统上有不同的实现,某些操作系统将其实现为 unsigned long ,某些操作系统将其实现为结构体。所有可移植代码中不可以对其直接比较,必须使用一个函数来对两个线程ID进行比较。

不同的实现方式带来的影响就是,不能使用一种可移植的方式来打印该数据类型的值。
获取当前线程:

2 线程创建

新增的线程可以通过调用pthread_create 函数来创建

新创建的线程从 start_routine 函数的地址开始运行。该函数有一个 void* 类型的指针做为参数。如果函数需要多个参数,需要将参数做为结构体,然后将结构体地址做为参数传入。 创建线程时并不能保证新增线程优先于其它线程先运行,这依赖于操作系统的线程实现与调度算法。

阅读全文…

解决 “building for iOS simulator, but linking in object file built for OSX, for architecture x86_64”

2016/03/14

在升级XCode到版本7以后,使用某些静态库(.a文件),并为IOS模拟器编译时,可能会出现如标题所示错误,提示这个库是为OSX系统的X86_64平台编译的。这可能是XCode版本的一个BUG,在链接静态库时,优先链接了OSX平台下的obj,并抛编译错误。

xcode error

为解决此问题,我们在编译静态库时,不要将添加OSX系统下的平台,或者使用lipo将静态库中关于OSX系统平台的内容移除。

首先使用 “-info”选项查看静态库里包含了哪些平台内容:

可以看到此静态库中包含了 i386和x86_64两个平台内容。然后使用 “-remove” 将其移除:

使用移除OSX平台内容后的静态库再次编译,问题解决。

但是会出现warning:

“ld: warning: ignoring file ……/libuuid.a, missing required architecture x86_64 in file ……/libuuid.a (4 slices)”

可以不用理会。但是在发布或部署到真机的时候,还是需要使用带有x86_64平台的静态库。

Note 4,040

了解Poco C++ Poco::Data 数据库基本操作(二)

2016/02/07

Statement

前面我们提到了Statement,但是到目前为止,我们只使用了session,至少从代码上来看是这样的。

而实际上,我们已经在使用statement了,只不过它在代码中隐藏了起来。我们来看看Session类中的<<操作符:

这里<<操作符创建了一个statement并将其返回。前面例子中我们使用<<操作符创建了statement,但是这个statement并没有分配到变量,一直到”now”部分executed了这个隐藏的statement,然后将其销毁。

我们将前面的例子拿一个出来修改一下:

当execute执行以后,数据已经插入了。可以使用stmt.done()来确认操作是否已经完全完成。

Prepared Statements

上例中省略的代码now子句创建了一个prepared statement.

prepared statement 的优势在于性能。我们假设有如下的循环:

在上例中,如果不使用prepated statement,我们需要创建和解析Statement 100次。而现在我们只需要创建并解析一次Statement,然后使用占位符匹配不同的值,多次执行来将name插入到数据中。

当然,对于如何将一系列值插入到数据库,上例的代码并不是最优解。通过Poco::Data与STL容器协作可以更好的解决这个问题。具体的我们将在”STL容器”这一节来讲。 阅读全文…

Coding 3,349

了解Poco C++ Poco::Data 数据库基本操作

2016/01/27

前言

POCO::Data 是Poco的数据库抽象层,为C++提供统一的结构化数据库访问接口,使得C++以简单而自然的方式访问关系型数据库。使用它可以方便地从多种数据库中存取数据。目前Poco::Data支持的数据库连接类型包括 SQLite,MySQL及ODBC。POCO框架支持扩展,所以你也可以添加其他的本地连接扩展。

下面是一个使用Poco::Data::SQLite的一个简单例子:

 

创建会话(Sessions)

通过Session 构造器可以创建Session:

这个例子创建了一个SQLite类型的session。

第一个参数是希望创建的Session类型,目前支持的连接类型有”SQLite”,”ODBC”,”MySQL”。

第二个参数是数据库连接字符串,根据不同的数据库使用不同的连接字符串:

  • 对于Sqlite数据库,数据库路径即可做为连接字符串
  • 对于ODBC数据库,连接字符串类似于 “DSN=MyDSNName”.具体参数请参考ODBC驱动文档
  • 对于MySQL数据,连接字符串由一系列键值对组成,例如 “host=localhist;port=3306;db=mydb;user=thename;password=thepwd;compress=true;auto-reconnect=true”;具体请教参MySQL官方文档。

存取数据

单数据集

假设我们有一个表 tbA,表中有一个字段 fN,插入记录时我们可以这么写:

是不是很简单。然而这种写法并不提倡,我们有更好的方式:占位符!使用占位符(placeholders)可以匹配变量和占位符,在执行语句时自动用变量替换占位符。普遍公认的占位符是问号(?),有些数据库也使用冒号(:)来做占位符。具体使用哪一种,请参考所选用数据库的说明文档。

那么上面的例子可以写做:

此例中使用use(name)来匹配占位符。use是Poco::Data::Keywords下提供的关键字,可以将变量和占位符绑定(Binding)。这么做真正的意义,是将变量与SQL语句分隔开,防止SQL注入式攻击。

从数据库中取数据的操作与之类似。into关键字将数据库返回的值与C++对象匹配起来。并且支持指定默认值以防数据库返回null值。

此处需要注意的是,指定默认值的into,其定义为:into(T& t, const Position& pos, const T& def),使用的是模样类。在有些编译器下,不能隐式转换数据类型,所以需要显示的让第一个参数和第三个参数的数据类型保持一致。

into还可以和use联合使用:

当然,现实中的数据库不会总像例子中这么简单。在一个表中往往有多列,这样需要使用多次into/use关键字:

那么此时尤为重要的,就是into和use的顺序了。第一个占位符使用第一个use,第二个占位符使用第二个use,以此类推。into亦如是。

阅读全文…