Android(安卓)NDK开发(1)----- Java与C互相调用实例详解
AndroidNDK开发(1)-----Java与C互相调用实例详解
一、概述
对于大部分应用开发者来说可能都不怎么接触到NDK,但如果涉及到硬件操作的话就不得不使用NDK了。使用NDK还有另一个原因,就是C/C++的效率比较高,因此我们可以把一些耗时的操作放在NDK中实现。所以NDK主要做驱动的开发。
在java中调用一个本地方法,然后由该本地方法直接返回一个参数给java(例如,在java中定义的本地方法为privateintcallJNI(inti))。但在大多数时候要求的并不是由开发者在java层主动去调JNI中的函数来返回想要的数据,而是由JNI主动去调java中的函数。举个最简单的例子,Android中的Camera,图像数据由内核一直往上传到java层,然而这些数据的传递并不需要开发者每一次主动去调用来JNI中的函数来获取,而是由JNI主动传给用java中方法,这类似于Linux驱动机制中的异步通知。
二、要求
用NDK实现Java与C/C++互调,实现int,string,byte[]这三种类型的互相传递。
三、实现
下面的实现中,每次java调用JNI中的某个函数时,最后会在该函数里回调java中相应的方法而不是直接返回一个参数。可能你会觉得这不还是每次都是由开发者来主动调用吗,其实这只是为了讲解而已,在实际应用中,回调java中的方法应该由某个事件(非java层)来触发。
步骤:
(1)Java类里的声明
packagecom.ljt.work;
importandroid.annotation.SuppressLint;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.TextView;
publicclassLjtndkActivityextendsActivity{
//本地方法,由java调用
privatenativevoidcallJNIInt(inti);
privatenativevoidcallJNIString(Strings);
privatenativevoidcallJNIByte(byte[]b);
static
{
//加载本地库
System.loadLibrary("myjni");
}
privateButtonintButton=null;
privateButtonstringButton=null;
privateButtonarrayButton=null;
privateTextViewintTextView=null;
privateTextViewstringTextView=null;
privateTextViewarrayTextView=null;
//定义一个处理线程的机制
privateHandlermHandler=null;
@SuppressLint("HandlerLeak")
@Override
protectedvoidonCreate(BundlesavedInstanceState){
//TODOAuto-generatedmethodstub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
intButton=(Button)this.findViewById(R.id.intbutton);
//注册按钮监听
intButton.setOnClickListener(newClickListener());
stringButton=(Button)this.findViewById(R.id.stringbutton);
//注册按钮监听
stringButton.setOnClickListener(newClickListener());
arrayButton=(Button)this.findViewById(R.id.arraybutton);
//注册按钮监听
arrayButton.setOnClickListener(newClickListener());
intTextView=(TextView)this.findViewById(R.id.inttextview);
stringTextView=(TextView)this.findViewById(R.id.stringtextview);
arrayTextView=(TextView)this.findViewById(R.id.arraytextview);
//消息处理
mHandler=newHandler()
{
@Override
publicvoidhandleMessage(Messagemsg)
{
switch(msg.what)
{
//整型
case0:
{
intTextView.setText(msg.obj.toString());
break;
}
//字符串
case1:
{
stringTextView.setText(msg.obj.toString());
break;
}
//数组
case2:
{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传入
privatevoidcallbackInt(inti)
{
Messagemsg=newMessage();
//消息类型
msg.what=0;
//消息内容
msg.obj=i;
//发送消息
mHandler.sendMessage(msg);
}
//被JNI调用,参数由JNI传入
privatevoidcallbackString(Strings)
{
Messagemsg=newMessage();
//消息类型
msg.what=1;
//消息内容
msg.obj=s;
//发送消息
mHandler.sendMessage(msg);
}
//被JNI调用,参数由JNI传入
privatevoidcallbackByte(byte[]b)
{
Messagemsg=newMessage();
//消息类型
msg.what=2;
//消息内容
msg.obj=b;
//发送消息
mHandler.sendMessage(msg);
}
//按钮监听事件
publicclassClickListenerimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
//TODOAuto-generatedmethodstub
switch(v.getId())
{
caseR.id.intbutton:
{
//调用JNI中的函数
callJNIInt(1);
break;
}
caseR.id.stringbutton:
{
//调用JNI中的函数
callJNIString("你好!我是从JNI函数中返回一个字符串");
break;
}
caseR.id.arraybutton:
{
//调用JNI中的函数
callJNIByte(newbyte[]{1,2,3,4,5});
break;
}
}
}
}
}
(2)在工程的根目录下新建jni文件夹,在里面添加一个Android.mk文件和一个callback.c文件,Android.mk文件如下:
LOCAL_PATH:=$(callmy-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>
#defineLOGI(...)((void)__android_log_print(ANDROID_LOG_INFO,"native-activity",__VA_ARGS__))
#defineLOGW(...)((void)__android_log_print(ANDROID_LOG_WARN,"native-activity",__VA_ARGS__))
/**********传输整数*************
两个方向都没有乱码问题
*/
JNIEXPORTvoidJNICALLJava_com_ljt_work_LjtndkActivity_callJNIInt
(JNIEnv*env,jobjectobj,jinti){
//找到java中的类
jclasscls=(*env)->FindClass(env,"com/ljt/work/LjtndkActivity");
//再找类中的方法,调用callbackInt方法传输整数
jmethodIDmid=(*env)->GetMethodID(env,cls,"callbackInt","(I)V");
if(mid==NULL)
{
LOGI("interror");
return;
}
//打印接收到的数据
LOGI("fromjavaint:%d",i);
//回调java中的方法
(*env)->CallVoidMethod(env,obj,mid,i);
}
/********传输字符串*************
两个方向都没有乱码问题
*/
JNIEXPORTvoidJNICALLJava_com_ljt_work_LjtndkActivity_callJNIString
(JNIEnv*env,jobjectobj,jstrings){
//找到java中的类
jclasscls=(*env)->FindClass(env,"com/ljt/work/LjtndkActivity");
//再找类中的方法callbackString
jmethodIDmid=(*env)->GetMethodID(env,cls,"callbackString","(Ljava/lang/String;)V");
if(mid==NULL)
{
LOGI("stringerror");
return;
}
constchar*ch;
//获取由java传过来的字符串
ch=(*env)->GetStringUTFChars(env,s,NULL);
//打印
LOGI("fromjavastring:%s",ch);
(*env)->ReleaseStringUTFChars(env,s,ch);
//回调java中的方法
(*env)->CallVoidMethod(env,obj,mid,(*env)->NewStringUTF(env,"你好!我是一个字符串haha"));
}
/********传输数组(byte[])*************
*/
JNIEXPORTvoidJNICALLJava_com_ljt_work_LjtndkActivity_callJNIByte
(JNIEnv*env,jobjectobj,jbyteArrayb){
//找到java中的类
jclasscls=(*env)->FindClass(env,"com/ljt/work/LjtndkActivity");
//再找类中的方法callbackByte
jmethodIDmid=(*env)->GetMethodID(env,cls,"callbackByte","([B)V");
if(mid==NULL)
{
LOGI("byte[]error");
return;
}
//获取数组长度
jsizelength=(*env)->GetArrayLength(env,b);
LOGI("length:%d",length);
//获取接收到的数据
inti;
jbyte*p=(*env)->GetByteArrayElements(env,b,NULL);
//打印
for(i=0;i<length;i++)
{
LOGI("%d",p[i]);
}
charc[5];
c[0]=1;c[1]=2;c[2]=3;c[3]=4;c[4]=5;
//构造数组
jbyteArraycarr=(*env)->NewByteArray(env,length);
(*env)->SetByteArrayRegion(env,carr,0,length,c);
//回调java中的方法
(*env)->CallVoidMethod(env,obj,mid,carr);
}
/********相加*************
*/
JNIEXPORTjlongJNICALLJava_com_ljt_work_LjtndkActivity_AddJNInum
(JNIEnv*env,jobjectobj,jlongx,jlongy){
returnx+y;
}
进入H:\android-ndk-r9d\samples\LJTNDKwork\bin\classes目录
用javah-jni
ljt@ljt-PC/cygdrive/H/android-ndk-r9d/samples/LJTNDKwork/bin/classes
$javah-jnicom.ljt.work.LjtndkActivity
.\com\ljt\work\LjtndkActivity.class:警告:无法找到类型为“android.annotation.Su
ppressLint”的注释方法“value()”:未找到android.annotation.SuppressLint的类文
件
1警告
然后将到的.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:Generatingdependencyfileconverterscript
[armeabi]Compilethumb:myjni<=callback.c
[armeabi]SharedLibrary:libmyjni.so
[armeabi]Install:libmyjni.so=>libs/armeabi/libmyjni.so
调试:
04-0921:07:13.728:I/native-activity(555):fromjavastring:你好!我是一个字符串haha
04-0921:07:13.728:W/dalvikvm(555):JNIWARNING:inputisnotvalidModifiedUTF-8:illegalcontinuationbyte0xe3
04-0921:07:13.728:W/dalvikvm(555):string:'���!����һ���ַ�haha'
04-0921:07:13.728:W/dalvikvm(555):inLcom/ljt/work/LjtndkActivity;.callJNIString:(Ljava/lang/String;)V(NewStringUTF)
更多相关文章
- Android基础知识巩固系列 Android之四大组件——Service(服务)
- [置顶] 解决android某些应用开发某些类无法解析/找到的问题--使
- Android面试真题,了解一下?
- 实现两次按返回键退出的五种方法
- ListView的两次测量(源码分析)
- android解析xml和json区别
- 向android 的状态栏中加入快捷按钮(home,back,menu等等)的方法(续)
- iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Androi
- 【重磅】通付盾独家获得应用加固发明专利授权