如何在android源码中添加自己的jni方法

如何在android源码中添加自己的jni方法,第1张

1,、 项目实现了一个简单的四则运算,项目的目录层次如下:

AndroidManifest.xml Android.mk jni res src

资源文件简简单单,一个布局文件,稍后会有demo的下载地址

主要记录备忘的内容如下:

MainActivity.Java

[html] view plain copypublic native int add(int x, int y)

public native int substraction(int x, int y)

public native float multiplication(int x, int y)

public native float division(int x, int y)

static{

System.loadLibrary("arithmetic")

}

2、生成lib的名称为libarithmetic.so.注意load的时候写"arithmetic"

jni 目录下有两个文件,一个是Android.mk,一个是c++源文件long.cpp

jni/Android.mk如下:

[html] view plain copyLOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE:= libarithmetic

LOCAL_SRC_FILES:= \

long.cpp

LOCAL_SHARED_LIBRARIES := \

libutils

LOCAL_STATIC_LIBRARIES :=

LOCAL_C_INCLUDES += \

$(JNI_H_INCLUDE)

LOCAL_CFLAGS +=

LOCAL_PRELINK_MODULE := false

include $(BUILD_SHARED_LIBRARY)

3、 注释:

LOCAL_PATH(必须定义,而且要第一个定义),宏函数‘my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk

file文件的目录)

include $( CLEAR_VARS),

CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE,

LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH

。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的

LOCAL_MODULE_TAGS :=user eng tests optional

user: 指该模块只在user版本下才编译

eng: 指该模块只在eng版本下才编译

tests: 指该模块只在tests版本下才编译

optional:指该模块在所有版本下都编译

LOCAL_MODULE(必须定义),标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。Note:编译系统会自动产生合适的前缀和后缀,例如:arithmetic编译成功后将生成libarithmetic.so库文件

LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中源代码文件。不用在这里列出头文件和包含文件。

LOCAL_SHARED_LIBRARIES中加入所需要链接的动态库(*.so)的名称

LOCAL_STATIC_LIBRARIES加入所需要链接的静态库(*.a)的名称

LOCAL_CFLAG可选的编译器选项,用法之一是定义宏,例如LOCAL_CFLAGS := -Werror作用是编译警告也作为错误信息

LOCAL_PRELINK_MODULE:=false,不作prelink处理,默认是要prelink *** 作的,有可能造成地址空间冲突(这地方目前还不明白)

long.cpp源代码如下:

[html] view plain copy#define LOG_TAG "LongTest2 long.cpp"

#include

#include

#include "jni.h"

jint add(JNIEnv *env, jobject thiz, jint x, jint y){

return x + y

}

jint substraction(JNIEnv *env, jobject thiz, jint x, jint y){

return x - y

}

jfloat multiplication(JNIEnv *env, jobject thiz, jint x, jint y){

return (float)x * (float)y

}

jfloat division(JNIEnv *env, jobject thiz, jint x, jint y){

return (float)x/(float)y

}

static const char *classPathName = "com/inspur/test2/MainActivity"

static JNINativeMethod methods[]= {

{"add", "(II)I", (void*)add},

{"substraction", "(II)I", (void*)substraction},

{"multiplication", "(II)F", (void*)multiplication},

{"division", "(II)F", (void*)division},

}

typedef union{

JNIEnv* env

void* venv

}UnionJNIEnvToVoid

static int registerNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, int numMethods){

jclass clazz

clazz = env->FindClass(className)

if (clazz == NULL)

return JNI_FALSE

if (env->RegisterNatives(clazz, gMethods, numMethods)<0)

return JNI_FALSE

return JNI_TRUE

}

static int registerNatives(JNIEnv *env){

if (!registerNativeMethods(env, classPathName,

methods, sizeof(methods)/sizeof(methods[0])))

{

return JNI_FALSE

}

return JNI_TRUE

}

jint JNI_OnLoad(JavaVM* vm, void* reserved){

UnionJNIEnvToVoid uenv

uenv.venv = NULL

jint result = -1

JNIEnv *env = NULL

if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK){

goto bail

}

env = uenv.env

env = uenv.env

if (registerNatives(env) != JNI_TRUE){

goto bail

}

result = JNI_VERSION_1_4

bail:

return result

}

除了利用 编写native JAVA类,通过javah生成.h文件,根据.h文件编写.c/cpp文件

方法外(名字像老太太的裹脚步,又臭又长,而且不灵活),Android还可以通过引用JNI_Onload方式实现。jint JNI_onLoad(JavaVM*

vm, void* reverced),改方法在so文件被加载时调用。

JNI_OnLoad()有两个重要的作用:

指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI

1.1版),如果要使用新版本的JNI,例如JNI

1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。

初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化 *** 作最为恰当。

JNI_OnUnload()的作用与JNI_OnLoad()对应,当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。

4、 项目根目录下Android.mk文件:

[html] view plain copyLOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_JNI_SHARED_LIBRARIES := libarithmetic

LOCAL_PACKAGE_NAME := LongTest

LOCAL_SHARED_LIBRARIES := \

libutils\

liblog

include $(BUILD_PACKAGE)

include $(LOCAL_PATH)/jni/Android.mk

# Also build all of the sub-targets under this one: the.shared library.

include $(call all-makefiles-under,$(LOCAL_PATH))

LOCAL_PACKAGE_NAME:项目名称,即最终生成apk的名字

LOCAL_JNI_SHARED_LIBRARIES := libxxx就是把so文件放到apk文件里的libs/armeabi里

执行BUILD_PACKAGE。它的定义也是在config.mk中定义如下:BUILD_PACKAGE:=

$(BUILD_SYSTEM)/package.mk

$(call all-java-files-under, src)编译的源代码文件列表添加src目录下所有的java 源文件

$(call all-makefiles-under, $(LOCAL_PATH))编译器会在编译完当前目录下的文件后再深入子目录编译

如果make过android源码,可以在项目根目录下执行mm命令进行编译。前提是执行过source

androidSRC/build/envsetup.sh

或者直接把source androidSRC/build/envsetup.sh添加到~/.bashrc中,会更加方便

android ndk开发经常遇到了动态库的问题,本文主要介绍:

① 动态链接库的生成;

② 在Java和C混合编程的情况下如何调用第三方动态链接库;

③ 使用dlopen程序运行时直接调用;

④ 纯c的方式开发调用;

本文重点推荐②和④,第③中太麻烦每个函数都需要dlsym调用一次;

工具/原料

Win8.1 x64

adt-bundle-windows-x86_64-20140702

android-ndk-r10d

生成动态库

1

android ndk下面生成动态库so文件的方法很多,但是这里只提供一种方法,更多的生成方法可以看,“ndk 编译静态库”:

2

fkAdd.c 的内容如下:

#include <jni.h>

int fkAdd(int nX, int nY)

{

return nX + nY

}

3

Android.mk 的内容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:= fkAdd

LOCAL_SRC_FILES:= fkAdd.c

include $(BUILD_SHRRED_LIBRARY)

4

1、打开 eclipse

2、点击 文件

3、点击 新建

4、点击 other...

5

1、展开 Android 选项;

2、选择 Android Project from Existing Code;

3、点击 Next

6

1、输入 Root Director;

2、取消 tests;

3、选中 Copy projects into workspace;

4、点击 Finish;

7

1、右键工程;

2、选择 Android Tools;

3、Add Native Support...;

8

点击 Finish

9

修改android sdk 版本为 4.0.3;

关于如何修改 android sdk 版本:

10

修改 Min SDK version:15

修改 Target SDK version:19

步骤阅读

11

在jni目录下面新建文件fkAdd.c 的内容如下:

int fkAdd(int nX, int nY)

{

return nX + nY

}

12

临时修改 Android.mk 文件内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#LOCAL_MODULE:= hello-jni

#LOCAL_SRC_FILES := hello-jni.c

LOCAL_MODULE:= fkaddso

LOCAL_SRC_FILES := fkAdd.c

include $(BUILD_SHARED_LIBRARY)

13

使用快捷键Ctrl+B编译后可以在libs目录下面看到生成的一些列的

libfkaddso.so文件,如下图所示

END

Java和c编程调so

1

1、将libs复制一份到jni目录下面,删掉其中不相关的文件

2、删掉文件 jni/fkadd.c 文件

3、将 Android.mk 文件还原成最开始的样子;

2

修改 hello-jni.c 中的部分代码,如下:

char szMsg[1024] = {0}

int nSum = fkAdd(100, 10)

sprintf (szMsg, "Hello from JNI ! Compiled with ABI " ABI ". %d ", nSum)

return (*env)->NewStringUTF(env, szMsg)

3

修改 Android.mk 文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := fkaddso

LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libfkaddso.so

include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE:= hello-jni

LOCAL_SRC_FILES := hello-jni.c

LOCAL_SHARED_LIBRARIES := fkaddso

include $(BUILD_SHARED_LIBRARY)

4

修改 HelloJni.java 在其中增加一行:

System.loadLibrary("fkaddso")

5

运行工程看效果:

END

用dlopen调用so

1

重复“生成动态库”中的过程1到10,

2

把需要调用so文件的目录libs拷贝到jni目录下面,并修改 Android.mk 文件的内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := fkaddso

LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libfkaddso.so

include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE:= hello-jni

LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

3

修改 hello-jni.c 的内容如下:

char* szSo = "/data/data/com.example.hellojni/lib/libfkaddso.so"

void* fkAddSo = dlopen(szSo, RTLD_LAZY)

int (*fpAdd)(int,int) = (int (*)(int,int))dlsym(fkAddSo, "fkAdd")

char szMsg[1024] = {0}

int nSum = fpAdd(100, 200)

dlclose(fkAddSo)

sprintf (szMsg, "%s %d", szSo, nSum)

return (*env)->NewStringUTF(env, szMsg)

4

关于如何获取 so在手机中的路径,可以通过在控制台下输入 adb shell 后,

在手机上查询:

5

编译后运行效果:

6

方便他人亦是方便自己,如果觉得还行就点下下边的投票吧,这样可以帮助其他人更快的找到解决问题的方法;有疑问的也可留言哦, 谢谢!

END

纯c的方式开发调用

1

2

根据“生成动态库”中的过程1到10,新建一个纯c的ndk程序:

“D:\Android\android-ndk-r10\samples\native-activity"

3

把会用的so文件的目录libs拷贝到 jni目录下面

4

修改 Android.mk 文件,内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := fkAdd

LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libfkAdd.so

include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE:= main

LOCAL_SRC_FILES := main.c

LOCAL_LDLIBS:= -llog -landroid -lEGL -lGLESv1_CM

LOCAL_STATIC_LIBRARIES := android_native_app_glue

LOCAL_SHARED_LIBRARIES := fkAdd

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE:= native-activity

LOCAL_SRC_FILES := NativeActivity.c

LOCAL_LDLIBS:= -llog -landroid

LOCAL_STATIC_LIBRARIES := android_native_app_glue

include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

5

增加 NativeActivity.c 文件,添加内容如下:

#include <jni.h>

#include <errno.h>

#include <dlfcn.h>

#include <android_native_app_glue.h>

void android_main(struct android_app* state) {

// Make sure glue isn't stripped.

app_dummy()

void* soAdd = dlopen("/data/data/com.example.native_activity/

lib/libfkAdd.so",RTLD_NOW)

void* soMain = dlopen("/data/data/com.example.native_activity/

lib/libmain.so",RTLD_NOW)

void (*fp_android_main)(struct android_app*) =

(void (*)(struct android_app*))dlsym(soMain,"android_main")

fp_android_main(state)

dlclose(soMain)

dlclose(soAdd)

}

6

在 main.c 文件中添加一行,方便测试:

__android_log_print(ANDROID_LOG_DEBUG, "fuke", "engine_handle_input 100 + 200 = [%d] ", fkAdd(100, 200))

7

编译运行,点击手机屏幕后,观察logcat 效果如下:

END

注意事项

c++的函数在写动态链接库的时候,需要注意的是c++的函数会被系统修改,所以做动态库测试的使用最好用c语言


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11181207.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-14
下一篇 2023-05-14

发表评论

登录后才能评论

评论列表(0条)

保存