为了完成标题,先看几个概念:
- JNI: Java Native Interface,Java提供,用来实现Java和C的互相调用,但是使Java丧失了跨平台的特性.
- NDK: Native Development Kit,Android提供,基于JNI,跨平台.
- 编译: CPU最常见的即ARM和x86,交叉编译就是在一个平台上编译出另一个平台可用的代码, NDK就是交叉编译工具.
- 为何使用NDK,抄过来以下几点:
- 方便跨平台.
- 算法复杂或资源消耗大的模块.
- 可以避免核心代码被反编译.
下面主要讲如何在MAC OS X 上使用NDK
- 安装AndroidStudio.
- 安装NDK,官网下载,解压
chmod a+x xx.bin
./xx.bin
-
新建一个空白工程,具体内容请参考:Building Your First App
-
使用Project视图,在项目主目录右键新建一个名为
method
的模块,New - Module - Android Library - Add No Activity - Finish
. -
在app模块的
MainActivity.java
添加native方法public class MainActivity extends AppCompatActivity { public native int add(int a, int b);
此处的add对应后面c文件中的方法(注意对象类型查看末尾链接)和h头文件的自动生成.
-
生成头文件
method模块根目录新建一个
autojavah.sh
用来生成.h
头文件 :#!/usr/bin/env bash export ProjectPath=$(cd "../$(dirname "$1")"; pwd) export TargetClassName="com.yanze.ajnidemo.MainActivity" export SourceFile="${ProjectPath}/app/src/main/java" export TargetPath="${ProjectPath}/method/src/main/jni" cd "${SourceFile}" javah -d ${TargetPath} -classpath "${SourceFile}" "${TargetClassName}" echo -d ${TargetPath} -classpath "${SourceFile}" "${TargetClassName}"
前四行定义,后两行执行,最后echo一下,照着自己改,注意一下
TargetPath
这个. 创建完成后会提示安装Bash Plugin
,安装后重启AS,右键Run(或者去目录自己运行).生成的
.h
头文件:/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_yanze_ajnidemo_MainActivity */ #ifndef _Included_com_yanze_ajnidemo_MainActivity #define _Included_com_yanze_ajnidemo_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_yanze_ajnidemo_MainActivity * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_yanze_ajnidemo_MainActivity_add (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif
根据app模块
MainActivity.java
中的add方法对应生成了一个add方法.注意对象类型(jint)和命名规则. -
add.c文件:
// Created by Kyle on 15/11/19. #include "com_yanze_ajnidemo_MainActivity.h" #include <stdio.h> JNIEXPORT jint JNICALL Java_com_yanze_ajnidemo_MainActivity_add(JNIEnv * env,jobject thiz, jint a, jint b){ int c = a + b; return c; }
注意c++和c写法略有不同.
-
编译
-
配置NDK环境,在根目录"local.properties"文件末尾添加NDK的目录,我的是:
ndk.dir=/Users/kyle/workspace/ndk/android-ndk-r10e
-
在"method"模块下的"gradle.properties"末尾添加
android.useDeprecatedNdk=true
. -
编译:右键
method
模块,make module
,成功后会生成libmethod.so(/Ajnidemo/method/build/intermediates/ndk/release/lib/各平台/libmethod.so)
和Android.mk(/Ajnidemo/method/build/intermediates/ndk/release/Android.mk)
两个文件.libmethod.so引用时名字是"method",Android.mk决定了编译时的配置(详细说明见引用链接).
-
给app模块添加method依赖.
右键app模块 - Open Module Setting - Dependencies - + - Module Dependency - method
.
-
-
加载库文件.app模块的MainActivity类下:
static{ System.loadLibrary("method"); }
调用add方法(第二行和第三行),写在onCreate下,以便启动时显示.
TextView tv = new TextView(this); MainActivity t = new MainActivity(); int c = t.add(1024,233); String s = Integer.toString(c); tv.setText(s); setContentView(tv);
嗯,最后是丑陋不堪的效果图:
虽然工具不同,方式不一,蛋整体的思路是一样的,具体操作可以参照第二个链接,不同的地方主要是上面第6条,另外NDK的Samples目录下也有一下Sample.