【Android源码 栏目提醒】:网学会员为需要Android源码 的朋友们搜集整理了在Android中嵌入C语言程序 - 企业软件开发相关资料,希望对各位网友有所帮助!
情况处理在编写OCR软件时处理从camera得到的图片需要使用c编写的程序。
整个OCR的编码来自google开源代码软件名wordsnap。
在文件夹目录中有一个名为jni的文件夹下有mk文件c文件h头文件。
这些是什么呢
Android JNI知识简介 Java Native Interface JNI标准是java平台的一部分它允许Java代码和其他语言写的代码进行交互。
JNI 是本地编程接口它使得在 Java 虚拟机 VM 内部运行的 Java 代码能够与用其它编程语言如 C、C 和汇编语言编写的应用程序和库进行交互操作。
1.从如何载入.so档案谈起 由于
Android的应用层的类都是以Java写的这些Java类编译为Dex型式的Bytecode之后必须靠Dalvik虚拟机VM: Virtual Machine来执行。
VM在
Android平台里扮演很重要的角色。
此外在执行Java类的过程中如果Java类需要与C组件沟通时VM就会去载入C组件然后让Java的函数顺利地调用到C组件的函数。
此时VM扮演着桥梁的角色让Java与C组件能通过标准的JNI介面而相互沟通。
应用层的Java类是在虚拟机VM: Vitual Machine上执行的而C件不是在VM上执行那么Java程式又如何要求VM去载入Load所指定的C组件呢 可使用下述指令 System.loadLibrary.so的档案名 例如
Android框架里所提供的MediaPlayer.java类含指令 public class MediaPlayer static System.loadLibrarymedia_jni 这要求VM去载入
Android的/system/lib/libmedia_jni.so档案。
载入.so之后Java类与.so档案就汇合起来一起执行了。
2.如何撰写.so的入口函数 ---- JNI_OnLoad与JNI_OnUnload函数的用途 当
Android的VMVirtual Machine执行到System.loadLibrary函数时首先会去执行C组件里的JNI_OnLoad函数。
它的用途有二 1告诉VM此C组件使用那一个JNI版本。
如果你的.so档没有提供JNI_OnLoad函数VM会默认该.so档是使用最老的JNI 1.1版本。
由于新版的JNI做了许多扩充如果需要使用JNI的新版功能例如JNI 1.4的java.nio.ByteBuffer就必须藉由JNI_OnLoad函数来告知VM。
2由于VM执行到System.loadLibrary函数时就会立即先呼叫JNI_OnLoad所以C组件的开发者可以藉由JNI_OnLoad来进行C组件内的初期值之设定Initialization 。
例如在
Android的/system/lib/libmedia_jni.so档案里就提供了JNI_OnLoad函数其程式码片段为 //define LOG_NDEBUG 0 define LOG_TAG MediaPlayer-JNI jint JNI_OnLoadJavaVM vm void reserved JNIEnv env NULL jint result -1 if vm-GetEnvvoid env JNI_VERSION_1_4 JNI_OK LOGEERROR: GetEnv failedn goto bail assertenv NULL if register_
android_media_MediaPlayerenv 0 LOGEERROR: MediaPlayer native registration failedn goto bail if register_
android_media_MediaRecorderenv 0 LOGEERROR: MediaRecorder native registration failedn goto bail if register_
android_media_MediaScannerenv 0 LOGEERROR: MediaScanner native registration failedn goto bail if register_
android_media_MediaMetadataRetrieverenv 0 LOGEERROR: MediaMetadataRetriever native registration failedn goto bail / success -- return valid version number / result JNI_VERSION_1_4 bail: return result 此函数回传JNI_VERSION_1_4值给VM于是VM知道了其所使用的JNI版本了。
此外它也做了一些初期的动作可呼叫任何本地函数例如指令 if register_
android_media_MediaPlayerenv 0 LOGEERROR: MediaPlayer native registration failedn goto bail 就将此组件提供的各个本地函数Native Function登记到VM里以便能加快后续呼叫本地函数的效率。
JNI_OnUnload函数与JNI_OnLoad相对应的。
在载入C组件时会立即呼叫JNI_OnLoad来进行组件内的初期动作而当 VM释放该C组件时则会呼叫JNI_OnUnload函数来进行善后清除动作。
当VM呼叫JNI_OnLoad或JNI_Unload函数 时都会将VM的指针Pointer传递给它们其参数如下 jint JNI_OnLoadJavaVM vm void reserved jint JNI_OnUnloadJavaVM vm void reserved 在JNI_OnLoad函数里就透过VM之指标而取得JNIEnv之指标值并存入env指标变数里如下述指令 jint JNI_OnLoadJavaVM vm void reserved JNIEnv env NULL jint result -1 if vm-GetEnvvoid env JNI_VERSION_1_4 JNI_OK LOGEERROR: GetEnv failedn goto bail 由于VM通常是多执行绪Multi-threading的执行环境。
每一个执行绪在呼叫JNI_OnLoad时所传递进来的JNIEnv指标值 都是不同的。
为了配合这种多执行绪的环境C组件开发者在撰写本地函数时可藉由JNIEnv指标值之不同而避免执行绪的资料冲突问题才能确保所写的本 地函数能安全地在
Android的多执行绪VM里安全地执行。
基于这个理由当在呼叫C组件的函数时都会将JNIEnv指标值传递给它如下 jint JNI_OnLoadJavaVM vm void reserved JNIEnv env NULL if register_
android_media_MediaPlayerenv 0 这JNI_OnLoad呼叫register_
android_media_MediaPlayerenv函数时就将env指标值传递过去。
如 此在register_
android_media_MediaPlayer函数就能藉由该指标值而区别不同的执行绪以便化解资料冲突的问题。
例如在register_
android_media_MediaPlayer函数里可撰写下述指令 if env-MonitorEnterenv obj JNI_OK 查看是否已经有其他执行绪进入此物件如果没有此执行绪就进入该物件里执行了。
还有也可撰写下述指令 if env-MonitorExitenv obj JNI_OK 查看是否此执行绪正在此物件内执行如果是此执行绪就会立即离开。
3.registerNativeMethods函数的用途 应用层级的Java类别透过VM而呼叫到本地函数。
一般是仰赖VM去寻找.so里的本地函数。
如果需要连续呼叫很多次每次都需要寻找一遍会多花许多 时间。
此时组件开发者可以自行将本地函数向VM进行登记。
例如在
Android的/system/lib/libmedia_jni.so档案里的代 码段如下 //define LOG_NDEBUG 0 define LOG_TAG MediaPlayer-JNI static JNINativeMethod gMethods setDataSource Ljava/lang/StringV void
android_media_MediaPlayer_setDataSource setDataSource Ljava/io/FileDescriptorJJV void
android_media_MediaPlayer_setDataSourceFD prepare V void
android_media_MediaPlayer_prepare prepareAsync V void
android_media_MediaPlayer_prepareAsync _start V void
android_media_MediaPlayer_start _stop V void
android_media_MediaPlayer_stop getVideoWidth I void
android_media_MediaPlayer_getVideoWidth getVideoHeight I void
android_media_MediaPlayer_getVideoHeight seekTo IV void
android_media_MediaPlayer_seekTo _pause V void
android_media_MediaPlayer_pause isPlaying Z void
android_media_MediaPlayer_isPlaying getCurrentPosition I void
android_media_MediaPlayer_getCurrentPosition getDuration I void
android_media_MediaPlayer_getDuration _release V void
android_media_MediaPlayer_release _reset V void
android_media_MediaPlayer_reset setAudioStreamTypeIV void
android_media_MediaPlayer_setAudioStreamType setLooping ZV void
android_media_MediaPlayer_setLooping setVolume FFV void
android_media_MediaPlayer_setVolume getFrameAt ILandroid/graphics/Bitmap void
android_media_MediaPlayer_getFrameAt native_setup Ljava/lang/ObjectV void
android_media_MediaPlayer_native_setup native_finalize V void
android_media_MediaPlayer_native_finalize static int register_
android_media_MediaPlayerJNIEnv env return AndroidRuntime::registerNativeMethodsenv
android/media/MediaPlayer gMethods NELEMgMethods jint JNI_OnLoadJavaVM vm void reserved if register_
android_media_MediaPlayerenv 0 LOGEERROR: MediaPlayer native registration failedn goto bail 当VM载入libmedia_jni.so档案时就呼叫JNI_OnLoad函数。
接着JNI_OnLoad呼叫 register_
android_media_MediaPlayer函数。
此时就呼叫到 AndroidRuntime::registerNativeMethods函数向VM即AndroidRuntime登记 gMethods表格所含的本地函数了。
简而言之registerNativeMethods函数的用途有二 1更有效率去找到函数。
2可在执行期间进行抽换。
由于gMethods是一个对照表在程序执行时可多次呼叫registerNativeMethods函数来更换本地函数之指针而达到弹性抽换本地函数之目的。
4.Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。
其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组并在其中描述了函数的参数和返回值。
这个数组的类型是JNINativeMethod定义如下 typedef struct const char name /Java中函数的名字/ const char signature /描述了函数的参数和返回值/ void fnPtr /函数指针指向C函数/ JNINativeMethod 其中比较难以理解的是第二个参数例如 V IIV Ljava/lang/StringLjava/lang/StringV 实际上这些字符是与函数的参数类型一一对应的。
中的字符表示参数后面的则代表返回值。
例如V 就表示void Func IIV 表示 void Funcint int 具体的每一个字符的对应关系如下 字符 Java类型 C类型 V void void Z jboolean boolean I jint int J jlong long D jdouble double F jfloat float B jbyte byte C jchar char S jshort short 数组则以开始用两个字符表示 I jintArray int F jfloatArray float B jbyteArray byte C jcharArray char S jshortArray short D jdoubleArray double J jlongArray long Z jbooleanArray boolean 上面的都是基本类型。
如果Java函数的参数是class则以L开头以结尾中间是用/ 隔开的包及类名。
而其对应的C函数名的参数则为jobject. 一个例外是String类其对应的类为jstring Ljava/lang/String String jstring Ljava/net/Socket Socket jobject 如果JAVA函数位于一个嵌入类则用作为类名间的分隔符。
例如 Ljava/lang/StringLandroid/os/FileUtilsFileStatusZ
Android JNI编程实践 一、直接使用java本身jni接口windows/ubuntu 1.在Eclipsh中新建一个
android应用程序。
两个类一个继承于ActivityUI显示用。
另一个包含native方法。
编译生成所有类。
jnitest.java文件 package com.hello.jnitest import
android.app.Activity import
android.os.Bundle public class jnitest extends Activity / Called when the activity is first created. / Override public void onCreateBundle savedInstanceState super.onCreatesavedInstanceState setContentViewR.layout.main Nadd cal new Nadd setTitleThe Native Add Result is String.valueOfcal.nadd10 19 Nadd.java文件 package com.hello.jnitest public class Nadd static System.loadLibraryNadd public native int naddint a int b 以上在windows中完成。
2.使用javah命令生成C/C的.h文件。
注意类要包含包名路径文件夹下要包含所有包中的类否则会报找不到类的错误。
classpath参数指定到包名前一级文件夹文件夹层次结构要符合java类的组织层次结构。
javah -classpath ../jnitest/bin com.hello.jnitest.Nadd com_hello_jnitest_Nadd .h文件 / DO NOT EDIT THIS FILE - it is machine generated / include / Header for class com_hello_jnitest_Nadd / ifndef _Included_com_hello_jnitest_Nadd define _Included_com_hello_jnitest_Nadd ifdef __cplusplus extern C endif / Class: com_hello_jnitest_Nadd Method: nadd Signature: III / JNIEXPORT jint JNICALL Java_com_hello_jnitest_Nadd_nadd JNIEnv jobject jint jint ifdef __cplusplus endif endif 3.编辑.c文件实现native方法。
com_hello_jnitest_Nadd.c文件 include include com_hello_jnitest_Nadd.h JNIEXPORT jint JNICALL Java_com_hello_jnitest_Nadd_naddJNIEnv env jobject c jint a jint b return ab 4.编译.c文件生存动态库。
arm-none-linux-gnueabi-gcc -I/home/a/work/
android/jdk1.6.0_17/include -I/home/a/work/
android/jdk1.6.0_17/include/linux -fpic -c com_hello_jnitest_Nadd.c arm-none-linux-gnueabi-ld -T/home/a/CodeSourcery/Sourcery_G_Lite/arm-none-linux-gnueabi/lib/ldscripts/armelf_linux_eabi.xsc -share -o libNadd.so com_hello_jnitest_Nadd.o 得到libNadd.so文件。
以上在ubuntu中完成。
5.将相应的动态库文件push到avd的system/lib中:adb push libNadd.so /system/lib。
若提示Read-only file system错误运行adb remount命令即可。
Adb push libNadd.so /system/lib 6.在eclipsh中运行原应用程序即可。
以上在windows中完成。
对于一中生成的so文件也可采用二中的方法编译进apk包中。
只需在工程文件夹中建libsarmeabi文件夹其他文件夹名无效只建立libs文件夹也无效然后将so文件拷入编译工程即可。
二.使用NDK生成本地方法ubuntu and windows 1.安装NDK解压然后进入NDK解压后的目录运行build/host-setup.sh需要Make 3.81和awk。
若有错修改host-setup.sh文件将/bin/sh修改为/bin/bash再次运行即可。
2.在apps文件夹下建立自己的工程文件夹然后在该文件夹下建一文件Application.mk和项project文件夹。
Application.mk文件 APP_PROJECT_PATH : call my-dir/project APP_MODULES : myjni 3.在project文件夹下建一jni文件夹然后新建
Android.mk和myjni.c。
这里不需要用javah生成相应的.h文件但函数名要包含相应的完整的包、类名。
4.编辑相应文件内容。
Android.mk文件 Copyright C 2009 The
Android Open Source Project Licensed under the Apache License Version 2.0 the License you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing software distributed under the License is distributed on an AS IS BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. See the License for the specific language governing permissions and limitations under the License. LOCAL_PATH : call my-dir include CLEAR_VARS LOCAL_MODULE : myjni LOCAL_SRC_FILES : myjni.c include BUILD_SHARED_LIBRARY myjni.c文件 include include jstring Java_com_hello_NdkTest_NdkTest_stringFromJNI JNIEnv env jobject thiz return env-NewStringUTFenv Hello from My-JNI myjni文件组织 aubuntu:/work/
android/ndk-1.6_r1/apps tree myjni myjni -- Application.mk -- project -- jni --
Android.mk -- myjni.c -- libs -- armeabi -- libmyjni.so 4 directories 4 files 5.编译make APPmyjni. 以上内容在ubuntu完成。
以下内容在windows中完成。
当然也可以在ubuntu中完成。
6.在eclipsh中创建
android application。
将myjni中自动生成的libs文件夹拷贝到当前工程文件夹中编译运行即可。
NdkTest.java文件 package com.hello.NdkTest import
android.app.Activity import
android.os.Bundle import
android.widget.TextView public class NdkTest extends Activity / Called when the activity is first created. / Override public void onCreateBundle savedInstanceState super.onCreatesavedInstanceState TextView tv new TextViewthis tv.setText stringFromJNI setContentViewtv public native String stringFromJNI static System.loadLibrarymyjni 对于二中生成的so文件也可采用一中的方法push到avd中运行。
上一篇:
内核移植和Android文件系统
下一篇:
2018年汽车自驾运动营地攻防箭大赛总决赛圆满落幕