Android NDK开发 Java与C互相调用实例详解

本文详细介绍了如何使用NDK实现Java与C/C++之间的数据互调,包括整数、字符串、字节数组的传递过程。通过构建一个回调机制,使得数据可以在不同语言环境中无缝传输。

http://abc20899.iteye.com/blog/1861121

一、概述

      对于大部分应用开发者来说可能都不怎么接触到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文件如下:
新建工程MyCallback,修改main.xml文件,在里面添加3个Button,分别对应3种类型的调用和3个TextView分别显示由JNI回调java时传给java的数据。完整的main.xml文件如下:

Java代码 复制代码 收藏代码
  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> 
<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:orientation="vertical" >
 
     <Button 
         android:id="@+id/intbutton"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="传给JNI一个整数1"
         /> 
     
     <TextView
         android:id="@+id/inttextview"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="接收到的整数:" 
         />
     
     <Button 
         android:id="@+id/stringbutton"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="传给JNI一个字符A"
         /> 
     
     <TextView
         android:id="@+id/stringtextview"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="接收到的字符:" 
         />
     
     <Button 
         android:id="@+id/arraybutton"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="传给JNI一个数组12345"
         /> 
     
     <TextView
         android:id="@+id/arraytextview"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="接收到的数组:" 
         />
     
 
 </LinearLayout>



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

Java代码 复制代码 收藏代码
  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.      private Button intButton = null
  14.      private Button stringButton = null
  15.      private Button arrayButton = null
  16.      private TextView intTextView = null;  
  17.      private TextView stringTextView = null;  
  18.      private TextView arrayTextView = null;  
  19.       
  20.      private Handler mHandler = null
  21.       
  22.       
  23.      /** Called when the activity is first created. */ 
  24.      @Override 
  25.      public void onCreate(Bundle savedInstanceState)  
  26.      { 
  27.          super.onCreate(savedInstanceState); 
  28.          setContentView(R.layout.main); 
  29.           
  30.          intButton = (Button)this.findViewById(R.id.intbutton); 
  31.          //注册按钮监听 
  32.          intButton.setOnClickListener(new ClickListener()); 
  33.          stringButton = (Button)this.findViewById(R.id.stringbutton); 
  34.          //注册按钮监听 
  35.          stringButton.setOnClickListener(new ClickListener()); 
  36.          arrayButton = (Button)this.findViewById(R.id.arraybutton); 
  37.          //注册按钮监听 
  38.          arrayButton.setOnClickListener(new ClickListener()); 
  39.           
  40.          intTextView = (TextView)this.findViewById(R.id.inttextview); 
  41.          stringTextView = (TextView)this.findViewById(R.id.stringtextview); 
  42.          arrayTextView = (TextView)this.findViewById(R.id.arraytextview); 
  43.           
  44.          //消息处理       
  45.          mHandler = new Handler() 
  46.          { 
  47.              @Override 
  48.              public void handleMessage(Message msg) 
  49.              { 
  50.                  switch(msg.what) 
  51.                  { 
  52.                      //整型 
  53.                      case 0
  54.                      { 
  55.                          intTextView.setText(msg.obj.toString()); 
  56.                          break
  57.                      } 
  58.                      //字符串 
  59.                      case 1
  60.                      { 
  61.                          stringTextView.setText(msg.obj.toString()); 
  62.                          break
  63.                      } 
  64.                      //数组 
  65.                      case 2
  66.                      {   byte[] b = (byte[])msg.obj;                   
  67.                          arrayTextView.setText(Byte.toString(b[0])+Byte.toString(b[1])+Byte.toString(b[2])+Byte.toString(b[3])+Byte.toString(b[4]));                      
  68.                          break
  69.                      } 
  70.                  } 
  71.                                  
  72.              }        
  73.               
  74.          }; 
  75.           
  76.           
  77.      } 
  78.               
  79.      //按钮监听实现 
  80.      public class ClickListener implements View.OnClickListener 
  81.      { 
  82.   
  83.          @Override 
  84.          public void onClick(View v)  
  85.          { 
  86.              // TODO Auto-generated method stub 
  87.              switch(v.getId()) 
  88.              { 
  89.                  case R.id.intbutton: 
  90.                  { 
  91.                      //调用JNI中的函数 
  92.                      callJNIInt(1);       
  93.                      break
  94.                  } 
  95.                  case R.id.stringbutton: 
  96.                  { 
  97.                      //调用JNI中的函数 
  98.                      callJNIString("你好A");              
  99.                      break
  100.                  } 
  101.                  case R.id.arraybutton: 
  102.                  {                 
  103.                      //调用JNI中的函数 
  104.                      callJNIByte(new byte[]{1,2,3,4,5});                
  105.                      break
  106.                  } 
  107.              } 
  108.          } 
  109.           
  110.      } 
  111.     
  112.       
  113.      //被JNI调用,参数由JNI传入 
  114.      private void callbackInt(int i) 
  115.      { 
  116.          Message msg = new Message(); 
  117.          //消息类型 
  118.          msg.what = 0
  119.          //消息内容 
  120.          msg.obj = i; 
  121.          //发送消息 
  122.          mHandler.sendMessage(msg); 
  123.      } 
  124.       
  125.      //被JNI调用,参数由JNI传入 
  126.      private void callbackString(String s) 
  127.      { 
  128.          Message msg = new Message(); 
  129.          //消息类型 
  130.          msg.what = 1
  131.          //消息内容 
  132.          msg.obj = s; 
  133.          //发送消息 
  134.          mHandler.sendMessage(msg); 
  135.      } 
  136.       
  137.      //被JNI调用,参数由JNI传入 
  138.      private void callbackByte(byte[] b) 
  139.      { 
  140.          Message msg = new Message(); 
  141.          //消息类型 
  142.          msg.what = 2
  143.          //消息内容 
  144.          msg.obj = b;      
  145.          //发送消息 
  146.          mHandler.sendMessage(msg); 
  147.      } 
  148.       
  149.      //本地方法,由java调用 
  150.      private native void callJNIInt(int i); 
  151.      private native void callJNIString(String s); 
  152.      private native void callJNIByte(byte[] b); 
  153.       
  154.      static 
  155.      { 
  156.          //加载本地库 
  157.          System.loadLibrary("myjni"); 
  158.      } 
  159.       
package com.nan.callback;
 
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
 
 
 public class MyCallbackActivity extends Activity 
 {
     private Button intButton = null;
     private Button stringButton = null;
     private Button arrayButton = null;
     private TextView intTextView = null; 
     private TextView stringTextView = null; 
     private TextView arrayTextView = null; 
     
     private Handler mHandler = null;
     
     
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) 
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         
         intButton = (Button)this.findViewById(R.id.intbutton);
         //注册按钮监听
         intButton.setOnClickListener(new ClickListener());
         stringButton = (Button)this.findViewById(R.id.stringbutton);
         //注册按钮监听
         stringButton.setOnClickListener(new ClickListener());
         arrayButton = (Button)this.findViewById(R.id.arraybutton);
         //注册按钮监听
         arrayButton.setOnClickListener(new ClickListener());
         
         intTextView = (TextView)this.findViewById(R.id.inttextview);
         stringTextView = (TextView)this.findViewById(R.id.stringtextview);
         arrayTextView = (TextView)this.findViewById(R.id.arraytextview);
         
         //消息处理      
         mHandler = new Handler()
         {
             @Override
             public void handleMessage(Message msg)
             {
                 switch(msg.what)
                 {
                     //整型
                     case 0:
                     {
                         intTextView.setText(msg.obj.toString());
                         break;
                     }
                     //字符串
                     case 1:
                     {
                         stringTextView.setText(msg.obj.toString());
                         break;
                     }
                     //数组
                     case 2:
                     {   byte[] b = (byte[])msg.obj;                  
                         arrayTextView.setText(Byte.toString(b[0])+Byte.toString(b[1])+Byte.toString(b[2])+Byte.toString(b[3])+Byte.toString(b[4]));                     
                         break;
                     }
                 }
                                
             }       
             
         };
         
         
     }
             
     //按钮监听实现
     public class ClickListener implements View.OnClickListener
     {
 
         @Override
         public void onClick(View v) 
         {
             // TODO Auto-generated method stub
             switch(v.getId())
             {
                 case R.id.intbutton:
                 {
                     //调用JNI中的函数
                     callJNIInt(1);      
                     break;
                 }
                 case R.id.stringbutton:
                 {
                     //调用JNI中的函数
                     callJNIString("你好A");             
                     break;
                 }
                 case R.id.arraybutton:
                 {                
                     //调用JNI中的函数
                     callJNIByte(new byte[]{1,2,3,4,5});               
                     break;
                 }
             }
         }
         
     }
   
     
     //被JNI调用,参数由JNI传入
     private void callbackInt(int i)
     {
         Message msg = new Message();
         //消息类型
         msg.what = 0;
         //消息内容
         msg.obj = i;
         //发送消息
         mHandler.sendMessage(msg);
     }
     
     //被JNI调用,参数由JNI传入
     private void callbackString(String s)
     {
         Message msg = new Message();
         //消息类型
         msg.what = 1;
         //消息内容
         msg.obj = s;
         //发送消息
         mHandler.sendMessage(msg);
     }
     
     //被JNI调用,参数由JNI传入
     private void callbackByte(byte[] b)
     {
         Message msg = new Message();
         //消息类型
         msg.what = 2;
         //消息内容
         msg.obj = b;     
         //发送消息
         mHandler.sendMessage(msg);
     }
     
     //本地方法,由java调用
     private native void callJNIInt(int i);
     private native void callJNIString(String s);
     private native void callJNIByte(byte[] b);
     
     static
     {
         //加载本地库
         System.loadLibrary("myjni");
     }
     
 }



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

Java代码 复制代码 收藏代码
  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) 
LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
 LOCAL_MODULE    := myjni
 LOCAL_SRC_FILES := callback.c
 
 LOCAL_LDLIBS    := -llog
 
 include $(BUILD_SHARED_LIBRARY)



callback.c文件如下:

Java代码 复制代码 收藏代码
  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. JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIInt( JNIEnv* env, jobject obj , jint i) 
  21.      //找到java中的类 
  22.      jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity"); 
  23.      //再找类中的方法 
  24.      jmethodID mid = (*env)->GetMethodID(env, cls, "callbackInt", "(I)V"); 
  25.      if (mid == NULL)  
  26.      { 
  27.          LOGI("int error"); 
  28.          return;   
  29.      } 
  30.      //打印接收到的数据 
  31.      LOGI("from java int: %d",i); 
  32.      //回调java中的方法 
  33.      (*env)->CallVoidMethod(env, obj, mid ,i); 
  34.           
  35. }     
  36.   
  37. /********传输字符串*************
  38. */ 
  39. JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIString( JNIEnv* env, jobject obj , jstring s) 
  40.      //找到java中的类 
  41.      jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity"); 
  42.      //再找类中的方法 
  43.      jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V"); 
  44.      if (mid == NULL)  
  45.      { 
  46.          LOGI("string error"); 
  47.          return;   
  48.      } 
  49.      const char *ch; 
  50.      //获取由java传过来的字符串 
  51.      ch = (*env)->GetStringUTFChars(env, s, NULL); 
  52.      //打印 
  53.      LOGI("from java string: %s",ch); 
  54.      (*env)->ReleaseStringUTFChars(env, s, ch);     
  55.      //回调java中的方法 
  56.      (*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha")); 
  57.   
  58.   
  59. /********传输数组(byte[])*************
  60. */ 
  61. JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b) 
  62.      //找到java中的类 
  63.      jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity"); 
  64.      //再找类中的方法 
  65.      jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V"); 
  66.      if (mid == NULL)  
  67.      { 
  68.          LOGI("byte[] error"); 
  69.          return;   
  70.      } 
  71.       
  72.      //获取数组长度 
  73.      jsize length = (*env)->GetArrayLength(env,b); 
  74.      LOGI("length: %d",length);     
  75.      //获取接收到的数据 
  76.      int i; 
  77.      jbyte* p = (*env)->GetByteArrayElements(env,b,NULL); 
  78.      //打印 
  79.      for(i=0;i<length;i++) 
  80.      { 
  81.          LOGI("%d",p[i]);     
  82.      } 
  83.   
  84.      char c[5]; 
  85.      c[0] = 1;c[1] = 2;c[2] = 3;c[3] = 4;c[4] = 5
  86.      //构造数组 
  87.      jbyteArray carr = (*env)->NewByteArray(env,length); 
  88.      (*env)->SetByteArrayRegion(env,carr,0,length,c); 
  89.      //回调java中的方法 
  90.      (*env)->CallVoidMethod(env, obj, mid ,carr); 
#include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
 #include <jni.h>
 #include <android/log.h>
 
 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
 
 
 
 /**********传输整数*************
 
 */
 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIInt( JNIEnv* env, jobject obj , jint i)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackInt", "(I)V");
     if (mid == NULL) 
     {
         LOGI("int error");
         return;  
     }
     //打印接收到的数据
     LOGI("from java int: %d",i);
     //回调java中的方法
     (*env)->CallVoidMethod(env, obj, mid ,i);
         
 }    
 
 /********传输字符串*************
 */
 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIString( JNIEnv* env, jobject obj , jstring s)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V");
     if (mid == NULL) 
     {
         LOGI("string error");
         return;  
     }
     const char *ch;
     //获取由java传过来的字符串
     ch = (*env)->GetStringUTFChars(env, s, NULL);
     //打印
     LOGI("from java string: %s",ch);
     (*env)->ReleaseStringUTFChars(env, s, ch);    
     //回调java中的方法
     (*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha"));
 
 }
 
 /********传输数组(byte[])*************
 */
 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V");
     if (mid == NULL) 
     {
         LOGI("byte[] error");
         return;  
     }
     
     //获取数组长度
     jsize length = (*env)->GetArrayLength(env,b);
     LOGI("length: %d",length);    
     //获取接收到的数据
     int i;
     jbyte* p = (*env)->GetByteArrayElements(env,b,NULL);
     //打印
     for(i=0;i<length;i++)
     {
         LOGI("%d",p[i]);    
     }
 
     char c[5];
     c[0] = 1;c[1] = 2;c[2] = 3;c[3] = 4;c[4] = 5;
     //构造数组
     jbyteArray carr = (*env)->NewByteArray(env,length);
     (*env)->SetByteArrayRegion(env,carr,0,length,c);
     //回调java中的方法
     (*env)->CallVoidMethod(env, obj, mid ,carr);
 }




利用ndk-build编译生成相应的库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值