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

来源:互联网 发布:简述网络贷款的危害 编辑:程序博客网 时间:2024/05/22 12:27

一、概述
      对于大部分应用开发者来说可能都不怎么接触到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文件如下:
 

[html] view plaincopy
  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.     <Button  
  7.         android:id="@+id/intbutton"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="传给JNI一个整数1"  
  11.         />  
  12.      
  13.     <TextView  
  14.         android:id="@+id/inttextview"  
  15.         android:layout_width="fill_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:text="接收到的整数:"  
  18.         />  
  19.      
  20.     <Button  
  21.         android:id="@+id/stringbutton"  
  22.         android:layout_width="fill_parent"  
  23.         android:layout_height="wrap_content"  
  24.         android:text="传给JNI一个字符A"  
  25.         />  
  26.      
  27.     <TextView  
  28.         android:id="@+id/stringtextview"  
  29.         android:layout_width="fill_parent"  
  30.         android:layout_height="wrap_content"  
  31.         android:text="接收到的字符:"  
  32.         />  
  33.   
  34.     <Button  
  35.         android:id="@+id/arraybutton"  
  36.         android:layout_width="fill_parent"  
  37.         android:layout_height="wrap_content"  
  38.         android:text="传给JNI一个数组12345"  
  39.         />  
  40.      
  41.     <TextView  
  42.         android:id="@+id/arraytextview"  
  43.         android:layout_width="fill_parent"  
  44.         android:layout_height="wrap_content"  
  45.         android:text="接收到的数组:"  
  46.         />  
  47.   
  48. </LinearLayout>  


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

[java] view plaincopy
  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文件如下:
 

[cpp] view plaincopy
  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文件如下:
 

[plain] view plaincopy
  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. /********传输字符串*************  
  40. */  
  41. JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIString( JNIEnv* env, jobject obj , jstring s)  
  42. {  
  43.     //找到java中的类  
  44.     jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");  
  45.     //再找类中的方法  
  46.     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V");  
  47.     if (mid == NULL)  
  48.     {  
  49.         LOGI("string error");  
  50.         return;   
  51.     }  
  52.     const char *ch;  
  53.     //获取由java传过来的字符串  
  54.     ch = (*env)->GetStringUTFChars(env, s, NULL);  
  55.     //打印  
  56.     LOGI("from java string: %s",ch);  
  57.     (*env)->ReleaseStringUTFChars(env, s, ch);     
  58.     //回调java中的方法  
  59.     (*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha"));  
  60.   
  61. }  
  62.   
  63. /********传输数组(byte[])*************  
  64. */  
  65. JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b)  
  66. {  
  67.     //找到java中的类  
  68.     jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");  
  69.     //再找类中的方法  
  70.     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V");  
  71.     if (mid == NULL)  
  72.     {  
  73.         LOGI("byte[] error");  
  74.         return;   
  75.     }  
  76.      
  77.     //获取数组长度  
  78.     jsize length = (*env)->GetArrayLength(env,b);  
  79.     LOGI("length: %d",length);     
  80.     //获取接收到的数据  
  81.     int i;  
  82.     jbyte* p = (*env)->GetByteArrayElements(env,b,NULL);  
  83.     //打印  
  84.     for(i=0;i<length;i++)  
  85.     {  
  86.         LOGI("%d",p[i]);     
  87.     }  
  88.   
  89.     char c[5];  
  90.     c[0] = 1;c[1] = 2;c[2] = 3;c[3] = 4;c[4] = 5;  
  91.     //构造数组  
  92.     jbyteArray carr = (*env)->NewByteArray(env,length);  
  93.     (*env)->SetByteArrayRegion(env,carr,0,length,c);  
  94.     //回调java中的方法  
  95.     (*env)->CallVoidMethod(env, obj, mid ,carr);  
  96. }  


利用ndk-build编译生成相应的库。代码都非常简单,思路在一开始的时候已经说明了,下面看运行结果。
分别点击三个按钮,效果如下:

\

 

再看看LogCat输出:

\

 

可见两个方向(java<--->JNI)传输的数据都正确。

0 0
原创粉丝点击