android.mk 是Android开发中,用来编译JNI代码的
在开发工具中,new JNI 即可创建一个 android.mk文件
一个Android.mk文件可以编译多个模块,每个模块属下列类型之一:
APK程序 一般的Android程序,编译打包生成apk文件
java库 java类库,编译打包生成jar文件
C\C++应用程序 可执行的C\C++应用程序
C\C++静态库 编译生成C\C++静态库,并打包成.a文件
C\C++共享库 编译生成共享库(动态链接库),并打包成.so文, 有且只有共享库才能被安装/复制到您的应用软件(APK)包中。
② JNI编程之如何传递参数(二)——数组类型的传递
来看一个传递int数组的例子,Java程序就不写了,JNIEXPORT jintJNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) { jint *carr; carr = env->GetIntArrayElements(arr, false); if(carr == NULL) { return 0; /* exception occurred */ } jint sum = 0; for(int i=0; i<10; i++) { sum += carr[i]; } env->ReleaseIntArrayElements(arr, carr, 0); return sum; }这个例子中的GetIntArrayElements和ReleaseIntArrayElements函数就是JNI提供用于处理int数组的函 数。如果试图用arr[i]的方式去访问jintArray类型,毫无疑问会出错。JNI还提供了另一对函数GetIntArrayRegion和 ReleaseIntArrayRegion访问int数组,就不介绍了,对于其他基本类型的数组,方法类似。二维数组和String数组在JNI中,二维数组和String数组都被视为object数组,因为数组和String被视为object。仍然用一个例子来说明,这次是一个二维int数组,作为返回值。JNIEXPORTjobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclasscls, int size) { jobjectArray result; jclass intArrCls = env->FindClass("[I"); result = env->NewObjectArray(size, intArrCls, NULL); for (int i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ jintArray iarr = env->NewIntArray(size); for(int j = 0; j < size; j++) { tmp[j] = i + j; } env->SetIntArrayRegion(iarr, 0, size, tmp); env->SetObjectArrayElement(result, i, iarr); env->DeleteLocalRef(iarr); } return result; }上面代码中的第三行, jobjectArray result; 因为要返回值,所以需要新建一个jobjectArray对象。jclass intArrCls = env->FindClass("[I"); 是创建一个jclass的引用,因为result的元素是一维int数组的引用,所以intArrCls必须是一维int数组的引用,这一点是如何保证的 呢?注意FindClass的参数"[I",JNI就是通过它来确定引用的类型的,I表示是int类型,[标识是数组。对于其他的类型,都有相应的表示方 法,Z booleanB byteC charS shortI intJ longF floatD doubleString是通过“Ljava/lang/String;”表示的,那相应的,String数组就应该是“[Ljava/lang/String;”。还是回到代码, result = env->NewObjectArray(size, intArrCls, NULL); 的作用是为result分配空间。
③ JNI编程之如何传递参数(一)——String参数的传递
先看一个例子,class Prompt { // native method that prints a prompt and reads a line private native String getLine(String prompt); public static void main(String args[]) { Prompt p = new Prompt(); String input = p.getLine("Type a line: "); System.out.println("User typed: " +input); } static { System.loadLibrary("Prompt"); } }在这个例子中,我们要实现一个native方法 String getLine(String prompt); 读入一个String参数,返回一个String值。通过执行javah -jni得到的头文件是这样的#include<jni.h> #ifndef_Included_Prompt #define_Included_Prompt #ifdef __cplusplus extern"C" { #endif JNIEXPORT jstringJNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt); #ifdef __cplusplus } #endif #endifjstring是JNI中对应于String的类型,但是和基本类型不同的是,jstring不能直接当作C++的string用。如果你用 cout << prompt << endl; 编译器肯定会扔给你一个错误信息的。其实要处理jstring有很多种方式,这里只讲一种我认为最简单的方式,看下面这个例子,#include"Prompt.h" #include<iostream> JNIEXPORT jstringJNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) { const char* str; str = env->GetStringUTFChars(prompt, false); if(str == NULL) { return NULL; /* OutOfMemoryError already thrown */ } std::cout << str << std::endl; env->ReleaseStringUTFChars(prompt, str); char* tmpstr = "return string succeeded"; jstring rtstr = env->NewStringUTF(tmpstr); return rtstr; }在上面的例子中,作为参数的prompt不能直接被C++程序使用,先做了如下转换 str = env->GetStringUTFChars(prompt, false); 将jstring类型变成一个char*类型。返回的时候,要生成一个jstring类型的对象,也必须通过如下命令,
④ 请教JNI编程中C调用Java实现中NullPointerException问题
java 与 C++ 两种编程语言,它们之间的相互调用:
1、java 调用C++编写的dll,可使用JNI 或 Jawin 开源项目(推荐第二种方法)。
2、C++ 调用java 的变量、方法,通过JNI (Java Native Interface)与java类交互。
----操作步骤(只总结第二个)-----
(1) vc6.0编译C++程序,开发环境设置:工具--》选项--》工具,工具标签下:选择“include files”,加入头文件目录:C:\Program Files\Java\jdk1.5.0\include 和 C:\Program Files\Java\jdk1.5.0\include\win32 ;选择“Libary files"下,加入LIB目录:C:\Program Files\Java\jdk1.5.0\lib 。会编译成exe文件。
执行程序环境设置: Path环境变量加入:C:\Program Files\Java\jdk1.5.0\jre\bin\client (jvm.dll所在目录),若不加入path会提示,执行时找不到jvm.dll.
(2)GetStaticMethodID(cls,"main","([Ljava/lang/String;)V");
//([Ljava/lang/String;)V 是main()签名
在java程序目录下执行:javap -s -p ClassDemo (注:ClassDemo.java 已经编译)
取main 下面的语句 :: Signature: ([Ljava/lang/String;)V
(3)附代码示例:
java程序
import java.io.*;
public class DemoMain{
public static void main(String[] args) throws java.io.IOException, java.lang.NullPointerException
{
System.out.println("This is a test.");
}
}
C++程序:
#ifndef __cplusplus
#define __cplusplus
#endif
#include "jni.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#pragma comment (lib,"C:\\Program Files\\Java\\jdk1.5.0\\lib\\jvm.lib") // 动态调用lib
#pragma warning(disable: 4129) // 关闭 warning, 4129
void main() {
LoadLibrary("C:\\Program Files\\Java\jre1.5.0\\bin\\client\\jvm.dll"); // 动态调用dll
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.classpath=.";
options[2].optionString = "-verbose:jni";
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
jint res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); // 创建虚拟机
if (res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
};
jclass cls = env->FindClass("DemoMain");
if (cls == 0) printf("Sorry, I can't find the class");
fprintf(stdout, "This is invokeSimplified4.\n");
jmethodID get_main_id;
if(cls != NULL)
{
get_main_id =env->GetStaticMethodID(cls,"main","([Ljava/lang/String;)V");
fprintf(stdout, "This is invokeSimplified5.\n");
if(get_main_id != NULL )
{
jclass string = env->FindClass("java/lang/String");
jobjectArray args = env->NewObjectArray(0,string, NULL);
fprintf(stdout, "This is invokeSimplified6.\n");
int i = env->CallIntMethod(cls, get_main_id, args);
fprintf(stdout, i+ "This is invokeSimplified7.\n");
}
}
jvm->DestroyJavaVM();
fprintf(stdout, "Java VM destory\n");
}
⑤ 如何在JNI编程中使用logCat
在android编程java代码中,我们知道,可以使用Log.v等一些将日志输出到logcat,然后我们就可以看到日志输出信息。当然,也可以在外壳里使用亚行 NDK是否支持了。以往,在JNI编程中,调试本地接口方法比较困难,往往都是采用打日志的方式将日志输出到文件。今天,在目录
/build/platforms/android-8/arch-arm/usr/include/android/log.h
下发现的Android NDK提供的头文件,打开瞧瞧
[CPP]鉴于plain
/ *
*版权所有(C)2009年的Android开源项目
*
* Apache许可证下授权,版本2.0(以下简称“许可证”);
*您不能使用这个文件除了在遵守许可。
*您可以在获得许可证的副本
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*除非适用法律要求或书面同意,软件
*许可证下发布分布在一个“AS IS”的基础上,
*无担保或任何形式的条件,明示或暗示的保证。
*请参阅许可证特定语言的管理权限和
*许可证下的局限性。
* /
#ifndef的_ANDROID_LOG_H
#定义_ANDROID_LOG_H
/ ******************* *****************
*
*重要提示:
*
*这个文件是Android的一套稳定的系统头的一部分
*揭发了Android NDK(原生开发套件),因为
*平台1.5版
*
*第三方源代码和二进制代码依赖于定义
*这里被冻结在所有UPCOMING平台版本。
*
* - 不要修改ENUMS(除非您添加新的32位值)
* - 不要修改常数或功能宏
* - 请勿更改功能的签名ANY WAY
* - 请勿更改结构的布局和大小
* /
/ *
*支持例程将消息发送到了Android的内核日志缓冲区,
*可以在以后通过“logcat的”实用工具进行访问。
*
*每个日志消息必须有
* - 优先
* - 日志标签
* - 一些文本
*
*标记通常对应于发射所述日志消息的组件,
*并应相当小。
*
*日志消息文本可能会被截断,以不到一个特定实现
*限制(如1023字符)。
*
*请注意,换行符(“/ N”)将被自动添加到您的
*日志消息,如果不是已经存在。这是不可能的发送几个消息
*,并让它们出现在logcat中一行。
*
*请使用日志切勿过量:
*
* - 发送日志消息吃CPU和减慢你的应用程序和
*系统。
*
* - 圆形日志缓冲区是非常小(<64KB),发送大量邮件
*可能会蹬从系统的其余部分等重要日志消息。
*
* - 在发布版本,只发送日志消息来解释例外
*条件。
*
*注意:这些功能必须由/system/lib/liblog.so实施
* /
的#include <STDARG.H>
的#ifdef __cplusplus
外部的“C”{
#ENDIF
/ *
* Android的日志优先级值,以升序的优先顺序。
* /
的typedef枚举android_LogPriority {
ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT,/ *仅供SetMinPriority()* /
ANDROID_LOG_VERBOSE,
ANDROID_LOG_DEBUG,
ANDROID_LOG_INFO,
ANDROID_LOG_WARN,
ANDROID_LOG_ERROR,
ANDROID_LOG_FATAL,
ANDROID_LOG_SILENT,/ *仅供SetMinPriority();必须是最后一个* /
} android_LogPriority;
/ *
*发送一个简单的字符串日志。
* /
INT __android_log_write(INT PRIO,为const char *标记,为const char *文本);
/ *
*发送一个格式化字符串到日志,使用如printf(FMT,...)
* /
INT __android_log_print(INT PRIO,为const char *标记,为const char * FMT,...)
#如果定义(__ GNUC__)
__attribute__((格式(printf的,3,4)))
#ENDIF
;
/ *
* __android_log_print(的一种变体),需要一个va_list的列表
*额外的参数。
* /
INT __android_log_vprint(INT PRIO,为const char *标记,
为const char * FMT,va_list的AP);
/ *
*登录断言失败和SIGTRAP过程中有机会
*检查它,如果一个调试器附加。它使用了致命的优先级。
* /
无效__android_log_assert(为const char * COND,为const char *标记,
为const char * FMT,...)
#如果定义(__ GNUC__)
__attribute__((不返回的))
__attribute__((格式(printf的,3,4)))
#ENDIF
;
的#ifdef __cplusplus
}
#ENDIF
#ENDIF / * * _ANDROID_LOG_H /
请仔细阅读这个头文件,我们会发现,Android的NDK完全支持JNI本地方法调试。它提供4个函数供我们使用,如下
[CPP]鉴于plain
/ *
*发送一个简单的字符串日志。
* /
INT __android_log_write(INT PRIO,为const char *标记,为const char *文本);
/ *
*发送一个格式化字符串到日志,使用如printf(FMT,...)
* /
INT __android_log_print(INT PRIO,为const char *标记,为const char * FMT,...)
/ *
* __android_log_print(的一种变体),需要一个va_list的列表
*额外的参数。
* /
INT __android_log_vprint(INT PRIO,为const char *标记,
为const char * FMT,va_list的AP);
/ *
*登录断言失败和SIGTRAP过程中有机会
*检查它,如果一个调试器附加。它使用了致命的优先级。
* /
无效__android_log_assert(为const char * COND,为const char *标记,
为const char * FMT,...)
我们可以将本地方法调试信息发送到logcat里。(是不是很爽,以后调试本地方法就不用那么纠结了^_^)
要使用这几个函数,就必须在本地文件中加入如下包含语句
的#include <机器人/ log.h>
⑥ Android中怎么通过JNI编程去发送adb指令控制手机
编写一个C程序,使用system(“cmd
line") ,直接调用命令行程序即可。更复杂调用可以用execv 之类系统调用
⑦ jni编程错误: Exception in thread main java.lang.UnsatisfiedLinkError:Test.print(Ljava/lang/String)
java这方面没有什么问题,你要把你的c/c++的代码贴出来才好分析原因
JNIEXPORT jstring JNICALL Java_com_it_Test_print (JNIEnv * env, jobject jobj, jstring string){
const char * test=env->GetStringUTFChars(string,NULL);
cout<<test<<endl;
env->ReleaseStringUTFChars(string,test);
}
⑧ 如何在Android JNI编程中实现对信号量的操作
可以。实现JNI原生函数源文件: 新建com_lucyfyr_HelloWorld.c文件: #include #define LOG_TAG "HelloWorld" #include /* Native interface, it will be call in java code */ JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI...