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

本文详细介绍了一种通过NDK实现Java与C/C++互相调用的方法,特别关注于C/C++主动回调Java的情况。文章提供了具体的代码示例,演示了如何在Android环境下实现整型、字符串及字节数组等不同数据类型的传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical" 
    tools:context=".MainActivity" >


    <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的监听。如下:

 
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 MainActivity 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;
    
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_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文件如下:
 
 LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := myjni
LOCAL_SRC_FILES := callback.c

LOCAL_LDLIBS    := -llog

include $(BUILD_SHARED_LIBRARY)

#include <string.h>
#include <jni.h>

JNIEXPORT void JNICALL Java_com_example_mycallback_MainActivity_callJNIInt( JNIEnv* env, jobject obj , jint i)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env, "com/example/mycallback/MainActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackInt", "(I)V");
     if (mid == NULL)
     {
        printf("int error");
         return; 
     }
     //打印接收到的数据
     printf("from java int: %d",i);
     //回调java中的方法
     (*env)->CallVoidMethod(env, obj, mid ,i);
        
 }   

 JNIEXPORT void JNICALL Java_com_example_mycallback_MainActivity_callJNIString( JNIEnv* env, jobject obj , jstring s)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env,  "com/example/mycallback/MainActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V");
     if (mid == NULL)
     {
        printf("string error");
         return; 
     }
     const char *ch;
     //获取由java传过来的字符串
     ch = (*env)->GetStringUTFChars(env, s, NULL);
     //打印
     printf("from java string: %s",ch);
     (*env)->ReleaseStringUTFChars(env, s, ch);   
     //回调java中的方法
     (*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha"));
 }


 JNIEXPORT void JNICALL JNICALL Java_com_example_mycallback_MainActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b)
 {
     //找到java中的类
     jclass cls = (*env)->FindClass(env,  "com/example/mycallback/MainActivity");
     //再找类中的方法
     jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V");
     if (mid == NULL)
     {
       //  LOGI("byte[] error");
         return; 
     }
    
     //获取数组长度
     jsize length = (*env)->GetArrayLength(env,b);
     printf("length: %d",length);   
     //获取接收到的数据
     int i;
     jbyte* p = (*env)->GetByteArrayElements(env,b,NULL);
     //打印
     for(i=0;i<length;i++)
     {
         printf("%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编译生成相应的库。代码都非常简单,思路在一开始的时候已经说明了,下面看运行结果。
分别点击三个按钮,效果如下:

\

 

再看看LogCat输出:

\

 

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

摘自  lknlfy


http://blog.sina.com.cn/s/blog_972577b30101dawx.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值