开发步骤
- 确认eclipse安装了CDT , eclipse中查看:Hlep--->Install New Software ---> 点击右下角already installed --->Plug-ins ---> 查看CDT是否安装* 新建Android项目* 在MainActivity.java文件中声明本地方法
public native String helloFromC();
- 在项目根目录下新建jni的Folder
- 在jni目录下新建hello.c
- 在hello.c文件写与java文件中对应的native方法的函数,c中本地函数命名规则:Java_包名(
.
需要换成_
)类名本地方法名,函数中有两个参数:
1,jobject obj:调用本地函数的Java对象,在本例中就是MainActivity实例
2,JNIEnv* env:是结构体JNINativeInterface 的二级指针,JNIEnv是结构体 JNINativeInterface 的一级指针,JNINativeInterface结构体中定义了大量的函数指针,这些函数指针在JNI开发中会经常使用
3,(*env)->:调用结构体中的函数指针
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
jstring Java_com_huachao_jnihelloworld_MainActivity_helloFromC(JNIEnv* env , jobject thiz){
char* cstr = "hello from c";
return (*env)->NewStringUTF(env,cstr);
}
- 配置ndk环境变量(path中添加ndk路径)
- 在命令行cd到项目目录下:F:\as\Eclipse\personal\JNI\01-JNIHelloWorld>
- 执行ndk-build命令,提示
Android NDK: WARNING: APP_PLATFORM android-21 is larger than android:minSdkVers
on 8 in ./AndroidManifest.xml
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk
- 在jni目录下,新建Android.mk文件
LOCAL_PATH := $(call my-dir)//设置为当前目录
include $(CLEAR_VARS) //清空变量
LOCAL_MODULE := hello//编译生成的文件名称
LOCAL_SRC_FILES := hello.c//.c源文件名称
include $(BUILD_SHARED_LIBRARY)
注意:
1,LOCAL_MODULE := hello,编译后,编译器会给文件添加lib
前缀和.so
后缀,例如本例生成libhello.so
2,若新建的文件夹名不是jni,或者修改了jni的文件夹名称,则在ndk-build的时候,不能在项目目录下build,要cd到这个目录下,再执行ndk-build
- 再次在命令行中在项目目录下执行ndk-build命令,提示Install,生成.so文件,表示成功
- 在MainActivity.java中调用
public class MainActivity extends Activity {
static{
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public native String helloFromC();
public void onClick(View v) {
String c = helloFromC();
Toast.makeText(this, c, Toast.LENGTH_LONG).show();
}
}
开发中常见错误
方法名
- native方法名不能包含
_
,因为c语言的函数命名规范也是使用的_
,会产生冲突,最后会报Native method not found
错误 - 如果本地方法一定要有
_
,那么c函数_
后加1区分,例如:
public native String hello_FromC();
jstring Java_com_huachao_jnihelloworld_MainActivity_hello_1FromC(JNIEnv* env , jobject thiz){
}
- 如果native名字太复杂,下划线太多,可以使用javah命令自动生成c的函数名
1,如果是jdk1.6,cd到:项目根目录\bin\classes>
2,如果是1.7及以上,cd到:项目根目录\src>
3,执行命令:javah -jni 包名.类名
在本例中如下:
F:\as\Eclipse\personal\JNI\01-JNIHelloWorld\src>javah -jni com.huachao.jnihelloworld.MainActivity
- 如果没有
System.loadLibrary("hello");
,也会报Native method not found
错误 - 注意:System.loadLibrary("hello");加载的库名称是
Android.mk
中LOCAL_MODULE := hello的名称,而不是生成的.so
文件的名称
Application.mk配置
多平台
- 如果Application.mk没有文件,或者在Application.mk文件中不指定APP_ABI,在NDK默认只编译
armeabi
的平台的库 - 在jni目录下新建Application.mk文件
1, APP_ABI := all //所有平台
2, APP_ABI := armeabi-v7a
3, APP_ABI := x86
4, APP_ABI := mips
5, APP_ABI := armeabi armeabi-v7a x86 mips //也是所有平台
SDK版本配置
- 在Application.mk添加
APP_PLATFORM := android-11
- 版本对应
android-3 -> Official Android 1.5 system imagesandroid-4 -> Official Android 1.6 system imagesandroid-5 -> Official Android 2.0 system imagesandroid-6 -> Official Android 2.0.1 system imagesandroid-7 -> Official Android 2.1 system imagesandroid-8 -> Official Android 2.2 system imagesandroid-9 -> Official Android 2.3 system imagesandroid-14 -> Official Android 4.0 system imagesandroid-18 -> Official Android 4.3 system images
简单开发流程
- 新建Android项目
- 如上所述,在MainActivity中添加native方法
- 指定项目的NDK目录:Eclipse视图中Window --> Preferences -->Android --> NDK --> 选择NDK目录(本例中目录
F:\as\plugin\android-ndk-r9d
) --> 点击 apply,然后点击OK - 在Eclipse视图中,在项目目录上右键点击-->Android Tools -->Add Native Support --> 输入自定义名称(本例中
hello
) --> 点击Finish
注意:
1,如果Finish点不了,那么可能是上一个步骤没做
2,如果Android Tools中没有Add Native Support这个菜单:将资料中的
com.android.ide.eclipse.ndk_23.0.2.1259578.jar
文件拷贝到eclipse的安装目录的plugin目录下,然后重启Eclipse - 将hello.cpp重命名为hello.c,并在
Android.mk
文件中做相应修改 - 用javah命令生成头文件
- 在项目目录上右键点击 --> Properties -->C/C++ General --> Paths and Symbols --> 选中Includes选项卡 --> Add --> 添加NDK的include目录(本例
F:\as\plugin\android-ndk-r9d\platforms\android-14\arch-arm\usr\include
)--> 点击Apply(会提示是否rebuild,点击YES) --> 点击OK
1, platforms的版本最好选择Application.mk设置的最低版本
2,
-
编译
1,切换到C/C++视图
2,点击那个类似锤子的按钮,进行编译,
- 在Java中调用和上面一样
- 提示:运行项目最好切换到Java视图,不然可能会报错
注意:在eclipse的jni开发中,包名不能包含下划线,否则在运行时运行不了,但是却不报错
在JNI开发中,指定NDK目录,是更换workspace的时候才要做,其他的都是每次新建项目都是要做的
- 设置编译时不编译C/C++代码,也不生成
.so
库(当so库已经生成完毕时,只是修改了java代码可以这样设置)
取消上面两个红色箭头的勾选
绕过JNI调用C代码
广泛使用的地方:
1,自定制平板,机顶盒
2,Android手机病毒
3,黑客操作纯c语言开发程序 ,步骤如下:
1.下载编译器和链接器软件.Sourcery G++ Lite Edition for ARM.
arm-none-linux-gnueabi-gcc.exe是编译命令
bin/arm-none-linux-gnueabi-ld.exe是链接命令
- 2.编写c源文件
#include <stdio.h>
int main()
{
printf("Hello, Android!\n");
return 0;
}
3.编译hello.c源文件
进入cmd
执行 arm-none-linux-gnueabi-gcc HelloWorld.c -static -o hellostatic4.将hellostatic文件传输手机
adb push hellostatic /data/data/5.改变文件的授权
adb shell chmod 777 /data/data/hellostatic6.运行程序
adb shell
cd /data/data
./hellostatic-
上面的实质就是把
.c
文件编译成静态库,然后在命令行执行,其实系统里面已经有很多这种可执行的文件
- 在java中执行可执行文件
public void binExce(View v) {//点击事件
try {
// Process process = Runtime.getRuntime().exec("/data/data/hello");//hello是我们自己编译并且放入的库文件
Process process = Runtime.getRuntime().exec("ps");
InputStream is = process.getInputStream();
DataInputStream dis = new DataInputStream(is);
String res ;
StringBuilder sb = new StringBuilder();
while ((res = dis.readLine()) !=null) {
sb.append(res);
sb.append("\n");
}
System.out.println("--->"+sb.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
注意:上述步骤只在Android 4.x下测试生效,高版本没有测试