代码结构如下:
Android下的Java代码 仍然还是使用了eclipse开发环境 这里在NDKUtils.java文件里定义了所有的本地方法
Linux下的C代码 这里是在Linux系统目录下新建的文件 其中libs/和obj/都是由ndk-build编译后编译器产生
// MainActivity.java
public class MainActivity extends Activity implements View.OnClickListener {
private Button btnJni1, btnJni2, btnJni3;
private TextView tvShowInfo;
private NDKUtils jniUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
jniUtil = new NDKUtils();
btnJni1 = (Button) findViewById(R.id.btnJni1);
btnJni1.setOnClickListener(this);
btnJni2 = (Button) findViewById(R.id.btnJni2);
btnJni2.setOnClickListener(this);
btnJni3 = (Button) findViewById(R.id.btnJni3);
btnJni3.setOnClickListener(this);
tvShowInfo = (TextView) findViewById(R.id.tvShowInfo);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btnJni1:
tvShowInfo.setText(String.format("【静态调用Jni】获取String,输出为:\n %s", jniUtil.getVipString()));
break;
case R.id.btnJni2:
tvShowInfo.setText(String.format("【静态调用Jni】输入vip,加密输出为:\n %s", jniUtil.staticGenerateKey("vip")));
break;
case R.id.btnJni3:
tvShowInfo.setText(String.format("【动态调用Jni】输入test,加密输出为:\n %s", jniUtil.dynamicGenerateKey("test")));
break;
default:
break;
}
}
}
// NDKUtils.java
public class NDKUtils {
static {
System.loadLibrary("zzyJni");
}
public native String getVipString();
public native String staticGenerateKey(String name);
public native String dynamicGenerateKey(String name);
}
这次的编译没有使用windows下的工具Cygwin64
而是直接在Linux系统下使用ndk-build工具生成对应的动态库(/libs/armeabi/libzzyJni.so)
直接使用ndk-build脚本编译出动态库也是非常简单的 在Linux宿主机上不需要安装和配置
只需要把android-ndk-r14b-linux-x86_64.zip解压到机器上 通过export导入路径到系统配置下就能直接使用
操作方法:
1 下载Linux版本的android-ndk-r14b-linux-x86_64.zip (我的Linux设备为Ubuntu64位操作系统)
2 导入ndk目录到系统配置,这里我使用了仅为我当前登录的用户有效
3 到/JniDemo-master/jni/目录下直接执行ndk-build脚本生成libs/和obj/目录以及动态库
4 拷贝动态库libzzyJni.so到eclipse对应的/libs/armeabi/libzzyJni.so
5 编译安卓应用程序到手机
NDK脚本的导入方法:
android-ndk-r14b-linux-x86_64/目录下执行两条export导入命令
/android-ndk-r14b$ export ANDROID_NDK=/home/admin/tools/android-ndk-r14b
/android-ndk-r14b$ export PATH=$PATH:$ANDROID_NDK
在/JniDemo-master目录下执行ndk-build脚本生成obj/和libs/libzzyJni.so动态库
/JniDemo-master$ ndk-build
还在使用古老的eclipse 是因为已经有一段时间没有做安卓项目 所以没有使用Android studio
如果使用AS开发的话 就不需要手动编写 Android.mk 等编译的脚本文件
这里关于编译脚本中指定使用哪种架构的CPU还做了区分armeabi或者armeabi-v7a 或者其他类型
在Application.mk中可以看到我已经指定编译的CPU类型为 armeabi
APP_PLATFORM = android-23
APP_ABI := armeabi
APP_STL := stlport_static
APP_OPTIM := debug
就做个简单介绍
Android 设备的CPU类型(通常称为”ABIs”)
armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年15月以后的生产的大部分Android设备都使用它.
arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多。
x86: 平板、模拟器用得比较多。
x86_64: 64位的平板。
最后关于查看库中存在哪些函数方法:
使用nm命令失败的话 nm: no symbols
可以使用
$ objdump -tT libxsfJni.so
运行在JM虚拟机上的Java代码是怎么样找到对应的本地方法的
这里有两种方法可以找到
1 C/C++代码的函数名以包名加上函数声明的形式让Java代码找到 属于静态方式
2 C/C++通过JNIEnv中的RegisterNatives方法注册函数名 属于动态方式
动态注册方式:
static JNINativeMethod methods[] = {
{"dynamicGenerateKey", "(Ljava/lang/String;)Ljava/lang/String;", (void *) native_dynamic_key},
};
static int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods, int numMethods) {
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { return JNI_FALSE; } return JNI_TRUE; } static int registerNatives(JNIEnv *env) { const char *className = "demo/jnidemo/NDKUtils"; //指定注册的类 return registerNativeMethods(env, className, methods, sizeof(methods) / sizeof(methods[0])); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { return -1; } assert(env != NULL); if (!registerNatives(env)){ return -1; } return JNI_VERSION_1_4; }
JNI_OnLoad函数作为JNI加载的入口,直接在这里进行函数的动态注册