android编译jni
㈠ android studio JNI开发时 编译成功 但是没有生成.so文件 什么原因
1
在交叉编译的时候怎么都无法生成so文件,javah生成头文件没错,c文件也没错,java文件也没错,
2.原因:是JNI文件夹路径不对
3
在执行javah命令时,我进入的是cd
app/src/main/java
这样jni文件夹在java文件夹下,作为一个包存在,这样就无法生成so文件
执行javah的正确姿势:
4
进入app/src/main目录:cd
app/src/main
执行javah命令:javah
javah
-d
jni
-classpath
./Java
lab.sodino.jnitest.MainActivity
5,
-d
jni
头文件生成到jni文件夹(当前在<Project>\app\src\main目录下,所以.h所在的目录为<Project>\app\src\main\jni
)
-classpath
./java
指定去当前路径下java下寻找包名指定的类
这样再rebuild一下,就会生成so文件了
㈡ android studio 怎么编译jni
一、使用已经编译好的so
这种情况比较件简单,只要把的.so文件放到相应的目录即可。如下:
.[mole_name]
. . [src]
. . .[main]
. . . .[jniLibs]
. . . . .[armeabi]
. . . . .[armeabi-v7a]
. . . . .[x86]
. . . . .[mips]
注意 jniLibs 目录是放在 mole 下面,在Android Studio中效果如下,这样编译之后so就会被自动打包进apk,代码中直接 loadLibrary即可了:
1 String libName = "helloNDK"; // 库名, 注意没有前缀lib和后缀.so
2 System.loadLibrary( libName );
二、使用C/C++源码
1 r9d以上版本NDK
首先确保自己的NDK版本在r9d以上,目前最新可以拿到的是r10,下载地址:
http://tools.android-studio.org/
感谢 Android Studio中文组的无私奉献。
如果低于r9d版本,Android studio 下ndk编译会出现 No rule to make target 的错误。
2 配置 ndk.dir
在 local.properties 添加如下配置:
sdk.dir=/path/to/android-sdk
ndk.dir=/path/to/android-ndk
㈢ 如何在Android下使用JNI
关于如何在Android使用JNI调用C/C++代码库,网上已经有很多优秀的文章了,这里说一个大概过程吧:
首先需要懂C,其次要明白JNI的开发流程,然后还要知道NDK如何使用
1、在java代码中声明了一个native本地方法
Public native String helloFromc();
2、在项目目录中创建JNI文件夹
3、在JNI文件夹里面创建C文件,按照规范写代码
Jstring
Java_com_cheng_jnitest_MainActivity_helloFromc(JNIEnv* env,jobject obj)
4、用ndk-build指令编译
编译前需要配置Android.mk文件
//指定编译的文件夹,指定当前的目录
LOCAL_PATH := $(call my-dir)
//编译器在编译的时候会产生很多临时变量,中间变量最好在编译前清空所有的临时变量
include $(CLEAR_VARS)
//编译完成后的模块名
LOCAL_MOUDLE := hello
//编译的源文件
LOCAL_SRC_FILES:=hello.c
//编译一个动态库
//动态库.so 只包含运行的函数,不包含依赖,所以体积小,运行的时候回去系统寻找依赖
//静态库.a 包含所有的函数和运行的依赖,所以体积大,包含所有的api
include $(BUILD_SHARED_LIBRARY)
5、生成了一个so动态库,放到了libs里面
6、项目中引入依赖库
Static{
System.loadLibrary("hello");
}
㈣ android studio 编译jni 跟工程有关吗
1 在交叉编译的时候怎么都无法生成so文件,javah生成头文件没错,c文件也没错,java文件也没错,
2.原因:是JNI文件夹路径不对
3 在执行javah命令时,我进入的是cd app/src/main/java 这样jni文件夹在java文件夹下,作为一个包存在,这样就无法生成so文件
执行javah的正确姿势:
4 进入app/src/main目录:cd app/src/main
执行javah命令:javah
javah -d jni -classpath ./Java lab.sodino.jnitest.MainActivity
5, -d jni 头文件生成到jni文件夹(当前在<Project>\app\src\main目录下,所以.h所在的目录为<Project>\app\src\main\jni )
-classpath ./java 指定去当前路径下java下寻找包名指定的类
这样再rebuild一下,就会生成so文件了
㈤ 如何在Android下使用JNI
第一步: 使用Java编写HelloWorld 的Android应用程序: 复制代码 package com.lucyfyr; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class HelloWorld extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.v("fresne", printJNI("I am HelloWorld Activity")); } static { //加载库文件 System.loadLibrary("HelloWorldJni"); } //声明原生函数 参数为String类型 返回类型为String private native String printJNI(String inputStr); } 复制代码 这一步我们可以使用eclipse来生成一个App; 因为eclipse会自动为我们编译此Java文件,后面要是用到。 第二步: 生成共享库的头文件: 进入到eclipse生成的Android Project中 :/HelloWorld/bin/classes/com/lucyfyr/ 下: 可以看到里面后很多后缀为.class的文件,就是eclipse为我们自动编译好了的java文件,其中就有: HelloWorld.class文件。 退回到classes一级目录:/HelloWorld/bin/classes/ 执行如下命令: javah com.lucyfyr.HelloWorld 生成文件:com_lucyfyr_HelloWorld.h 复制代码 /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_lucyfyr_HelloWorld */ #ifndef _Included_com_lucyfyr_HelloWorld #define _Included_com_lucyfyr_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: com_lucyfyr_HelloWorld * Method: printJNI * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif 复制代码 可以看到自动生成对应的函数:Java_com_lucyfyr_HelloWorld_printJNI Java_ + 包名(com.lucyfyr) + 类名(HelloWorld) + 接口名(printJNI):必须要按此JNI规范来操作; java虚拟机就可以在com.simon.HelloWorld类调用printJNI接口的时候自动找到这个C实现的Native函数调用。 当然函数名太长,可以在.c文件中通过函数名映射表来实现简化。 第三步: 实现JNI原生函数源文件: 新建com_lucyfyr_HelloWorld.c文件: 复制代码 #include <jni.h> #define LOG_TAG "HelloWorld" #include <utils/Log.h> /* Native interface, it will be call in java code */ JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI(JNIEnv *env, jobject obj,jstring inputStr) { LOGI("fresne Hello World From libhelloworld.so!"); // 从 instring 字符串取得指向字符串 UTF 编码的指针 const char *str = (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE ); LOGI("fresne--->%s",(const char *)str); // 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。 (*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str ); return (*env)->NewStringUTF(env, "Hello World! I am Native interface"); } /* This function will be call when the library first be load. * You can do some init in the libray. return which version jni it support. */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { void *venv; LOGI("fresne----->JNI_OnLoad!"); if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) { LOGE("fresne--->ERROR: GetEnv failed"); return -1; } return JNI_VERSION_1_4; } 复制代码 OnLoadJava_com_lucyfyr_HelloWorld_printJNI 函数里面做一些log输出 注意JNI中的log输出的不同。 JNI_OnLoad函数JNI规范定义的,当共享库第一次被加载的时候会被回调, 这个函数里面可以进行一些初始化工作,比如注册函数映射表,缓存一些变量等, 最后返回当前环境所支持的JNI环境。本例只是简单的返回当前JNI环境。 第四步: 编译生成so库 编译com_lucyfyr_HelloWorld.c成so库可以和app一起编译,也可以都单独编译。 在当前目录下建立jni文件夹:HelloWorld/jni/ 下建立Android.mk ,并将com_lucyfyr_HelloWorld.c和 com_lucyfyr_HelloWorld.h 拷贝到进去 编写编译生成so库的Android.mk文件: 复制代码 LOCAL_PATH:= $(call my-dir) # 一个完整模块编译 include $(CLEAR_VARS) LOCAL_SRC_FILES:=com_lucyfyr_HelloWorld.c LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) LOCAL_MODULE := libHelloWorldJni LOCAL_SHARED_LIBRARIES := libutils LOCAL_PRELINK_MODULE := false LOCAL_MODULE_TAGS :=optional include $(BUILD_SHARED_LIBRARY) 复制代码 系统变量解析: LOCAL_PATH - 编译时的目录 $(call 目录,目录….) 目录引入操作符 如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径 include $(CLEAR_VARS) -清除之前的一些系统变量 LOCAL_MODULE - 编译生成的目标对象 LOCAL_SRC_FILES - 编译的源文件 LOCAL_C_INCLUDES - 需要包含的头文件目录 LOCAL_SHARED_LIBRARIES - 链接时需要的外部库 LOCAL_PRELINK_MODULE - 是否需要prelink处理 include$(BUILD_SHARED_LIBRARY) - 指明要编译成动态库
㈥ Android用NDK和整套源码下编译JNI的不同
2. 注册函数的方法是不同的。举例说,在com/evan129/jnitest/jniutils.java有个native int foo()方法,需要在jni中实现
在ndk中,只要实现这个函数,然后函数名是以jint java_com_evan129_jnitest_jniutils_foo(jnienv* env, jobject thiz) 命名既可。也就是说,如果jni只要实现这个函数,并且功能也很简单的话,那么jni c/cpp文件里只需要这一个函数就完事了。
但在android源码中编译jni代码是不同的,jni中的函数名无所谓。不过至少还需要加一个
jniexport jint jnicall jni_onload(javavm* vm, void* reserved)方法,这个方法可以找个现有的复制一把就行,检查运行环境的。然后主要是这个方法中会调用(*env).registernatives函数,在这里把jni中的方法和java文件中的方法关联起来。
3. 有个很诡异的区别,自动传入的jnienv* env好像不是一个东西。因为在android源码中使用这个env一般是如env->newstringutf(…),而ndk中sample里的一处是(*env)->newstringutf(…) 这env和*env差很大。但两处函数传入的都是jnienv* env,只能怀疑jnienv的定义是不是都是不同的。
㈦ android jni怎么编译
参考如下
打开Eclipse,选择菜单 "File->New->other...";
选择“Android->Android Project from Existing Code”后,点击 Next;
① 点击"Browse..."按钮,选择"ndk根目录下->samples->hello-jni",如目录是“D:\Android\android-ndk-r9d\samples\hello-jni”;
② 取消“Project:->Project to import->tests”的复选框;
③ 选中“Copy projects into workspace”
④ 然后点击 Finish 完成
① 选择““HelloJni”工程后点击右键->Android Tools->Add Native Support...”;
② 在 "Add Android Native Support" 界面点击 Finish
打开工程文件 “hellojni->jni->hello-jni.c",发现报错:”Method 'NewStringUTF' could not be resolved“;解决方法:
①将 文件名”hello-jni.c“改为”hello-jni.cpp“;
②将文件”hello-jni.cpp“中的
函数 ”Java_com_example_hellojni_HelloJni_stringFromJNI“的最前面加上"extern C"
③将文件”hello-jni.cpp“中的
函数 ”Java_com_example_hellojni_HelloJni_stringFromJNI“
的最后一行的代码:
”return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");“
改为
”return env->NewStringUTF("Hello from JNI ! Compiled with ABI " ABI ".");“
④在工程文件"hellojni->jni->Android.mk”中的
“LOCAL_SRC_FILES := hello-jni.c”改为“LOCAL_SRC_FILES := hello-jni.cpp”
⑤点击工具栏上的按钮 Build All(Ctrl+B),或者使用快捷键"Ctrl+B"
⑥改为后的文件内容如下:
点击工程"hellojni右键->Debug As->Andrid Native Application;结果报错:
“Unable to resolve target 'android-3'”,解决方法:
打开工程文件“hellojni->AndroidManifest.xml”选择”Manifest分页->Manifest Extras->Uses SDK“,修改右边”Atributes for User Sdk“下的“Min SDK Version”为19, “Target SDK Version"为19, (注:19是android4.4.2版,目前最新版),保存;
保存后又发现错误提示”Avoid hardcoding the debug mode;“,解决方法:”打开AndroidManifest.xml文件Application分页“,将"Application Attributes"下的 Debuggable 属性框中的 true 清除掉;
点击工程"hellojni右键->Debug As->Andrid Native Application;运行结构
㈧ Android用NDK和整套源码下编译JNI的不同
NDK:
1、包含头文件
#include <android/log.h>
2、Android.mk中包含库
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
上面这个一定要,不然出现error: undefined reference to '__android_log_print'
LOCAL_SHARED_LIBRARIES := libdl\
liblog\ #经测试在Eclipse中用NDK编译可有可无,没啥用!但在源码中就必须是他,所以都加上吧!
libpre_AppUpgrade\
libpre_AppArea\
3、在你的jni文件中定义
#define LOGD() __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) // 定义LOGD类型
#define LOGI() __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) // 定义LOGI类型
#define LOGW() __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) // 定义LOGW类型
#define LOGE() __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) // 定义LOGE类型
#define LOGF() __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__) // 定义LOGF类型
adnroid4.2源码中已经将LOGD等都加了一个头,
#define ALOGD() __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) // 定义LOGD类型
#define ALOGI() __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) // 定义LOGI类型
#define ALOGW() __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) // 定义LOGW类型
#define ALOGE() __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) // 定义LOGE类型
#define ALOGF() __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__) // 定义LOGF类型
注意如果你不想在每一个jni文件中都定义上述宏,投机方法即可以定义在:D:\android-ndk-r9d\platforms\android-19\arch-arm\usr\include\android\log.h中!当然这种编译也只能在你本机上使用啦~
源码开发:
1、Android.mk中包含库
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
LOCAL_SHARED_LIBRARIES := libdl\
liblog\ //源码中开发一定的加上
libpre_AppUpgrade\
libpre_AppArea\
2、包含头文件#include <utils/Log.h>
3、注意在使用时记得包含库的头文件
转载