Android NDK环境搭建及JNI开发java和C的相互调用的基本介绍

由于技术和表达能力有限,直接上重点,主要是针对jni及NDK使用过程中的问题。
首先搭建好NDK环境,作为一个刚开始使用的NDK的工程师,肯定会相当陌生,于是乎,参考别人,搜索的日子就来了。由于Android Studio用的不是很熟悉,加上公司其他人基本使用的是Eclipse,所以我 也就使用Eclipse了。
首先下载NDK,Android studio很简单,直接进入SDK就有选项,Eclipse的话就有点麻烦要单独去下载。下载地址: http://developer.android.com/sdk/ndk/index.html
直接解压安装就可以了。接下来就是要搭建NDK环境了。
首先检查下Eclipse是否有NDK辅助插件,在单独下载的ADT自己配置选择全部的情况是是肯定有的,但是在bundle的Eclipse就不一定了。

如果没有,参照大神们的解决方案: http://blog.csdn.net/lovexieyuan520/article/details/43225887

有了NDK之后,我们就是要点击这个NDK选项之后,把解压后的路径放上去。至此,NDK大环境算是搞定了。但是,这样我们怎么知道好没好呢,很简单,运行一个简单的Demo就知道了。
新建项目:TestNDK(大家都是这个例子,我也就勉为其难为了符合大众了)。
一路next之后,就有了项目自带的Hello World了。好了,接下来就是建立和C/C++之间关联的时候到了。
简单粗暴的方式:选中项目,右键->Android tools ->Add Android Native Support进入

wjw_ndk就是生成的so文件,也是java需要调用的JNI的本地静态库的名称,点击Finish之后,Eclipse就会自动帮我们生成jni文件夹,里面还会包含我们需要的Android.mk,相当于C/C++的makefile,没做过C++开发的童鞋肯定不知道makefile是什么,没关系,搜下概念就行,就是对代码的管理文件。不知道也不影响我们开发Android。于是,我们对项目的环境也搭建好了,你会发现在项目中多了个obj文件夹,里面放的就是我们需要的.so文件,没运行时,它是空的,等待我们生成。现在开始写测试代码。在jni文件夹里面的两个文件,我们只需要对wjw_ndk.cpp这个生成的源文件进行编写C的代码就行了。

只做简单的java调用C的函数。仿照NDK Sample写个例子
java:
public class MainActivity extends Activity {
<span style="white-space:pre">	</span>static {
<span style="white-space:pre">		</span>System.loadLibrary("wjw_ndk");//库名字一定要和自己设置的一直
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>public native String stringFromJNI();// 声明JNI层的原生方法,使用native关键字
<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>protected void onCreate(Bundle savedInstanceState) {
<span style="white-space:pre">		</span>super.onCreate(savedInstanceState);
<span style="white-space:pre">		</span>TextView tvText = new TextView(this);
<span style="white-space:pre">		</span>tvText.setText(stringFromJNI());
<span style="white-space:pre">		</span>setContentView(tvText);
<span style="white-space:pre">		</span>setContentView(tvText);
<span style="white-space:pre">	</span>}
}



wjw_ndk.cpp文件:
#include <jni.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_com_wujianwu_testndk_MainActivity_stringFromJNI(
JNIEnv *env, jobject thiz) {
return env->NewStringUTF("Hello jni");
}
}


简单说下:native声明本地方法,静态加载so库。JNIEXPORT 和JNICALL 固定使用,算是提供给java调用的声明吧,jstring 表示的是java调用的是String类型的。 JNIEnv类型代表Java环境。通过这个JNIEnv*指针,就可以对Java端的代码进行操作。如,创建Java类的对象,调用Java对象的方法,获取Java对象的属性等。JNIEnv的指针会被JNI传送到本地方法的实现函数中来对Java端的代码进行操作

对于Java_com_wujianwu_testndk_MainActivity_stringFromJNI,前面开头的必须是Java,开头是大写,把java中的点换成下划线,最后跟着的是java中定义好了的方法,也就是java定义的本地方法在C里面实现,供java调用。上面例子返回的字符串是"Hello jni"。
java调用步骤总结如下:
1、声明本地方法,使用native修饰并静态加载so库
2、cpp文件声明java调用的函数,并实现
3、java调用native声明的方法
有时候我们并不仅仅是java调用C,如果C调用Java的方法和属性,我们改怎么去实现呢,这就有点复杂了。但是简单的我们还是可以做到的。

首先看下面的一些基本介绍看看改怎么调用java中的代码:
1、JNIEnv类中的函数:
NewObject/NewString/New<TYPE>Array :new新对象
Get/Set<TYPE>Field:获取属性
Get/SetStatic<TYPE>Field :获取静态属性
Call<TYPE>Method/CallStatic<TYPE>Method:调用方法
现在知道JNIEnv是多重要了吧。
2. Java数据类型与C/C++数据类型的对应关系
Java类型 别名C++本地类型字节(bit)
booleanjbooleanunsigned char8, unsigned
bytejbyte  signed char8
char     jchar  unsigned short16, unsigned
short jshortshort 16
int jint   int32
longjlonglong64
float     jfloat     float     32
double   jdouble   double   64
void     void     n/a n/a 
Object    _jobject  *jobject 
为了能够在C/C++使用Java类,jni.h头文件中专门定义了jclass类型来表示Java中的Class类
jclass的取得:
JNIEnv类中有如下几个简单的函数可以取得jclass
jclass FindClass(const char* clsName) 根据类名来查找一个类,完整类名。
jclass GetObjectClass(jobject obj) 根据一个对象,获取该对象的类
jclass GetSuperClass(jclass obj) 获取一个类的父类
FindClass 会在classpath系统环境变量下寻找类,需要传入完整的类名,注意包与包之间是用"/"而不是"."来分割
如:jclass cls_string= env->FindClass("java/lang/String");
展示下调用的例子:
extern std::string Test() {
SGJniMethodInfo t;
std::string ret("");
if (SGSDKJniHelper::getStaticMethodInfo(t, CLASS_NAME, "getImei","()Ljava/lang/String;")) {
jstring str = (jstring) t.env->CallStaticObjectMethod(t.classID,t.methodID);
t.env->DeleteLocalRef(t.classID);
ret = SGSDKJniHelper::jstring2string(str);
t.env->DeleteLocalRef(str);
}
return ret;
}

extern void showTestJNI() {
SGJniMethodInfo t;
if (SGSDKJniHelper::getStaticMethodInfo(t, CLASS_NAME, "showGameForum","()V")) {
t.env->CallStaticVoidMethod(t.classID, t.methodID);
t.env->DeleteLocalRef(t.classID);
}
}


其中:
typedef struct SGJniMethodInfo_
{
JNIEnv * env;
jclass classID;
jmethodID methodID;
} SGJniMethodInfo;

class SGSDKJniHelper{
bool SGSDKJniHelper::getStaticMethodInfo(SGJniMethodInfo &methodinfo,
const char *className, 
const char *methodName,
const char *paramCode) {
if ((nullptr == className) ||
(nullptr == methodName) ||
(nullptr == paramCode)) {
return false;
}

JNIEnv *env = SGSDKJniHelper::getEnv();
if (!env) {
return false;
}
jclass classID = _getClassID(className);
if (! classID) {
env->ExceptionClear();
return false;
}

jmethodID methodID = env->GetStaticMethodID(classID, methodName, paramCode);
if (! methodID) {
env->ExceptionClear();
return false;
}
methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;
return true;
}
std::string SGSDKJniHelper::jstring2string(jstring jstr) {
if (jstr == nullptr) {
return "";
}
JNIEnv *env = SGSDKJniHelper::getEnv();
if (!env) {
return nullptr;
}

const char* chars = env->GetStringUTFChars(jstr, nullptr);
std::string ret(chars);
env->ReleaseStringUTFChars(jstr, chars);

return ret;
}
}


同样是面向对象的语言,不懂得童鞋可以稍微看下C++的语法,注意的一点是,C++要自己释放资源,不像java这么方便,系统自动帮我们实现了垃圾回收。


NDK搭建与实例过程中,遇到的一些问题如下:可以参考: http://www.2cto.com/kf/201505/403121.html
 
Eclipse报错“Unresolved inclusion jni.h”
在做NDK开发过程中有时候在eclipse里会遇到其无法处理inclusion导致symbol显示错误,网上有许多方法可以解决类似“Unresolved inclusion jni.h” 错误的方法,包括includepath等方法,不过对我都不管用。
最终的解决办法就是初始化eclipse对该project的nativesupport:
1. 在eclipse中关闭指定Project
2. 用其他编辑工具打开该project的.project文件,删除以下内容:
......
org.eclipse.cdt.managedbuilder.core.genmakebuilder
clean,full,incremental,
......
org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
full,incremental,
......
org.eclipse.cdt.core.cnature
org.eclipse.cdt.core.ccnature
org.eclipse.cdt.managedbuilder.core.managedBuildNature
org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
3. 删除.cproject文件
4. 在eclipse里打开原来的project, refresh,然后右键->properties->Android Tools -> Add Native Support

window平台下 Eclipse Ndk开发中的Method 'NewStringUTF' could not be resolved问题
 
项目右键->属性->c/c++常规->Code Analysis,选择Use project settings 中的方法无法被解析(Method cannot be resolved)取消选择,应用->确定,然后刷新、清理、刷新、build项目。
 
jni/hellocpp/main.cpp:16:18:error: base operand of '->' has non-pointer type 'JNIEnv {aka _JNIEnv}'
 
 
错误在于:
(*env)->NewStringUTF(env, Hello from JNI !);
这一行,这是c的写法,而我的是cpp程序,需要改写成:
env->NewStringUTF( Hello from JNI !);
 
java.lang.UnsatisfiedLinkError:Native method not found问题
 
 
1、c++中的方法Java_xxx_xxx中的Java 首字母一定要大写
2、如果是 .cpp 文件则用 extern C {您的方法在这里},大括号里是您的本地方法。
补充:
3、库的名字有没有一致,方法名字有没有写错等。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值