JNI实现

来源:互联网 发布:定制家具生产软件 编辑:程序博客网 时间:2024/05/16 08:44

一、概述

      对于大部分应用开发者来说可能都不怎么接触到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     <TextView15         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     <TextView29         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     <TextView43         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     static156     {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)传输的数据都正确。



转载自:http://www.cnblogs.com/lknlfy/archive/2012/03/13/2394153.html


原创粉丝点击