本篇文章为示范如何在Eclipse中开始一个Jni工程的操作步骤,并假设读者初步了解JNI具体规范及使用ANT脚本。(话说这样的初学者不多见吧…其实我只是想给自己写个总结而已)
阅读本文之前,请确保你的开发环境已经满足如下配置:
1. 设置好了Android Sdk及Ndk环境变量。
NDK的安装建议使用MinGW,只要能在命令行中正常运行make gcc程序即是成功了。并在EclipseàPreferencesàNDK填写NDK的本地路径。
2. 支持Ant脚本执行。
3. Eclipse中安装了CDT插件,可以直接编写C/C++代码。
编写Jni时改动C/C++代码,要执行编译并安装时经过的步骤为:
1. 执行ndk-build
2. Eclipse中启动编译apk并安装
但是我是个懒汉,在想只要进行一个操作就能安装开始调试了。所以为了更加方便,下面演示创建并编写Jni代码的步骤,在普通步骤结束后,会讲述如何使用一句命令行即可编译jni代码并打包生成apk,最后自动启动APK的Ant脚本。
创建工程TestJni,工程结构如下图。
对应的MainActivity.java的代码如下:
public class MainActivity extends Activity {
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextViewtxt = (TextView)findViewById(R.id.txtJni);
txt.setText(hello());
}
public native Stringhello();
static{
System.loadLibrary("TestJni");
}
}
接下来处理Jni的事情。
右击工程”TestJni”à” Android Tools” à” Add Native Support…”,在弹出的输入”Add Android Native Support”对话框中输入最终生成的so文件名为“TestJni”如下图。
这时工程结构也会新增加”jni”文件夹并自动生成了TestJni.cpp及Android.mk文件。即Eclipse默认生成C++文件,这与使用javah命令去生成的C头文件是不同的。如下图。
这时来个永恒的”HelloWorld”示例吧。TestJni.cpp文件中的C++代码如下:
#include <string.h>
#include <jni.h>
#ifdef__cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_lab_sodino_testjni_MainActivity_hello(JNIEnv * env, jobject thiz) {
returnenv->NewStringUTF("HelloWorld!A string from JNI.");
}
#ifdef__cplusplus
}
#endif
代码中extern “C”{}的作用是C++编译的时候,会对函数名进行修饰以用于实现函数充载,而C里面没有这个,所以需要用extern “C”在对头文件进行声明的时候加以区分。这个用于链接的时候进行函数名查找。如果没有此句代码,编译出so文件并打包运行后,会导致java.lang.UnsatisfiedLinkError:Native method not found异常。
此时,在工程目录中执行命令行ndk-build,即生成so文件。工程结构变化如下图:然后在Eclipse中点击F11,即可自动完成打包及安装到手机或模拟器的工作。运行后效果图如下:
下面要讲述的是使用Ant脚本实现方便编译ndk-build并打包APK及安装到手机的实现方法。
在命令行模式下进入到该工程的目录中,并执行如下命令以生成build.xml脚本。
android updateproject -p . –s
这时工程目录结构发生了变化,新生成了build.xml及local.properties文件如下图。
已经生成的build.xml文件,是支持Ant全编译Android工程的,尝试着在命令行中输入ant debug即可打包生成APK。
ant debug
到了这一步即验证当前的开发环境是支持Ant脚本的。
接下来处理ANT脚本的改动。首先先编译local.properties。内容如下:
### 这个文件用来声明键对<key,value>
### 在build.xml中通过<property file="local.properties" />加载到ant中作为常量
sdk.dir=D:\\Mission\\adt-bundle-windows\\sdk
### 定义apk的包名
app_package=lab.sodino.testjni
### 定义apk的启动入口Activity
app_launch_activity=lab.sodino.testjni.MainActivity
最后在build.xml中”<importfile=${sdk.dir}/tools/ant/build.xml”/>中增加如下Ant脚本内容:
<import file="${sdk.dir}/tools/ant/build.xml" />
<!-- ==================以下为新增加的内容================== -->
<target name="easy">
<!-- 编译生成最新的so文件 -->
<antcall target="ndk_build"/>
<!-- 编译apk工程 -->
<antcall target="debug"/>
<!-- 执行安装 -->
<antcall target="install" />
<!-- 启动主界面 -->
<antcall target="startActivity" />
</target>
<!-- target<description>可通过ant -p命令查看 -->
<target name="ndk_build"
description="Compile c/c++ code to so file.">
<exec executable="ndk-build.cmd"/>
<echo message="out.final.file----${out.final.file}"></echo>
</target>
<target name="install"
description="install debug.apk">
<!-- target:debug调用过后,out.filal.file才会作为常量被初始化。所以单独调用install是无效的 -->
<echo message="adb install -r ${out.absolute.dir}/${ant.project.name}-debug.apk"/>
<apply executable="${sdk.dir}/platform-tools/adb.exe" failonerror="true">
<arg value="install" />
<arg value="-r" />
<file file="${out.absolute.dir}/${ant.project.name}-debug.apk"/>
</apply>
</target>
<target name="startActivity"
description="Start Activity: app_package/app_launch_activity">
<echo message="adb shell am start -n ${app_package}/${app_launch_activity}"/>
<exec executable="adb">
<arg value="shell"/>
<arg value="am"/>
<arg value="start"/>
<arg value="-n"/>
<arg value="${app_package}/${app_launch_activity}"/>
</exec>
</target>
代码具体含义可以看注释。
致此,修改TestJni.cpp代码后,只需在命令行下当前工程路径下输入
ant easy
即可实现ndk-build+ 打包 + 安装 + 启动4个动作。如下图。
/**
* @author sodino
* @mail: sodino@qq.com
* 转载请保留出处。
*/