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

Android NDK开发(1)----- Java与C互相调用实例详解

2012-05-22 19:42 771 查看
一、概述

对于大部分应用开发者来说可能都不怎么接触到NDK,但如果涉及到硬件操作的话就不得不使用NDK了。使用NDK还有另一个原因,就是C/C++的效率比较高,因此我们可以把一些耗时的操作放在NDK中实现。

关于java与c/c++的互相调用,网上有一大堆的文章介绍。但仔细观察可以发现,基本都是讲在java中调用一个本地方法,然后由该本地方法直接返回一个参数给java(例如,在java中定义的本地方法为private int callJNI(int i))。但在大多数时候要求的并不是由开发者在java层主动去调JNI中的函数来返回想要的数据,而是由JNI主动去调java中的函数。举个最简单的例子,Android中的Camera,图像数据由内核一直往上传到java层,然而这些数据的传递并不需要开发者每一次主动去调用来JNI中的函数来获取,而是由JNI主动传给用java中方法,这类似于Linux驱动机制中的异步通知。

二、要求

用NDK实现Java与C/C++互调,实现int,string,byte[]这三种类型的互相传递。

三、实现

下面的实现中,每次java调用JNI中的某个函数时,最后会在该函数里回调java中相应的方法而不是直接返回一个参数。可能你会觉得这不还是每次都是由开发者来主动调用吗,其实这只是为了讲解而已,在实际应用中,回调java中的方法应该由某个事件(非java层)来触发。

新建工程MyCallback,修改main.xml文件,在里面添加3个Button,分别对应3种类型的调用和3个TextView分别显示由JNI回调java时传给java的数据。完整的main.xml文件如下:

1 <?xml version="1.0" encoding="utf-8"?>

2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

3 android:layout_width="fill_parent"

4 android:layout_height="fill_parent"

5 android:orientation="vertical" >

6

7 <Button

8 android:id="@+id/intbutton"

9 android:layout_width="fill_parent"

10 android:layout_height="wrap_content"

11 android:text="传给JNI一个整数1"

12 />

13

14 <TextView

15 android:id="@+id/inttextview"

16 android:layout_width="fill_parent"

17 android:layout_height="wrap_content"

18 android:text="接收到的整数:"

19 />

20

21 <Button

22 android:id="@+id/stringbutton"

23 android:layout_width="fill_parent"

24 android:layout_height="wrap_content"

25 android:text="传给JNI一个字符A"

26 />

27

28 <TextView

29 android:id="@+id/stringtextview"

30 android:layout_width="fill_parent"

31 android:layout_height="wrap_content"

32 android:text="接收到的字符:"

33 />

34

35 <Button

36 android:id="@+id/arraybutton"

37 android:layout_width="fill_parent"

38 android:layout_height="wrap_content"

39 android:text="传给JNI一个数组12345"

40 />

41

42 <TextView

43 android:id="@+id/arraytextview"

44 android:layout_width="fill_parent"

45 android:layout_height="wrap_content"

46 android:text="接收到的数组:"

47 />

48

49

50 </LinearLayout>

修改MyCallbackActivity.java文件,定义了一个Handler,当JNI回调java的方法时,用来发送消息;实现3个Button的监听。如下:

1 package com.nan.callback;

2

3 import android.app.Activity;

4 import android.os.Bundle;

5 import android.os.Handler;

6 import android.os.Message;

7 import android.view.View;

8 import android.widget.Button;

9 import android.widget.TextView;

10

11

12 public class MyCallbackActivity extends Activity

13 {

14 private Button intButton = null;

15 private Button stringButton = null;

16 private Button arrayButton = null;

17 private TextView intTextView = null;

18 private TextView stringTextView = null;

19 private TextView arrayTextView = null;

20

21 private Handler mHandler = null;

22

23

24 /** Called when the activity is first created. */

25 @Override

26 public void onCreate(Bundle savedInstanceState)

27 {

28 super.onCreate(savedInstanceState);

29 setContentView(R.layout.main);

30

31 intButton = (Button)this.findViewById(R.id.intbutton);

32 //注册按钮监听

33 intButton.setOnClickListener(new ClickListener());

34 stringButton = (Button)this.findViewById(R.id.stringbutton);

35 //注册按钮监听

36 stringButton.setOnClickListener(new ClickListener());

37 arrayButton = (Button)this.findViewById(R.id.arraybutton);

38 //注册按钮监听

39 arrayButton.setOnClickListener(new ClickListener());

40

41 intTextView = (TextView)this.findViewById(R.id.inttextview);

42 stringTextView = (TextView)this.findViewById(R.id.stringtextview);

43 arrayTextView = (TextView)this.findViewById(R.id.arraytextview);

44

45 //消息处理

46 mHandler = new Handler()

47 {

48 @Override

49 public void handleMessage(Message msg)

50 {

51 switch(msg.what)

52 {

53 //整型

54 case 0:

55 {

56 intTextView.setText(msg.obj.toString());

57 break;

58 }

59 //字符串

60 case 1:

61 {

62 stringTextView.setText(msg.obj.toString());

63 break;

64 }

65 //数组

66 case 2:

67 { byte[] b = (byte[])msg.obj;

68 arrayTextView.setText(Byte.toString(b[0])+Byte.toString(b[1])+Byte.toString(b[2])+Byte.toString(b[3])+Byte.toString(b[4]));

69 break;

70 }

71 }

72

73 }

74

75 };

76

77

78 }

79

80 //按钮监听实现

81 public class ClickListener implements View.OnClickListener

82 {

83

84 @Override

85 public void onClick(View v)

86 {

87 // TODO Auto-generated method stub

88 switch(v.getId())

89 {

90 case R.id.intbutton:

91 {

92 //调用JNI中的函数

93 callJNIInt(1);

94 break;

95 }

96 case R.id.stringbutton:

97 {

98 //调用JNI中的函数

99 callJNIString("你好A");

100 break;

101 }

102 case R.id.arraybutton:

103 {

104 //调用JNI中的函数

105 callJNIByte(new byte[]{1,2,3,4,5});

106 break;

107 }

108 }

109 }

110

111 }

112

113

114 //被JNI调用,参数由JNI传入

115 private void callbackInt(int i)

116 {

117 Message msg = new Message();

118 //消息类型

119 msg.what = 0;

120 //消息内容

121 msg.obj = i;

122 //发送消息

123 mHandler.sendMessage(msg);

124 }

125

126 //被JNI调用,参数由JNI传入

127 private void callbackString(String s)

128 {

129 Message msg = new Message();

130 //消息类型

131 msg.what = 1;

132 //消息内容

133 msg.obj = s;

134 //发送消息

135 mHandler.sendMessage(msg);

136 }

137

138 //被JNI调用,参数由JNI传入

139 private void callbackByte(byte[] b)

140 {

141 Message msg = new Message();

142 //消息类型

143 msg.what = 2;

144 //消息内容

145 msg.obj = b;

146 //发送消息

147 mHandler.sendMessage(msg);

148 }

149

150 //本地方法,由java调用

151 private native void callJNIInt(int i);

152 private native void callJNIString(String s);

153 private native void callJNIByte(byte[] b);

154

155 static

156 {

157 //加载本地库

158 System.loadLibrary("myjni");

159 }

160

161 }

最后就是本篇随笔的“重头戏”,在工程的根目录下新建jni文件夹,在里面添加一个Android.mk文件和一个callback.c文件,Android.mk文件如下:

1 LOCAL_PATH := $(call my-dir)

2

3 include $(CLEAR_VARS)

4

5 LOCAL_MODULE := myjni

6 LOCAL_SRC_FILES := callback.c

7

8 LOCAL_LDLIBS := -llog

9

10 include $(BUILD_SHARED_LIBRARY)

callback.c文件如下:

1 #include <string.h>

2 #include <stdio.h>

3 #include <stdlib.h>

4 #include <unistd.h>

5 #include <sys/ioctl.h>

6 #include <sys/types.h>

7 #include <sys/stat.h>

8 #include <fcntl.h>

9

10 #include <jni.h>

11 #include <android/log.h>

12

13 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))

14 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))

15

16

17

18 /**********传输整数*************

19

20 */

21 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIInt( JNIEnv* env, jobject obj , jint i)

22 {

23 //找到java中的类

24 jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");

25 //再找类中的方法

26 jmethodID mid = (*env)->GetMethodID(env, cls, "callbackInt", "(I)V");

27 if (mid == NULL)

28 {

29 LOGI("int error");

30 return;

31 }

32 //打印接收到的数据

33 LOGI("from java int: %d",i);

34 //回调java中的方法

35 (*env)->CallVoidMethod(env, obj, mid ,i);

36

37 }

38

39 /********传输字符串*************

41 */

42 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIString( JNIEnv* env, jobject obj , jstring s)

43 {

44 //找到java中的类

45 jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");

46 //再找类中的方法

47 jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V");

48 if (mid == NULL)

49 {

50 LOGI("string error");

51 return;

52 }

53 const char *ch;

54 //获取由java传过来的字符串

55 ch = (*env)->GetStringUTFChars(env, s, NULL);

56 //打印

57 LOGI("from java string: %s",ch);

58 (*env)->ReleaseStringUTFChars(env, s, ch);

59 //回调java中的方法

60 (*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha"));

61

62 }

63

64 /********传输数组(byte[])*************

65 */

66 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b)

67 {

68 //找到java中的类

69 jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");

70 //再找类中的方法

71 jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V");

72 if (mid == NULL)

73 {

74 LOGI("byte[] error");

75 return;

76 }

77

78 //获取数组长度

79 jsize length = (*env)->GetArrayLength(env,b);

80 LOGI("length: %d",length);

81 //获取接收到的数据

82 int i;

83 jbyte* p = (*env)->GetByteArrayElements(env,b,NULL);

84 //打印

85 for(i=0;i<length;i++)

86 {

87 LOGI("%d",p[i]);

88 }

89

90 char c[5];

91 c[0] = 1;c[1] = 2;c[2] = 3;c[3] = 4;c[4] = 5;

92 //构造数组

93 jbyteArray carr = (*env)->NewByteArray(env,length);

94 (*env)->SetByteArrayRegion(env,carr,0,length,c);

95 //回调java中的方法

96 (*env)->CallVoidMethod(env, obj, mid ,carr);

97 }

利用ndk-build编译生成相应的库。代码都非常简单,思路在一开始的时候已经说明了,下面看运行结果。

分别点击三个按钮,效果如下:



再看看LogCat输出:



可见两个方向(java<--->JNI)传输的数据都正确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: