您的位置:首页 > 移动开发 > Android开发

利用JNI技术在Android中调用、调试C++代码

2013-07-07 23:00 513 查看
在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在Android中会生成Linux系统下的.so文件(好吧,其实我基本没用过Linux)。

没写过JNI的可以看看我之前的博客(Windows下利用Visual Studio开发的过程):http://cherishlc.iteye.com/admin/blogs/1328136

以及自动生成工具swig的使用方法(数组的支持不好!其他挺方便):http://cherishlc.iteye.com/admin/blogs/1689224

另外推荐一篇非常不错的NDK博文,(配置忽略,主要是各种数据的传递,下代码看看吧)/article/4571578.html

扯远了,下面来看看真正在Android中的开发过程。

1、下载ADT及NDK

下载ADT (Android Developer Tools) Bundle:http://developer.android.com/sdk/index.html
下载NDKhttp://developer.android.com/tools/sdk/ndk/index.html

其中ADT中包含了Eclipse及google的开发套件,不用写C++的下载ADT就足够了。

NDK则是包含了GCC的编译器,以及各个平台(arm,X86,MIPS)的相关头文件,交叉编译的一些平台相关文件等。

2、在ADT中配置NDK路径

解压NDK压缩包到任意路径,按下图在ADT中(也即ADT解压后的Eclipse文件下的Eclipse中)设置NDK的路径。

设置方法如下图所示:



3、创建含有本地代码的Android Project

该过程分为以下两步:

创建普通的Android Application工程(注意最小支持的API版本要不小于14)
加入本地代码支持

具体过程如下图所示:

创建工程:



加入本地代码支持:



完成情况:



点击菜单栏Project->Build All命令进行编译。

注意:如果之前最小支持的API版本要不小于14,将出现编译错误。“Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 7 in ./AndroidManifest.xml”

解决方法如下:

打开AndroidManifest.xml,切换到源文件视图,将minSdkVersion 改为14以上:



4、编写Java端代码和C++端代码

Java端,注意不要继承自Android中的类,否则javah编译头文件时要指定android类路径。

Java代码


package com.lc.testndk2;

import android.util.Log;

public class NativeClass {

//数组a中的每个元素都加上b,返回值为在C++中数据是否为a中数据拷贝得到的(按值拷贝还是传递指针)

public static native boolean jniArrayAdd(int[] a, int b);

// 在C++中创建Java中的int数组,其中元素为 数组a中的对应元素乘以b

public static native int[] jnitArrayMul(int[] a,int b);

static {

Log.i("NativeClass","before load library");

System.loadLibrary("TestNDK2");//注意这里为自己指定的.so文件,无lib前缀,亦无后缀

Log.i("NativeClass","after load library");

}

}

javah推荐两种方法:

使用我写的工具,含本地方法的类多时,一次编译完成:/article/4395012.html
在Eclipse中配置外部工具,配置方法参考自(同时也是讲Android中JNI配置方法的博客):http://www.cnblogs.com/yemeishu/archive/2012/12/24/NDK%E5%BC%80%E5%8F%91.html

在Eclipse中配置javah外部工具方法为:





上图中最长的一行命令如下:

Cmdineclipse代码


-v -classpath "${project_loc}/bin/classes" -d "${project_loc}/jni" ${java_type_name}

配置好之后:



点刚才配置好的javah工具,生成.h文件,然后:



Java端调用JNI方法的代码:

将MainActivity改为:

Java代码


package com.lc.testndk2;

import java.util.Arrays;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView tv = new TextView(this);

int[] array = new int[] { 1, 2, 3};

String str = "数组,调用C++前" + Arrays.toString(array);

boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array, 1);

str += "\n在C++中为副本? " + isCopyOfArrayInCpp;

str += "\n数组,调用C++后:" + Arrays.toString(array);

tv.setText(str);

setContentView(tv);

}

}

编写C++代码:

打开刚才系统生成的TestNDK2.cpp,修改成如下样子:

C++代码


#include <jni.h>

#include "com_lc_testndk2_NativeClass.h"

#ifdef __cplusplus //最好有这个,否则被编译器改了函数名字找不到不要怪我

extern "C" {

#endif

/*

* Class: com_lc_testndk2_NativeClass

* Method: jinArrayAdd

* Signature: ([II)[I

*/JNIEXPORT jboolean JNICALL Java_com_lc_testndk2_NativeClass_jniArrayAdd(

JNIEnv * env, jclass, jintArray array, jint b) {

jsize size = env->GetArrayLength(array);

// jintArray sum=env->NewIntArray(2);

jboolean isCopy;

jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, &isCopy);

for (int i = 0; i < size; i++)

pArray[i] += b;

env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);

//env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);

return isCopy;

}

/*

* Class: com_lc_testndk2_NativeClass

* Method: jnitArrayMul

* Signature: ([II)[I

*/JNIEXPORT jintArray JNICALL Java_com_lc_testndk2_NativeClass_jnitArrayMul(

JNIEnv * env, jclass, jintArray array, jint b) {

jsize size = env->GetArrayLength(array);

jintArray product = env->NewIntArray(size);

jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, 0);

jint* pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);

// jintArray product = env->NewIntArray(size); //不能在这里创建!!因为上面的方法会使java进入critical region, 在这里创建的话虚拟机直接崩溃

for (int i = 0; i < size; i++)

pProduct[i] =pArray[i]* b;

env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);

env->ReleasePrimitiveArrayCritical(product,pProduct,JNI_COMMIT);

return product;

}

#ifdef __cplusplus

}

#endif

5、配置生成的.so文件的目标平台

Java是跨平台的可是C++生成动态链接文件不是!!!同是Android,底层的CPU架构不同,动态链接文件也不同。。。好吧,这个我不知道原因。。。

于是乎,还得为不同的CPU创建不同的动态链接库文件,好在一行命令搞定~所有的动态链接一起打包,管他是哪个CPU,统统适用,Happy啊。

参考自:http://bbs.csdn.net/topics/390158301

过程如下:



再编译时会发现生成了对应以上四个平台的.so文件~~~



一切搞定,可以运行了!!!运行结果如下:



6、Java与C++联合调试

参见:/article/8138812.html

注意:

C++的调试器有几秒的延迟才能启动好,也就是程序运行了一会儿才可以开始调试,所以要调试的代码一定要是几秒钟后才能调试!!!
断点设置在C++中才有效。。。

过程为:

右键点击工程文件, 在properties -> C/C++ Build中:



完了设置断点(只能在C++中)就可以启动调试了~~



好吧,,,调试怎么不灵光呢。。。再想想刚才的注意事项。。。好吧,早执行完了JNI代码了。。。

于是乎,修改MainActivity代码如下:

Java代码


package com.lc.testndk2;

import java.util.Arrays;

import java.util.Timer;

import java.util.TimerTask;

import android.annotation.SuppressLint;

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.util.Log;

import android.widget.TextView;

/**

* @author LC

*

*完整的演示Android通过JNI调用C++代码的工程

*/

public class MainActivity extends Activity {

TextView tv = null;

int count = 0;

Timer timer;

@SuppressLint("HandlerLeak")

class MyHandler extends Handler{

@Override

public void handleMessage(Message msg) {

if (tv != null) {

tv.setText(msg.getData().getString("text"));

}

super.handleMessage(msg);

}

};

Handler handle= new MyHandler();

class refreshTask extends TimerTask {

@Override

public void run() {

try {

count++;

Log.i("MainActivity", "before call native code,count="

+ count);

int[] array = new int[] { count, -count, 2*count };

String str = "第" + count + "次了\n";

str += "数组,调用C++前" + Arrays.toString(array);

boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array,1);

str += "\n在C++中为副本? " + isCopyOfArrayInCpp;

str += "\n数组,调用C++后:" + Arrays.toString(array)+"\n\n";

str+="测试在C++中创建数组:\n";

str += Arrays.toString(array)+"* 2 =";

str+=Arrays.toString(NativeClass.jnitArrayMul(array, 2))+"\n\n";

Message msg=new Message();

Bundle b=new Bundle();

b.putString("text", str);

msg.setData(b);

handle.sendMessage(msg);

Log.i("MainActivity", "after call native code");

} catch (Exception e) {

Log.i(MainActivity.class.getSimpleName(), e.toString());

e.printStackTrace();

}

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

tv = new TextView(this);

tv.setText("我是初始值");

setContentView(tv);

}

@Override

protected void onPause() {

Log.i(MainActivity.class.getSimpleName(),"onPuase()");

timer.cancel();

timer=null;

super.onPause();

}

@Override

protected void onResume() {

Log.i(MainActivity.class.getSimpleName(),"onResume()");

timer=new Timer();

timer.scheduleAtFixedRate(new refreshTask(), 0, 1000);

super.onResume();

}

}

运行到断点的结果:






大小: 121.8 KB




大小: 68.8 KB




大小: 37.8 KB




大小: 29.2 KB




大小: 40.6 KB




大小: 11.5 KB




大小: 98.3 KB




大小: 27.1 KB




大小: 25.9 KB




大小: 42 KB




大小: 7.8 KB




大小: 80.7 KB




大小: 71.1 KB




大小: 21.7 KB




大小: 36.3 KB

TestNDK2.rar (710.1 KB)
下载次数: 21
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: