JNI实现
来源:互联网 发布:定制家具生产软件 编辑:程序博客网 时间:2024/05/16 08:44
一、概述
对于大部分应用开发者来说可能都不怎么接触到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文件如下:
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 <TextView15 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 <TextView29 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 <TextView43 android:id="@+id/arraytextview"44 android:layout_width="fill_parent"45 android:layout_height="wrap_content"46 android:text="接收到的数组:" 47 />48 49 50 </LinearLayout>
修改MyCallbackActivity.java文件,定义了一个Handler,当JNI回调java的方法时,用来发送消息;实现3个Button的监听。如下:
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 static156 {157 //加载本地库158 System.loadLibrary("myjni");159 }160 161 }
最后就是本篇随笔的“重头戏”,在工程的根目录下新建jni文件夹,在里面添加一个Android.mk文件和一个callback.c文件,Android.mk文件如下:
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文件如下:
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 /********传输字符串*************41 */42 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIString( JNIEnv* env, jobject obj , jstring s)43 {44 //找到java中的类45 jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");46 //再找类中的方法47 jmethodID mid = (*env)->GetMethodID(env, cls, "callbackString", "(Ljava/lang/String;)V");48 if (mid == NULL) 49 {50 LOGI("string error");51 return; 52 }53 const char *ch;54 //获取由java传过来的字符串55 ch = (*env)->GetStringUTFChars(env, s, NULL);56 //打印57 LOGI("from java string: %s",ch);58 (*env)->ReleaseStringUTFChars(env, s, ch); 59 //回调java中的方法60 (*env)->CallVoidMethod(env, obj, mid ,(*env)->NewStringUTF(env,"你好haha"));61 62 }63 64 /********传输数组(byte[])*************65 */66 JNIEXPORT void JNICALL Java_com_nan_callback_MyCallbackActivity_callJNIByte( JNIEnv* env, jobject obj , jbyteArray b)67 {68 //找到java中的类69 jclass cls = (*env)->FindClass(env, "com/nan/callback/MyCallbackActivity");70 //再找类中的方法71 jmethodID mid = (*env)->GetMethodID(env, cls, "callbackByte", "([B)V");72 if (mid == NULL) 73 {74 LOGI("byte[] error");75 return; 76 }77 78 //获取数组长度79 jsize length = (*env)->GetArrayLength(env,b);80 LOGI("length: %d",length); 81 //获取接收到的数据82 int i;83 jbyte* p = (*env)->GetByteArrayElements(env,b,NULL);84 //打印85 for(i=0;i<length;i++)86 {87 LOGI("%d",p[i]); 88 }89 90 char c[5];91 c[0] = 1;c[1] = 2;c[2] = 3;c[3] = 4;c[4] = 5;92 //构造数组93 jbyteArray carr = (*env)->NewByteArray(env,length);94 (*env)->SetByteArrayRegion(env,carr,0,length,c);95 //回调java中的方法96 (*env)->CallVoidMethod(env, obj, mid ,carr);97 }
利用ndk-build编译生成相应的库。代码都非常简单,思路在一开始的时候已经说明了,下面看运行结果。
分别点击三个按钮,效果如下:
再看看LogCat输出:
可见两个方向(java<--->JNI)传输的数据都正确。
转载自:http://www.cnblogs.com/lknlfy/archive/2012/03/13/2394153.html
- JNI实现
- jni实现
- 用jni实现ping
- 例解 VC 实现 JNI
- 简单的JNI实现
- Linux下JNI实现
- Android中JNI实现
- jni实现过程
- LInux 下 Jni 实现
- android JNI 实现测试
- Linux下JNI实现
- Linux下jni实现
- Linux下JNI实现
- Linux下JNI实现
- Linux下JNI实现
- 实现jni方法
- JNI的实现流程
- Linux下JNI实现
- 赋值构造函数找错题
- android两种方式实现发送短信的功能代码
- 使用Intent跳转后清除以前所访问的页面
- 最长公共子序列dp
- Mysql分页存储过程
- JNI实现
- servlet中alert对话框汉字乱码问题
- MySQL数据库设置远程访问权限方法总结
- linux下用mython语言写的简单zeromq push/pull模式操作mysql数据
- PHP非线程安全与线程安全版本的选择技巧
- Windwos 编程源码例子下载:包括界面编程,网路编程,图像编程,多媒体编程等等
- free&Linux服务器系统内存监控方法详解
- 管理Activity的生命周期
- 贪心算法活动选择问题