您的位置:首页 > 其它

JNI方法的静态注册和动态注册RegisterNatives

2017-11-25 10:37 447 查看
代码结构如下:

Android下的Java代码 仍然还是使用了eclipse开发环境 这里在NDKUtils.java文件里定义了所有的本地方法



Linux下的C代码 这里是在Linux系统目录下新建的文件 其中libs/和obj/都是由ndk-build编译后编译器产生



点击查看完整项目代码JniDemo-master

// 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


或者写成脚本

#Android NDK export_ndk.sh
export ANDROID_NDK=/home/admin/tools/android-ndk-r14b
export PATH=$PATH:$ANDROID_NDK


执行脚本 source export_ndk.sh

这里不是执行 sh export_ndk.sh

当在一个终端shell中执行一个sh脚本,本质是创建一个子进程来执行sh脚本中的命令,当执行export时,变量加入子进程的环境变量(而非父进程shell). 当脚本执行结束,子进程销毁. 返回父进程,父进程的环境变量没有任何变化

若希望嵌入linux启动时加入的环境变量, 可以直接将export的环境变量添加到/etc/profile

若希望在ubuntu上为特定用户添加环境变量,可以在~/.bashrc文件中添加

在/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 libzzyJni.so


运行在JM虚拟机上的Java代码是怎么样找到对应的本地方法的

这里有两种方法可以找到

1 C/C++代码的函数名以包名加上函数声明的形式让Java代码找到 属于静态方式

2 C/C++通过JNIEnv中的RegisterNatives方法注册函数名 属于动态方式

动态注册方式:

#include <jni.h>

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加载的入口,直接在这里进行函数的动态注册

点击查看完整项目代码JniDemo-master
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: