Android NDK 开发之(二) 牛刀小试

本文介绍了如何在Android应用中使用NDK进行JNI开发,包括NDK、Android SDK、Eclipse和ADT的安装,Eclipse项目的创建,JNI函数的声明和实现,以及Eclipse的配置,如头文件和SO库的自动编译。
摘要由CSDN通过智能技术生成

上一篇中讲的是怎么样在PC上实现简单的JNI调用, 这一篇介绍怎么样在Android应用中完成同样的功能。

因为PC与手机上的处理器是不一样的,所以不能直接使用电脑上的gcc和对应的库文件进行编译,而是使用对应的sdk,也就是 NDK进行开发。

NDK + Android SDK + Eclipse + ADT 安装

这个就不用多说了, 网上教程一找一大把。 先安装好Android SDK, 然后是Eclipse,在里面配置好Android SDK, 然后下载安装ADT。 NDK的安装的话,下载解压,然后把对应的路径添加到系统路径里面就OK了。 具体的说,就是在~/.bashrc 里面添加如下一句:
export PATH=$PATH:you/ndk/install/dir
然后,启用新配置的.bashrc, 
. ~/.bashrc
输入 ndk-build, 如下说明安装完毕:
viking@own:~/documents/project/csdn$ ndk-build 
Android NDK: Could not find application project directory !    
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.    
/home/viking/bin/android/android-ndk-r8c/build/core/build-local.mk:130: *** Android NDK: Aborting    .  Stop.


Eclipse 项目创建

创建一个Android Application Project,下面是命名页面,后面的一路默认。

接下来,我要做的就是启动程序的时候显示一个对话框,对话框上的文本是调用一个native函数 getMsg() 得到的。具体的操作过程的话,和前一篇差不多,只不过,不是使用gcc编译,而是使用 NDK 里面的工具进行编译。 OK, let's go!!!

对话框显示

so easy, 直接修改 MainActivity.java 中的 Oncreate 函数,修改如下:
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);     
        
        // create a alert dialog and show it
        AlertDialog alertDialog = new AlertDialog.Builder(this).create();
        alertDialog.setTitle("title");
        alertDialog.setMessage("msg");
        
        alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
			}	
		});
        
        alertDialog.show();
    }
运行结果如下:

那么,接下来要做的就很明显啦,就是修改这个对话框的message, 用来测试 jni 函数调用。


JNI函数登台

和第一篇一样,先声明这个native函数,修改Mainctivity.java 如下:
public class MainActivity extends Activity {
	private native String getMsg();

        static {
            System.loadLibrary("JniTest");
        }

        /* some other things */

        alertDialog.setTitle("title");
        alertDialog.setMessage( getMsg() );    

        /* some other things */
修改的地方有三处,添加了 getMsg() 的声明以及对它的调用,另外就是加载动态链接库 libJniTest。
这个时候整个程序是不能运行的,因为没有 getMsg 这个函数的实现。接下来的工作就是实现这个函数啦。

原始方法

最原始的方法,就是按照第一篇中说到的来操作,切换到 ManActivity 这个类的目录下面,然后调用 javah 就OK了。不过, 注意这个类名不是简单的 MainActivity,而是 com.viking.JniTest.MainActivity, 所以,切换目录到 bin/classes 下面,输入:
viking@own:~/documents/project/workspace/JniTest/bin/classes$ javah com.viking.JniTest.MainActivity
就会生成一个对应的头文件, com_viking_JniTest_MainActivity.h, 内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_viking_JniTest_MainActivity */

#ifndef _Included_com_viking_JniTest_MainActivity
#define _Included_com_viking_JniTest_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_viking_JniTest_MainActivity
 * Method:    getMsg
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_viking_JniTest_MainActivity_getMsg
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
接下来,只需要实现里面的函数并编译到 libJniTest.so 就OK了。

JNI 函数实现

就我现在所知道的,NDK 的默认编辑路径是在 jni 目录下面,好像有方法可以修改, 不过,先按照默认得来。在项目根目录下面添加一个 jni目录,将上面得到的 com_viking_JniTest_MainActivity.h 移到这个目录下面, 并添加一个 jniTest.c 文件,用来实现这个函数。内容如下:
#include <jni.h>

#include "com_viking_JniTest_MainActivity.h"

JNIEXPORT jstring JNICALL Java_com_viking_JniTest_MainActivity_getMsg
  (JNIEnv * env, jobject obj)
{
	return (*env)->NewStringUTF(env, "Hello, JNI for Android!");
}
此外,ndk 使用编辑配置文件为 Android.mk, 类似于 Makefile 文件,也许要手动创建并放在jni目录下面,内容如下:
LOCAL_PATH := $(call my-dir)

LOCAL_MODULE    := JniTest
LOCAL_SRC_FILES := jniTest.c 

include $(BUILD_SHARED_LIBRARY)
其中,第一行指定路径为 jni目录,第二行为编译输出文件名字(不需要全称 libJniTest.so ),第三行为源文件,最后一行指定编译的是动态链接库。具体的意义以及使用可以参考NDK安装目录下的 docs/ANDROID-MK.html, 也可以参考 安装目录下的 documentation.html, 这个是全部的文档索引页面。 都是用浏览器打开即可。
注意,这里定义的变量名称一定不能出错,我之前就是因为把 LOCAL_SRC_FILES 写成了 LOCAL_SRC, 结果编译也不报错,但是总是出现 java.lang.UnstatisfiedLinkError, 倒腾了半天才发现,伤心。。。

最后,在jni目录下面,输入
viking@own:~/documents/project/workspace/JniTest/jni$ ndk-build 
/home/viking/bin/android/android-ndk-r8c/build/core/add-application.mk:128: Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in /home/viking/documents/project/workspace/JniTest/AndroidManifest.xml    
Compile thumb  : JniTest <= jniTest.c
SharedLibrary  : libJniTest.so
Install        : libJniTest.so => libs/armeabi/libJniTest.so
表示编译成功, 然后可以 Ctrl+F11 运行程序啦,下面是最后的结果:

说明一切OK!!


Eclipse配置

前面虽然已经搞定了最简单的测试,但是,总是需要在终端里面输入命令,然后回到eclipse里面修改代码,很不爽,实际上可以直接在Eclipse完成所有的一切,而且,可以像java代码一样实现自动编译(前提是自动编译已经打开)。接下来一一搞定。

头文件的自动编译

每次我们修改了MainActivity.java 之后,可能都需要重新编译得到 native code对应的头文件,下面就来设置实现。
Project -> Properties -> Builder 菜单, 选择 New, 然后选择 Program, 就可以看到如下对话框:

首先是Main页面
  • Location: 这个就是指编译程序的路径,我们要是用javah来生成头文件,所有是 /usr/bin/javah
  • Work Directory: 工作目录,相当与终端下面切换到这个目录然后执行编译命令
  • Arguments: 编译参数
    • -classpath,指的是类所在的路径,注意不要写错了。 
    • com.viking.JniTest, 这个是类名
    • -d, 结果输出目录,实际上,如果上面的Work Directory 设置成 jni目录的话,这个可以省略掉,不过,加上是最好的啦
接下来是 Refresh 页面,这个页面设置的就是是否在完成编译以后刷新项目,以及怎么刷新(整个项目,指定的目录)。刷新的效果有很多,一个就是如果生成了新的文件,那么项目资源浏览器(也就是Package Explorer选项卡)里面被刷新的目录会更新, 另一个就是可能会 触发其他Builder 的动作, 比如,我们生成了头文件,那么很可能意味着需要重新编译 so 文件,如果 so 对应的Builder设置合理的话(也就是Builde Options页面设置,下面会讲到), 那么就会自动调用。 现在,我们选择第一个,也就是刷新整个工作目录。


最后,也是比较关键的一个页面,就是Build Options 页面,这个页面决定了什么时候调用我们所定义的Builder, 把During Auto builds选上,然后修改那个 Specify working set of relevant resources, 点击那个 Specify Resoures按钮。 这个就是说,指定项目中的某些内容,一旦这些内容发生改变,那么就调用当前的 Builder。 显然,对于NDK Header Builder 来说,一旦我们修改了 MainActivity.java, 那么很有可能需要重新生成 so 对应的头文件, 所以把 MainActivity.java 选上。另外,为了避免删掉了那个头文件,导致后面的so编译失败,把它也选上,这样只要我们删掉了这个头文件,就会自动调用 NDK Header Builder来生成它。 然后点击OK。

现在,我们不妨测试一下这个Builder,先删除 jni下面的头文件,然后修改一下 MainActivity.java, Ctrl+S保存, 接下来,看 Console标签页,如下:


说明这个Build已经自动调用了。再说一句,前提是自动编译已经打开。

so 文件的自动编译

这里的设置和前面大同小异啦


设置好 ndk-builder 的路径。
设置Refresh为刷新整个工作目录。
然后修改Build Options, During Auto builds 勾上,Specify  Resources里面把输入文件 jni目录和输出文件 libs/armeabi/libJniTest.so 选上。

最后,如果两个Builder之间有依赖关系,比如这里的NDK Builder必须在 NDK Header Builder生成头文件之后方可以正确工作,所以要把 NDK Header Builder 放在 NDK Builder前面,至此一切OK。


当然,有利就有弊,比如我们刚开始生成头文件,但是 Android.mk 和 c文件还没有创建,那么这个时候的 NDK Buidler也会自动调用,当然,编译是会报错的。

接下来,可能是关于第三方库的使用。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值