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

时间:2023-02-05 01:51:45

 Android NDK开发(1----- JavaC互相调用实例详解

一、概述

      对于大部分应用开发者来说可能都不怎么接触到NDK,但如果涉及到硬件操作的话就不得不使用NDK了。使用NDK还有另一个原因,就是C/C++的效率比较高,因此我们可以把一些耗时的操作放在NDK中实现。所以NDK主要做驱动的开发。

      在java中调用一个本地方法,然后由该本地方法直接返回一个参数给java(例如,在java中定义的本地方法为private int callJNI(int i))。但在大多数时候要求的并不是由开发者在java层主动去调JNI中的函数来返回想要的数据,而是由JNI主动去调java中的函数。举个最简单的例子,Android中的Camera,图像数据由内核一直往上传到java层,然而这些数据的传递并不需要开发者每一次主动去调用来JNI中的函数来获取,而是由JNI主动传给用java中方法,这类似于Linux驱动机制中的异步通知。

 

二、要求

      用NDK实现JavaC/C++互调,实现intstringbyte[]这三种类型的互相传递。

 

三、实现

      下面的实现中,每次java调用JNI中的某个函数时,最后会在该函数里回调java中相应的方法而不是直接返回一个参数。可能你会觉得这不还是每次都是由开发者来主动调用吗,其实这只是为了讲解而已,在实际应用中,回调java中的方法应该由某个事件(非java层)来触发。

 

步骤:

1Java类里的声明

package com.ljt.work;

 

import android.annotation.SuppressLint;

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 LjtndkActivity extends Activity{

 //本地方法,由java调用

    private native void callJNIInt(int i);

    private native void callJNIString(String s);

    private native void callJNIByte(byte[] b);

    

    

 static

 {

     //加载本地库

        System.loadLibrary("myjni");

    }

 

 

   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;

@SuppressLint("HandlerLeak")

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

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;

                    }

                }

                               

            }       

            

        };

        

        

}

/*下面定义被JNI调用的方法*/

//被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);

    }

    

//按钮监听事件

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("你好!我是从JNI函数中返回一个字符串");             

                    break;

                }

                case R.id.arraybutton:

                {                

                    //调用JNI中的函数

                    callJNIByte(new byte[]{1,2,3,4,5});               

                    break;

                }

                

            }

}

}

    

}

 

 

 

 

        (2在工程的根目录下新建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)

 

 

 

callback.c文件如下:

#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 <com_ljt_work_LjtndkActivity_ClickListener.h>

#include <com_ljt_work_LjtndkActivity.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_ljt_work_LjtndkActivity_callJNIInt

(JNIEnv* env, jobject obj , jint i){

 

    //找到java中的类

    jclass cls = (*env)->FindClass(env,"com/ljt/work/LjtndkActivity");

 

    //再找类中的方法,调用callbackInt方法传输整数

        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_ljt_work_LjtndkActivity_callJNIString

(JNIEnv* env, jobject obj , jstring s){

 

    //找到java中的类

    jclass cls = (*env)->FindClass(env,"com/ljt/work/LjtndkActivity");

    //再找类中的方法callbackString

        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_ljt_work_LjtndkActivity_callJNIByte

(JNIEnv* env, jobject obj ,jbyteArray b){

 

    //找到java中的类

    jclass cls = (*env)->FindClass(env,"com/ljt/work/LjtndkActivity");

    //再找类中的方法callbackByte

        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);

}

 

 

 

/********相加*************

*/

 

 

JNIEXPORT jlong JNICALL Java_com_ljt_work_LjtndkActivity_AddJNInum

(JNIEnv* env, jobject obj ,jlong x,jlong y){

 

    return x+y;

 

}

 

 

 

 

 

 

 

 

 

 

 

进入H:\android-ndk-r9d\samples\LJTNDKwork\bin\classes目录

用javah -jni 

ljt@ljt-PC /cygdrive/H/android-ndk-r9d/samples/LJTNDKwork/bin/classes

$ javah -jni com.ljt.work.LjtndkActivity

.\com\ljt\work\LjtndkActivity.class: 警告:无法找到类型为“android.annotation.Su

ppressLint”的注释方法“value()未找到 android.annotation.SuppressLint 的类文

警告

 然后将到的.h库文件声明到.c文件中

#include <com_ljt_work_LjtndkActivity_ClickListener.h>

#include <com_ljt_work_LjtndkActivity.h>

然后进入H:\android-ndk-r9d\samples\LJTNDKwork

$NDK/ndk-build

 

ljt@ljt-PC /cygdrive/H/android-ndk-r9d/samples/LJTNDKwork

$ $NDK/ndk-build

[armeabi] Cygwin         : Generating dependency file converter script

[armeabi] Compile thumb  : myjni <= callback.c

[armeabi] SharedLibrary  : libmyjni.so

[armeabi] Install        : libmyjni.so => libs/armeabi/libmyjni.so

调试:

04-09 21:07:13.728: I/native-activity(555): from java string: 你好!我是一个字符串haha

04-09 21:07:13.728: W/dalvikvm(555): JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0xe3

04-09 21:07:13.728: W/dalvikvm(555):              string: '���!����һ���ַhaha'

04-09 21:07:13.728: W/dalvikvm(555):              in Lcom/ljt/work/LjtndkActivity;.callJNIString:(Ljava/lang/String;)V (NewStringUTF)