首页 > 其他 > 详细

继续研究NDK

时间:2015-12-05 11:15:53      阅读:250      评论:0      收藏:0      [点我收藏+]

继续研究NDK                     

jni.h provides a thin C++ wrapper around the C API; the underlying calls are language-agnostic.基础调用是语言无关的。

wiki:

技术分享

readelf查看ELF文件格式:

技术分享

ELF文件格式

ProgramHeaders:

技术分享

ELF函数头

ABI(application binary interface)

  • 数据类型的大小、布局和对齐;
  • 调用约定(控制着函数的参数如何传送以及如何接受返回值),例如,是所有的参数都通过栈传递,还是部分参数通过寄存器传递;哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先push到栈上还是最后;
  • 系统调用的编码和一个应用如何向操作系统进行系统调用;

 

来自 <http://baike.baidu.com/link?url=49-mYyGuqI687pINMMP-e2dU-vYp2wcQYdr_rhnoydZy881DUdRVgcVz2AM3cr5e5zCh58dbhV46jIkWwvW5AJPnFo0JvqMuqI2TJtGmd73>

 

Jni:ndk-build hello-jni.c

android-arch

技术分享

android.mk

技术分享

Generate libhello-jni.so

Application.mk

技术分享

Target all platform such as arm-v7\x86\ ...

技术分享

 

 

Add a mudule:LOCAL_LDLIBS += -lsth

JNI函数声明:

JNIEXPORT <return> JNICALL Java_<package>_<class>_<function>(JNIEnv* env,jobject,<Args>)

例如:

JNIEXPORT void JNICALL Java_com_Cartoonifier_CartoonifierView_ShowPreview(

JNIEnv* env,jobject ,jint width,jint height,jbyteArray yuv,jintArray bgra){ }

 

JNIEnv:

native程序中频繁使用JNIEnv*和JavaVM*。而C和C++代码使用JNIEnv*和JavaVM*这两个指针的做法是有区别的,网上大部分代码都使用C++,基本上找不到关于C和C++在这个问题上的详细叙述。

在C中:

使用JNIEnv* env要这样      (*env)->方法名(env,参数列表)

使用JavaVM* vm要这样       (*vm)->方法名(vm,参数列表)

在C++中:

使用JNIEnv* env要这样      env->方法名(参数列表)

使用JavaVM* vm要这样       vm->方法名(参数列表)

JNIEnv struct:

 

技术分享

 

技术分享

本地方法不能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv

 

Java通过JNI机制调用c/c++写的native程序。c/c++开发的native程序需要遵循一定的JNI规范,下面的例子就是一个JNI函数声明:

JNIEXPORT jint JNICALL Java_jnitest_MyTest_test

   (JNIEnv * env, jobject obj, jint arg0);

JVM负责从Java Stack转入C/C++ Native Stack。当Java进入JNI调用,除了函数本身的参数(arg0),会多出两个参数:JNIEnv指针和jobject指针。

JNIEnv指针是JVM创建的,用于Nativec/c++方法操纵Java执行栈中的数据,比如Java Class, Java Method等。

首先,JNI对于JNIEnv的使用, 提供了两种语法: c语法以及c++语法,如下:

c语法:

jsize len = (*env)->GetArrayLength(env,array);

c++语法:

jsize len =env->GetArrayLength(array);

(注:由于C语言并不支持对象的概念,所以C语法中需要把env作为第一个参数传入,类似于C++的隐式参数this指针).

另外: JNIEnv有几个设计的原则:

第一、JNIEnv指针被设计成了Thread Local Storage(TLS)变量,也就是说每一个Thread, JNIEnv变量都有独立的Copy。你不能把Thead#1使用的JNIEnv传给Thread#2使用。

 

第二、JNIEnv中定义了一组函数指针,c/c++ Native程序是通过这些函数指针操纵Java数据。这样设计的好处是:你的c/c++ 程序不需要依赖任何函数库,或者DLL。由于JVM可能由不同的厂商实现,不同厂商有自己不同的JNI实现,如果要求这些厂商暴露约定好的一些头文件和库,这不是灵活的设计。

而且使用函数指针表的另外一个好处是: JVM可以根据启动参数动态替换JNI实现。

需要强调的是JNIEnv是跟线程相关的。

 

调用JavaVM接口:

第一种方式,在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。

第二种方式,在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。两种情况下,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。Android系统是利用第二种方式Invocation interface来创建JVM的。

/*

JavaVM *jvm=0;

JNIEnv *env=(JNIEnv *)p;

env->GetJavaVM(&jvm);

*/

 

调用JNIEnv接口:

native method中,JNIEnv作为第一个参数传入。那么在JNIEnv不作为参数传入的时候,该如何获得它

JNI提供了两个函数:

  *jvm)->AttachCurrentThread(jvm, (void**)&env, NULL)

  (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)

两个函数都利用JavaVM接口获得JNIEnv接口,上面已经讲到如何获得JavaVM接口。JNI规范也说明,可以将获得JNIEnv封装成一个函数。

JNIEnv* JNU_GetEnv()

{

    JNIEnv* env;

    (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_2);//C风格

//jint result = g_jvm->GetEnv((void **) &env,JNI_VERSION_1_2);

    return env;

}

技术分享

JNI_VERSION

下面是来自stackoverflow的一段代码:

#include <jni.h>

#include <iostream>

#include <pthread.h>

using namespace std;

JNIEnv* getEnv(JavaVM *jvm)

    {

        JNIEnv *env = 0;

        jint result = jvm->GetEnv((void **) &env, JNI_VERSION_1_6);//C++

        if (result != JNI_OK)

        {

技术分享

JavaVM

            result = jvm->AttachCurrentThread((void **) &env, NULL);

//struct JNIInvokeInterface

            if (result != JNI_OK)

            {

                cout << "Failed to attach current thread " << pthread_self() << endl;

            }

            else

            {

                cout << "Successfully attached native thread " << pthread_self() << endl;

            }

 

            // ...and register for detach when thread exits

            int result = pthread_setspecific(key, (void *) env);

            if (result != 0)

            {

                cout << "Problem registering for detach" << endl;

            }

            else

            {

                cout << "Successfully registered for detach" << endl;

            }

        }

 

        return env;

    }

static pthread_key_t key;

static pthread_once_t key_once;

 

NDK platform/android-19/*

技术分享

NDK.inlcude\c\c++

JNIEnv使用方法:

参数声明 ...jintArray bgra,jbyteArray yuv...

/*......*/

jbyte* _yuv=env->GetByteArrayElements(yuv,0);//JVM中获取图像数据

jint* _bgra=env->GetIntArrayElements(bgra,0);

/*......*/

env->ReleaseIntArrayElement(bgra,_bgra,0);

env->ReleaseByteArrayElements(yuv,_yuv,0);

问题是:从这里看到指向的是像素缓冲区,也就是java的对象数据,那么就不是copy,那么多线程调用TLS是怎么实现的?指针发生了竞争怎么办?还是就是拷贝,如果是内存拷贝又是怎么改变原实例的数据的。

 

引用:

SWIG (Simplified Wrapper and Interface Generator)

Tagline: SWIG is a compiler that integrates C and C++ with languages
         including Perl, Python, Tcl, Ruby, PHP, Java, C#, D, Go, Lua,
         Octave, R, Scheme (Guile, MzScheme/Racket, CHICKEN), Scilab,
         Ocaml, Modula-3, Common Lisp (CLISP, Allegro CL, CFFI, UFFI)
         and Pike. SWIG can also export its parse tree into XML and
         Lisp s-expressions.

 

来自 <https://github.com/swig/swig

继续研究NDK

原文:http://www.cnblogs.com/ypwen/p/5021112.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!