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

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

2012-11-12 16:35 309 查看
转自:/article/5068125.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)传输的数据都正确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: