前几天研究了JNI技术后,想在Android上试一试研究结果,查阅了很多资料后,总结如下步骤:

首先来看一下什么是NDK

NDK 提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so 和java 应用一起打包成apk。这些工具对开发者的帮助是巨大的。
NDK 集成了交叉编译器,并提供了相应的mk 文件隔离CPU、平台、ABI 等差异,开发人员只需要简单修改mk 文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。NDK 可以自动地将so 和Java 应用一起打包,极大地减轻了开发人员的打包工作。比较简单的说,NDK是一套交叉编译工具,它可以帮你把你用C或C++书写的代码,编译为.so(类似与win下的.dll)格式的文件,使你可以在你的Android程序当中用Java语言(JNI)调用这些代码

下面来看一下具体的操作步骤


第一步:搭建NDK环境


下载Android NDK。下载地址:http://developer.android.com/tools/sdk/ndk/index.html

下载放到指定的目录下,解压即可,这一步很简单的

第二步:下载安装cygwin


由于NDK编译代码时必须要用到make和gcc,所以你必须先搭建一个linux环境,cygwin是一个在windows平台上运行的unix模拟环境,它对于学习unix/linux操作环境,或者从unix到windows的应用程序移植,非常有用。通过它,你就可以在不安装linux的情况下使用NDK来编译C、C++代码了。下面我们一步一步的安装cygwin吧。

下载地址:http://cygwin.com/install.html

1、 然后双击运行吧,运行后你将看到安装向导界面:

2、 点击下一步,此时让你选择安装方式:
1)Install from Internet:直接从Internet上下载并立即安装(安装完成后,下载好的安装文件并不会被删除, 而是仍然被保留,以便下次再安装)。
2)Download Without Installing:只是将安装文件下载到本地,但暂时不安装。
3)Install from Local Directory:不下载安装文件,直接从本地某个含有安装文件的目录进行安装。

3、选择第一项,然后点击下一步:

4、选择要安装的目录,注意,最好不要放到有中文和空格的目录里,似乎会造成安装出问题,其它选项不用变,之后点下一步:

5、上一步是选择安装cygwin的目录,这个是选择你下载的安装包所在的目录,默认是你运行setup.exe的目录,直接点下一步就可以:

6、此时你共有三种连接方式选择:
1) Direct Connection:直接连接。
2) Use IE5 Settings:使用IE的连接参数设置进行连接。
3) Use HTTP/FTP Proxy:使用HTTP或FTP代理服务器进行连接(需要输入服务器地址、端口号)。
根据自己的网络连接的实情情况进行选择,一般正常情况下,均选择第一种,也就是直接连接方式。然后再点击“下一步”,

7、 这是选择要下载的站点,选择后点下一步.

8、 此时会下载加载安装包列表

9、Search是可以输入你要下载的包的名称,能够快速筛选出你要下载的包。那四个单选按钮是选择下边树的样式,默认就行,不用动。View默认是Category,建议改成full显示全部包再查,省的一些包被隐藏掉。左下角那个复选框是是否隐藏过期包,默认打钩,不用管它就行,下边开始下载我们要安装的包吧,为了避免全部下载,这里列出了后面开发NDK用得着的包:autoconf2.1、automake1.10、binutils、gcc-core、gcc-g++、gcc4-core、gcc4-g++、gdb、pcre、pcre-devel、gawk、make共12个包

10、然后开始选择安装这些包吧,点skip,把它变成数字版本格式,要确保Bin项变成叉号,而Src项是源码,这个就没必要选了。

11、下面测试一下cygwin是不是已经安装好了。
运行cygwin,在弹出的命令行窗口输入:cygcheck -c cygwin命令,会打印出当前cygwin的版本和运行状态,如果status是ok的话,则cygwin运行正常。
然后依次输入gcc --version,g++ --version,make –version,gdb –version进行测试,如果都打印出版本信息和一些描述信息,非常高兴的告诉你,你的cygwin安装完成了.

第三步:配置NDK环境变量


1、首先找到cygwin的安装目录,找到一个home\<你的用户名>\.bash_profile文件,我的是:c:\cygwin\home\Administrator\.bash_profile

2、打开bash_profile文件,添加NDK=/cygdrive/<你的盘符>/<android ndk 目录> ,就是第一步中下载下来的NDK解压的目录我的目录是:

例如:
NDK=/cygdrive/e/android-ndk-r9c
export NDKRoot

(NDKRoot这个名字是随便取的,为了方面以后使用方便,选个简短的名字,然后保存)

(这里还要注意的是盘符是"/"而不是"\",因为cygwind是Linux,不是windows的)

3、打开cygwin,输入cd $NDK,如果输出上面配置的/cygdrive/e/android-ndk-r9c信息,则表明环境变量设置成功了。

第四步:用NDK来编译程序,测试功能是否可用


1、现在我们用安装好的NDK来编译一个简单的程序吧,我们选择ndk自带的例子hello-jni,我的位于E:\android-ndk-r9c\samples\hello-jni(根据你具体的安装位置而定).

2、运行cygwin,输入命令cd /cygdrive/e/android-ndk-r9c/samples/hello-jni,进入到E:\android-ndk-r9c\samples\hello-jni目录。

3、 输入$NDK/ndk-build,执行成功后,它会自动生成一个libs目录,把编译生成的.so文件放在里面。($NDK是调用我们之前配置好的环境变量,ndk-build是调用ndk的编译程序)

(早期NDK版本是make APP=hello-jni ,还要对应app和source2个目录的项目目录,现在改成了$NDK/ndk-build)

第五步:开发一个简单的Android NDK例子


在Eclipse中新建一个AndroidNDKDemo工程

其中JNI.java是定义的本地方法的类,代码如下:

package com.ndk.demo;
public class JNI {
//获取字符串
public native String getString();
//进行加法操作
public native int plus(int a,int b);
}

在来看一下AndroidNDKDemoActivity.java,主要是测试的Demo,代码如下:

package com.ndk.demo;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast; import com.ndk.demo.R; public class AndroidNDKDemoActivity extends Activity {
//使用静态代码块加载类库
static{
System.loadLibrary("first_jni");//一定要注意名称没有“lib"
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn1 = (Button)findViewById(R.id.show_btn);
final EditText oneEdit = (EditText)findViewById(R.id.one_number);
final EditText twoEdit = (EditText)findViewById(R.id.two_number);
final Button btn2 = (Button)findViewById(R.id.calculate_btn);
//定义本地类
final JNI jni = new JNI();
btn1.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
//显示从C代码中返回的字符串
Toast.makeText(getApplicationContext(), jni.getString(), 1500).show();
}});
btn2.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
//调用C代码中的加法操作
String oneNumber = oneEdit.getText().toString();
String twoNumber = twoEdit.getText().toString();
int oneNumbers = Integer.valueOf(oneNumber);
int twoNumbers = Integer.valueOf(twoNumber);
btn2.setText(jni.plus(oneNumbers, twoNumbers)+"");
}});
}
}

上面的工作搞定后,就生成c代码的头文件吧!具体操作,这里不再赘述,可以访问我的博客:

http://blog.csdn.net/jiangwei0910410003/article/details/17465085

同时使用VC6.0建一个NDKDemo.c代码,如下:

#include"jni.h"
/*
* Class: com_ndk_demo_JNI
* Method: getString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ndk_demo_JNI_getString(JNIEnv* env, jobject obj)
{
//返回一个字符串
return (*env)->NewStringUTF(env,"Hello NDK!");
} /*
* Class: com_ndk_demo_JNI
* Method: plus
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ndk_demo_JNI_plus(JNIEnv* env, jobject obj, jint a, jint b)
{
//返回计算结果
return a+b;
}

到这一步,离成功就不远了,现在需要将NDKDemo.c编译成.so库,在目录中新建一个文件夹(这个目录是随便建立的),这里我为了方便就在AndroidNDKDemo工程的bin\classes目录中新建一个jni文件夹(这里一定要注意文件夹的名称一定是"jni",不然后面的编译会报错的),下面是我的目录图:

你同样可以在D盘下新建一个jni文件夹,或者其他的目录下都可以的,但是一定要注意文件夹的名称必须是"jni"

然后将生成的C代码的头文件和你刚刚新建的.c文件拷贝到jni文件夹下,(这里的NDKDemo.cpp不要管了,是C++代码文件)

下面接着新建一个Android.mk文件,文件内容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := first_jni (最终so文件名是libfirst_jni.so)
LOCAL_SRC_FILES := NDKDemo.c (C代码)
include $(BUILD_SHARED_LIBRARY)

上面的准备工作都做完了,现在就开始编译吧:

打开cygwin,输入以下命令:

cd /cygdrive/e/workspace/AndroidNDKDemo/bin/classes  

进入到jni文件夹的根目录,这个是我的目录,你们要进入你们刚才新建的jni文件夹的根目录即可

然后输入命令:

$NDK/ndk-build

进行编译

上面说明编译成功,这时候到jni的根目录中可以看到多出两个文件夹,一个是libs,一个是obj,我们这里只关心libs中的文件

进入到lib文件夹下,看到有一个libfirst_jni.so文件,这时候,我们的编译工作全部搞定了,这时候只需要将libfirst_jni.so文件拷贝到AndroidNDKDemo工程中的libs文件夹下,如果libs下没有文件夹armeabi,就新建一个armeabi文件夹,将libfirst_jni.so文件放到armeabi文件夹中,加载库的代码具体可以看上面。

运行结果如下:

可以看到,成功的运行了!

问题

就是我这里面使用了C代码测试的,其实我开始的时候是用C++代码测试的,就是上面的NDKDemo.cpp文件:代码如下:

#include"jni.h"
/*
* Class: com_ndk_demo_JNI
* Method: getString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ndk_demo_JNI_getString(JNIEnv* env, jobject obj)
{
//返回字符串
return env->NewStringUTF("Hello NDK!");
} /*
* Class: com_ndk_demo_JNI
* Method: plus
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ndk_demo_JNI_plus(JNIEnv* env, jobject obj, jint a, jint b)
{
//计算结果
return a+b;
}

这里面就是方法:JNICALL Java_com_ndk_demo_JNI_getString不一样

C代码中是:

return (*env)->NewStringUTF(env,"Hello NDK!");

C++代码是:

return env->NewStringUTF("Hello NDK!");

但是使用C++代码的话,在Eclipse中编译报错,很是纠结,现在还没有解决,如果哪位大神能够告诉我一下,本人将不胜感激!

Demo的下载的地址: 

http://download.csdn.net/detail/jiangwei0910410003/7594551

Android中的第一个NDK的例子的更多相关文章

  1. Android中Service的一个Demo例子

    Android中Service的一个Demo例子  Service组件是Android系统重要的一部分,网上看了代码,很简单,但要想熟练使用还是需要Coding.  本文,主要贴代码,不对Servic ...

  2. 在Android中动画移动一个View的位置,采用Scroller类实现Android动画之 View移动

    在Android中动画移动一个View的位置,采用Scroller类实现 今天说最近自己遇到的一个问题,就是要用动画效果来移动一个VIew的位置. 这个具体的情况是,需要做一个SlidingMenu的 ...

  3. [转]android中drawable资源的解释及例子

    原文链接:         http://blog.csdn.net/wode_dream/article/details/38584693 文章中的内容参考Dev Guide中的Drawable R ...

  4. 浅谈android中只使用一个TextView实现高仿京东,淘宝各种倒计时

    今天给大家带来的是只使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.近期公司一直加班也没来得及时间去整理,今天难得歇息想把这个分享给大家.只求共同学习,以及自己兴许 ...

  5. Android中自己定义一个shade.xml

    自己定义一个shade: <shape> <!-- 实心 --> <solid android:color="#ff9d77"/> <!- ...

  6. session中删除数组中的某一个值 - 购物车例子 - jsp

    这篇随笔简单的讲一下在session中移除数组中的某一项内容,比如这里有一个购物车其中有两件商品,需要移除其中洗发水这一件商品. 其实在这个session对象中存储了一个数组,在订购页面时选择商品加入 ...

  7. Android中图表AChartEngine学习使用与例子

    很多时候项目中我们需要对一些统计数据进行绘制表格,更多直观查看报表分析结果.基本有以下几种方法: 1:可以进行android  api进行draw这样的话,效率比较低 2:使用开源绘表引擎,这样效率比 ...

  8. Android 中如何从一个App启动另外一个App(如启动支付界面、启动地图界面、应用商场下载App等场景)

    假定两个App,分别是A和B,当A运行某个功能需要启动B,一种是启动B应用,一种直接进入B的某个Activity.搜了很多资料,没有一个完整的.下面就A--Android5.1.1.B--Androi ...

  9. Android中如何搭建一个WebServer

    今天终于把老大交代的任务搞完了,感觉收获挺多的,所以就写一篇来记录一下吧,首先还是来看一下,老大们的需求 需求: 希望移动端的用户标识(IMEI)和HTML页面的用户标识(Cookie)连接起来,其中 ...

随机推荐

  1. JavaScript 中 reduce去重方法

    过去有很长一段时间,我一直很难理解 reduce() 这个方法的具体用法,平时也很少用到它.事实上,如果你能真正了解它的话,其实在很多地方我们都可以用得上,那么今天我们就来简单聊聊 JS 中 redu ...

  2. 72.Properties(配置文件)

    Properties(配置文件):主要用于存储配置文件到硬盘上面和读取配置文件 public class Properties extends Hashtable<Object,Object&g ...

  3. 一个比较独特的"HelloWorld"

    为什么说是特殊的helloworld呢给全世界的服务器发送一个请求,这就是我"打招呼"的方式核心内容就是:向所有的ipv4地址发送一个请求即x.x.x.x,如果其是web服务,就拿 ...

  4. 【TCP/IP】TCP的三次握手和四次挥手

    传输控制协议(TCP)是一种面向连接的协议,网络程序使用这个协议的时候,网络可以保证客户端和服务端的连接是可靠的,安全的. 如果 A机向 B机发送“hello”,在物理网线上传输的数据不仅仅是“hel ...

  5. rails调试

    1.debugger() 测试结果来看,在Controller中加入后,可在后台中断调试如下 通常输出 log 是输出到终端(启动 rails server 的终端): # 控制器里 logger.i ...

  6. jQuery Validate (摘自官网)

    jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 URL 和电子邮件验证 ...

  7. windows系统使用

    1.访问局域网共享的文件,用 \\ip号 2.电脑的硬件名称(设备管理器中)是可以用软件修改的. 3.电脑中每一个连接网络的设备都有一个网卡地址(MAC地址),如无线网卡地址.有线网卡地址. 4.wi ...

  8. (转)Linux环境进程间通信----系统 V 消息队列列

    转:http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/ 消息队列(也叫做报文队列)能够克服早期unix通信机制的一些缺点.作为早期unix通 ...

  9. opencv 打开摄像头(c++)

    1,打开视频文件 2,打开IP摄像头 读取大华摄像头 大华的网络摄像头编号:DH-IPC-HFW1225M-I1-0600B,用的是RTSP协议. "rtsp://admin:dahua@1 ...

  10. js面向对象(三)---组件开发

    一.对象的多种表现形式 1.提高对象的复用性 2.如何配置参数和默认参数 不知道该怎么描述,就直接上代码吧,下面做了2个例子,重点看整个组件的大体结构 用组件的方式做拖拽窗口,你可以狠狠的点击这里进行 ...