利用JNI来实现android与SO文件的交互.doc_第1页
利用JNI来实现android与SO文件的交互.doc_第2页
利用JNI来实现android与SO文件的交互.doc_第3页
利用JNI来实现android与SO文件的交互.doc_第4页
利用JNI来实现android与SO文件的交互.doc_第5页
免费预览已结束,剩余8页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

利用JNI来实现android与SO文件的交互。该文档主要演示了android中如何与so文件进行交互。即现在的问题需要硬件厂商重新开发动态库,并按照全网物联生成的.h文件进行开发动态库。1、 总体步骤为A、 写好native 本地java类B、 生成.h文件。C、 硬件厂商按.h文件开发动态库soD、 Android中采用system.loadLib(*.so)。E、 调用本地native方法。全网物联研发人员需要用Handler独立进程去调用,不然会有问题。F、 驱动硬件。2、 教程: JNI提供了一种扩展Android功能和移植已有软件的方式。本文将通过一个实例来讲述如何建立JNI库以及JNI库如何与android的JVM交互。Java接口 定义java类JNIExampleInterface, 该类提供了调用Native库中本地函数的接口。本地函数和对应的Java函数具有相互匹配的签名式(即,参数的类型和个数,以及返回值的类型)。获取本地库中对应的函数签名式的最简单的方法就是,首先写出对应的Java原型,然后使用javah工具生成对应的本地JNI头文件。可以copy/paste到C+文件中来实现对应的函数。 本地函数支撑的对应的Java函数按照正常方式去声明,但需要加上native。我们还想演示如何在native代码中调用Java代码,因此我们的接口类定义如下:package org.wooyd.android.JNIExample;import android.os.Handler;import android.os.Bundle;import android.os.Message;import org.wooyd.android.JNIExample.Data;public class JNIExampleInterface private Handler h; 为什么要定义Handler呢?当本地库需要通过callback传递信息给Java进程,如果这个callback是由本地线程调用的,并且想修改应用的用户界面,就会产生exception。这是因为Android仅仅允许主线程更改用户界面。为了避免这个问题,我们使用Handler提供的消息传递接口将callback接收到的数据传递给主线程,让主线程去更改界面。 public JNIExampleInterface(Handler h) this.h = h; 为了阐述不同的参数传递技术,我们定义了三个native函数:callVoid(): 没有参数并且没有返回值 ;getNewData(): 有两个参数,用来构造一个新的类的实例;getDataString(): 用对象作为参数,从对象中抽取值。public native void callVoid(); public native Data getNewData(int i, String s); public native String getDataString(Data d);callback接收一个string参数,并将其封装成Bundle后分发给Handler: public static void callBack(String s) Bundle b = new Bundle(); b.putString(callback_string, s); Message m = Message.obtain(); m.setData(b); m.setTarget(h); m.sendToTarget(); 另外我们定义一个Data dummy类Data.javapackage org.wooyd.android.JNIExample;public class Data public int i; public String s; public Data() public Data(int i, String s) this.i = i; this.s = s; 编译Data.java和JNIExampleInterface.java$ javac org/wooyd/android/JNIExample/*.java生成JNI头文件,包含与Java对应的本地函数的原型$ javah -classpath . org.wooyd.android.JNIExample.JNIExampleInterface本地库的实现,这个地方就需要全网物联把这个.H文件给硬件厂商了,硬件厂商需要按照这个文件开发动态库。函数名必须一致。#ifdef _cplusplusextern C #endif#ifdef _cplusplus#endifC中的头文件和全局变量下面的include包含了Android的JNI定义的函数: #include #include #include 一些其他要用到的头文件: #include #include #include static JavaVM *gJavaVM;static jobject gInterfaceObject, gDataObject;const char *kInterfacePath = org/wooyd/android/JNIExample/JNIExampleInterface;const char *kDataPath = org/wooyd/android/JNIExample/Data; 从本地线程和java线程调用java函数callVoid函数是最简单的一个,因为他没有任何参数并且没有返回值。我们用它来说明通过调用Java的callBack函数,如何将数据传回给Java。至此,我们有必要区分下面两种情况:Java函数可能在Java线程或本地线程中被调用,JVM是无法得知的。对于前者,调用可以直接执行,而对于后者,我们必须首先将本地线程关联到JVM中。因此需要一个附加层,本地回调句柄(Native CallBack Handler),来正确处理这两种情况。我们还需要一个建立本地线程的函数,因此实现如下:=Native callback handler获取JNI环境(如果需要,则关联本地线程),使用缓存的全局对象gInterfaceObject获取JNIExampleInterface类,获取callBack()函数的引用,并调用:static void callback_handler(char *s) int status; JNIEnv *env; bool isAttached = false; status = gJavaVM-GetEnv(void *) &env, JNI_VERSION_1_4); if(status AttachCurrentThread(&env, NULL); if(status NewStringUTF(s); jclass interfaceClass = env-GetObjectClass(gInterfaceObject); if(!interfaceClass) LOGE(callback_handler: failed to get class reference); if(isAttached) gJavaVM-DetachCurrentThread(); return; /* Find the callBack method ID */ jmethodID method = env-GetStaticMethodID( interfaceClass, callBack, (Ljava/lang/String;)V); if(!method) LOGE(callback_handler: failed to get method ID); if(isAttached) gJavaVM-DetachCurrentThread(); return; env-CallStaticVoidMethod(interfaceClass, method, js); if(isAttached) gJavaVM-DetachCurrentThread();说明:1、JNI GetEnv()函数返回的JNI环境对每个线程来说是独特的,因此我们必须在每次进入函数时都要重新获取。然而JavaVM指针是属于每个进程的,因此我们可以将其缓存起来(在JNI_OnLoad()函数中),在多个线程间使用。2、当我们关联本地线程时,其相关的Java环境是与类引导程序一起的,这就意味着即使我们要在函数中获取一个类的引用(通常使用JNI函数FindClass(),都将会引发一个exception。因此我们使用缓存的JNIExampleInterface对象去获取类的引用(有趣的是,我们不能缓存类引用本身,JVM认为这种引用在本地代码中是不可见的,因此任何试图使用它都会触发JVM产生exception)。3、为了获取callBack()的函数ID,我们需要指定其名称和JNI签名式。这里的签名式指出该函数需要一个java.lang.String对象作为参数,返回值为空。关于函数签名式的更多信息请参阅JNI文档,你可以使用javap工具去查询非本地函数的签名式(本地函数的签名式信息已经包含在javah产生的头文件中了)。 为了测试从本地线程中调用函数,我们需要一个在另一个独立线程中运行的函数。它唯一的任务就是调用callback handler: =void *native_thread_start(void *arg) sleep(1); callback_handler(char *) Called from native thread);现在我们已经有了实现callVoid()函数的所有本地部分的代码: /* * Class: org_wooyd_android_JNIExample_JNIExampleInterface * Method: callVoid * Signature: ()V */JNIEXPORT void JNICALL Java_org_wooyd_android_JNIExample_JNIExampleInterface callVoid (JNIEnv *env, jclass cls) pthread_t native_thread; callback_handler(char *) Called from Java thread); if(pthread_create(&native_thread, NULL, native_thread_start, NULL) LOGE(callVoid: failed to create a native thread); 实现其他本地函数getNewData()函数描述了如何在本地库中建立一个新的Java对象,并返回给调用者。为了获取类并创建其实例,我们再次使用缓存的Data对象。=/* * Class: org_wooyd_android_JNIExample_JNIExampleInterface * Method: getNewData * Signature: (ILjava/lang/String;)Lorg/wooyd/android/JNIExample/Data; */JNIEXPORT jobject JNICALL Java_org_wooyd_android_JNIExample_JNIExampleInterface_getNewData (JNIEnv *env, jclass cls, jint i, jstring s) jclass dataClass = env-GetObjectClass(gDataObject); if(!dataClass) LOGE(getNewData: failed to get class reference); return NULL; jmethodID dataConstructor = env-GetMethodID( dataClass, , (ILjava/lang/String;)V); if(!dataConstructor) LOGE(getNewData: failed to get method ID); return NULL; jobject dataObject = env-NewObject(dataClass, dataConstructor, i, s); if(!dataObject) LOGE(getNewData: failed to create an object); return NULL; return dataObject;getDataString()函数描述了如何在本地函数中获取对象属性值。=/* * Class: org_wooyd_android_JNIExample_JNIExampleInterface * Method: getDataString * Signature: (Lorg/wooyd/android/JNIExample/Data;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_org_wooyd_android_JNIExample_JNIExampleInterface_getDataString (JNIEnv *env, jclass cls, jobject dataObject) jclass dataClass = env-GetObjectClass(gDataObject); if(!dataClass) LOGE(getDataString: failed to get class reference); return NULL; jfieldID dataStringField = env-GetFieldID( dataClass, s, Ljava/lang/String;); if(!dataStringField) LOGE(getDataString: failed to get field ID); return NULL; jstring dataStringValue = (jstring) env-GetObjectField( dataObject, dataStringField); return dataStringValue; JNI_OnLoad()函数的实现为了让JNI可以和Android JVM一起工作,必须提供JNI_OnLoad()函数。在本地库被装入到JVM时会调用该函数。之前我们已经提及许多任务要在该函数中完成,如:缓存全局JavaVM指针和对象实例。另外,我们需要在Java中调用的任何本地函数都必须注册,否则Android JVM将无法解析它们。具体函数实现如下:=jint JNI_OnLoad(JavaVM* vm, void* reserved)JNIEnv *env;gJavaVM = vm;LOGI(JNI_OnLoad called);if (vm-GetEnv(void*) &env, JNI_VERSION_1_4) != JNI_OK) LOGE(Failed to get the environment using GetEnv();return -1;return JNI_VERSION_1_4;由于本地线程无法存取functional classloader,所以我们需要将类引用缓存起来。正如之前所述,我们不能缓存类引用本身,我们缓存这些类的实例,之后我们可以通过GetObjectClass()JNI函数来获取类引用。我们要记住的一点就是我们必须使用NewGlobalRef()函数将这些对象保护起来,以免被GC回收,这样就可以在JVM的整个生命周期内的任何线程中使用。建立实例并将其保存到全局变量是函数initClassHelper()的工作:=void initClassHelper(JNIEnv *env, const char *path, jobject *objptr) jclass cls = env-FindClass(path); if(!cls) LOGE(initClassHelper: failed to get %s class reference, path); return; jmethodID constr = env-GetMethodID(cls, , ()V); if(!constr) LOGE(initClassHelper: failed to get %s constructor, path); return; jobject obj = env-NewObject(cls, constr); if(!obj) LOGE(initClassHelper: failed to create a %s object, path); return; (*objptr) = env-NewGlobalRef(obj);定义了这个函数,缓存类实例就是小菜一碟了 initClassHelper(env, kInterfacePath, &gInterfaceObject); initClassHelper(env, kDataPath, &gDataObject);为了注册本地函数,我们建立一个JNINativeMethod结构的数组,该结构包含函数名称、签名式(可以从javah产生的注释中拷贝)、实现函数的指针。然后将这个数组传递给Android的registerNativeMethods()函数: JNINativeMethod methods = callVoid, ()V, (void *) Java_org_wooyd_android_JNIExample_JNIExampleInterface_callVoid , getNewData, (ILjava/lang/String;)Lorg/wooyd/android/JNIExample/Data;, (void *) Java_org_wooyd_android_JNIExample_JNIExampleInterface_getNewData , getDataString, (Lorg/wooyd/android/JNIExample/Data;)Ljava/lang/String;, (void *) Java_org_wooyd_android_JNIExample_JNIExampleInterface_getDataString ; if(android:AndroidRuntime:registerNativeMethods( env, kInterfacePath, methods, NELEM(methods) != JNI_OK) LOGE(Failed to register native methods); return -1; 编译本地库这里仅介绍使用Android.mk编译本地库的方法,你必须先现在Android的整个源码。为你的本地库建立一个目录,如:/path/to/android/source/code/vendor/your/sample。将Native的源码文件放到该目录下,并建立Android.mk文件,内容如下:# This makefile supplies the rules for building a library of JNI code for# use by our example platform shared library.LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optional# This is the target being built.LOCAL_MODULE:= libjniexample# All of the source files that we will compile.LOCAL_SRC_FILES:= JNIExample.cpp# All of the shared libraries we link against.LOCAL_SHARED_LIBRARIES := libandroid_runtime libnativehelper libcutils libutils# No static libraries.LOCAL_STATIC_LIBRARIES :=# Include C headers#LOCAL_C_INCLUDES+= # $(call include-path-for, dbus)#LOCAL_C_INCLUDES +=# external/freetype/include # Also need the JNI headers.LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)# No specia compiler flags.LOCAL_CFLAGS +=# Dont prelink this library. For more efficient code, you may want# to add this library to the prelink map and set this to true.LOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)在Java代码中使用本地函数我们将建立一个简单的activity,来使用JNI函数。我们唯一要做的就是在activity的onCreate()函数中load本地JNI库,使得其中定义的函数在Java中可用。整体结构如下: =package org.wooyd.android.JNIExample;import android.app.Activity;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.os.Bundle;import android.os.Handler;import android.os.Message;import org.wooyd.android.JNIExample.JNIExampleInterface;import org.wooyd.android.JNIExample.Data;import android.util.Log;public class JNIExample extends Activity TextView callVoidText, getNewDataText, getDataStringText; Button callVoidButton, getNewDataButton, getDataStringButton; Handler callbackHandler; JNIExampleInterface jniInterface; Override public void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.main); 首先使用System.loadLibrar安装库。try System.loadLibrary(libjniexample.so; catch (Exception ex) Log.e(JNIExample, failed to install native library: + ex); 接下来就是调用本地库函数并显示结果。为了演示callVoid(),我们必须先初始化一个handler,并将其传给JNI接口类,以使得我们能接受到callback消息: callVoidText = (TextView) findViewById(R.id.callVoid_text); callbackHandler = new Handler() public void handleMessage(Message msg) Bundle b = msg.getData(); callVoidText.se

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论