博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android NDK开发(1)----- Java与C互相调用实例详解
阅读量:7142 次
发布时间:2019-06-28

本文共 8910 字,大约阅读时间需要 29 分钟。

一、概述

      对于大部分应用开发者来说可能都不怎么接触到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 
2
6 7
13 14
20 21
27 28
34 35
41 42
48 49 50

修改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 static 156 {
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 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 10 #include
11 #include
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
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)传输的数据都正确。

 

附上完整工程代码,Android2.3的。

转载于:https://www.cnblogs.com/lknlfy/archive/2012/03/13/2394153.html

你可能感兴趣的文章
软件设计是怎样炼成的(7)——细节决定成败(详细设计)
查看>>
UVA11300 Spreading the Wealth
查看>>
C的一种老写法,没怎么理解,大家看看
查看>>
Linux系统安装如何给磁盘分区
查看>>
TOMCAT日常调整的手记.
查看>>
我的友情链接
查看>>
配置功能: WiFi无线网络配置代理
查看>>
备份服务器为什么不稳定的原因?
查看>>
我的友情链接
查看>>
转载-------我当网络包租公的日子
查看>>
linux下的公式编辑器
查看>>
网卡初始化一般操作
查看>>
linux 实现自动挂载
查看>>
材料采购科目解释
查看>>
系统丢失boot.ini启动文件怎么办?
查看>>
php防止恶意频繁刷新页面或form提交
查看>>
rm 命令
查看>>
php5.4 安装出现问题cp: cannot stat `sapi/cli/php.1': No such file or directory
查看>>
python语法总结
查看>>
我的友情链接
查看>>