Android NDK开发(1)----- Java与C互相调用实例详解
链接地址:http://www.cnblogs.com/lknlfy/archive/2012/03/13/2394153.html
一、概述
对于大部分应用开发者来说可能都不怎么接触到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)传输的数据都正确。
附上完整工程代码,Android2.3的。
http://files.cnblogs.com/lknlfy/my-CallEachOther.rar
Android NDK开发(1)----- Java与C互相调用实例详解的更多相关文章
- Android NDK 开发(四)java传递数据到C【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701 前面几篇文章介绍了Android NDK开发的简单概念.常见错误及处 ...
- Android NDK开发 JNI操作java构造方法,普通方法,静态方法(七)
Android NDK开发 JNI操作java普通.静态.构造方法 1.Jni实例化一个Java类的实例jobject 1.通过FindClas( ),获取Java类的的jclass 2.通过GetM ...
- Android NDK开发Hello Word!
在之前的博客中已经为大家介绍了,如何在win环境下配置DNK程序,本篇我将带大家实现一个简单的Hello jni程序,让大家真正感受一下NDK开发的魅力.这里我们选择使用C+JAVA开发Android ...
- Android NDK开发初识
神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理, ...
- Android NDK开发
Android NDK 开发教程(极客学院) 一.Android NDK环境搭建 使用最新ndk,直接抛弃cygwin,以前做Android的项目要用到NDK就必须要下载NDK,下载安装Cygwin( ...
- Android NDK 开发(三)--常见错误锦集合Log的使用【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41826511 Android NDK开发经常因某些因素会出现一些意想不到的错误, ...
- Android NDK 开发(二) -- 从Hlello World学起【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41805719 上篇文章讲述了Android NDK开发的一些基本概念,以及NDK ...
- android NDK开发环境搭建
android NDK开发环境搭建 2012-05-14 00:13:58 分类: 嵌入式 基于 Android NDK 的学习之旅-----环境搭建 工欲善其事必先利其器 , 下面介绍下 Eclip ...
- !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-opencv-development-3/ Android Ndk a ...
随机推荐
- 使WEBBROWSER 可编辑
procedure TForm1.CheckBox1Click(Sender: TObject);begin {这里是让整个页面可编辑, 也可以单独编辑某个元素} WebBrowser1.OleO ...
- 用sqlserver处理excel表格
本来最近在研究微信公众平台的,老大临时交我个任务,把excel表格里的数据导入sql数据库,我想这so easy嘛. 没想都在上面消磨了两天... 把情况介绍下:在数据库中有如下这样结构的表(A表) ...
- ognl.InappropriateExpressionException: Inappropriate OGNL expression: 1
WARN OgnlValueStack:49 - Error setting expression '1' with value '[Ljava.lang.String;@11c7eb2' ognl. ...
- [置顶] 请听一个故事------>你真的认为iPhone只是一部手机?苹果惊天秘密!!
在网上看到的一篇小说,感觉有点意思,转载过来大家一起围观下,作者很幽默很风趣. 导读:iPhone的隐藏功能!Jobs的军方身份!图灵服毒自杀的传奇故事!中兴华为的神秘背景! 你真的认为iPhone只 ...
- 应用java多线程实现server端与多client之间的通信
应用多线程来实现server与多线程之间的通信的基本步骤 1.server端创建ServerSocket,循环调用accept()等待client链接 2.client创建一个Socket并请求和se ...
- 初探swift语言的学习笔记(闭包 - 匿名函数或block块代码)
很多高级语言都支持匿名函数操作,在OC中的block也为大家所熟悉,然面在swift里好像是被重新作了一个定义,不叫匿名函数,或 block了,而叫闭包(closure).下面配合代码来理解一下swi ...
- cocos2d-x -- 渠道SDK【棱镜】接入(2)
上一章<cocos2d-x -- 渠道SDK[棱镜]接入(1)>,已经接入好了SDK.如今要准备加入渠道了,以豌豆荚为例. 详细流程: 1.加入渠道:
- java入门学习(九) 算术运算符
请大家关注我的博客www.taomaipin.com 运算符在java基础中也占有着举足轻重的位置,我们当然要学会它.java 其实和其他计算机语言一样,基本的算术运算符基本一样,让我们看看 有哪些算 ...
- libcurl使用示例
远程下载文件,并将http 头信息存放内存中以及文件大小等相关信息: #include <stdio.h> #include <curl/curl.h> #include &l ...
- ORACLE 查找字段在哪些表里存在
查找不是主键的字段在哪些表里存在: select owner, table_namefrom dba_tab_columnswhere lower(column_name)='firstname'; ...