Android 4.4.2 动态添加JNI库方法记录 (一 JNI库层)

欢迎转载,但必须注明出处。http://blog.csdn.net/wang_shuai_ww/article/details/44456755

本篇是继《s5p4418 Android 4.4.2 驱动层 HAL层 服务层
应用层 开发流程记录
》之后的另外一种添加JNI和服务的方法。

前面的方法是直接把HAL和服务层添加到了,Android的api中,这样的方式好处是操作系统已开发完成,剩下做APP的开发,那么我们只需要一个classes.jar文件即可使用我们自己Android系统的被隐藏的PI了(在Android官方的sdk中没有的API和用户自己添加的API),在运行自己Android系统的平台可直接方便的测试。当然也可以做出自己的android.jar文件放到sdk的platforms对应的android版本中,替换之前的android.jar,那么以后选择该目标Android版本时,加载的就是我们自己的Android系统的API了,可以像android.os.xxx这样调用我们的接口。

本篇介绍的是动态加载JNI的方式,顾名思义,就是在apk运行的时候去加载我们写好的.so JNI库。本方法灵活,开发移植使用也很方便,也不用在源码树中进入framework等目录进行各种繁琐的操作,只需要写一个满足要求的.c文件,编译生成对应的.so文件就行了,而且对于存放的位置没有什么特别要求,我一般放在对应的板级目录下(/device/nexell/realarm)。

看起来比直接添加到Android的api中要方便多了,最起码简洁多了,没那么繁琐,只是该方法在eclipse中需要多建立一个类文件,不过也没什么大不了的,O(∩_∩)O。对于想快速学习,添加自己的led操作的朋友来说,本篇的方法非常合适。

对于动态JNI,首先引用 三篇博文,地址是http://blog.sina.com.cn/s/blog_4c451e0e0101339i.html、http://www.cnblogs.com/simonshi/archive/2011/01/25/1944910.html、http://blog.csdn.net/happy08god/article/details/11405607。朋友们可以多读读,看看他们介绍的相同和不同之处。

本篇文章是在参考多篇博文之后自己编写代码在开发板上测试通过后记录写下的,大家可以参考代码写出自己的程序。然后对比与其他博文的不同之处。

我的源码目录是/device/nexell/realarm/led2,在该目录下有两个文件led2.c和Android.mk。它们的源码如下:

led2.c:

#include <stdio.h>  

#include "jni.h"
#include "JNIHelp.h"
#include <assert.h> 

// 引入log头文件
#include <android/log.h>
// log标签
#define  TAG    "Led_Load_JNI"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

#define DEVICE_NAME		"/dev/real_led"

<span style="color:#ff0000;">#define NO_REGISTER</span>

<span style="color:#ff0000;">#ifndef NO_REGISTER</span>
static jint Java_realarm_hardware_HardwareControl_LedSetState
  	(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)
<span style="color:#ff0000;">#else</span>
JNIEXPORT jint JNICALL Java_realarm_hardware_HardwareControl_LedSetState
	(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)
<span style="color:#ff0000;">#endif</span>
{
	int fd = open(DEVICE_NAME, 0);

	if (fd == -1)
	{
		LOGE("led open error");
		return 1;
	}
	LOGD("Led open success");
	ledState &= 0x01;
	ioctl(fd, ledState, 0);

	close(fd);
	return 0;
}
<span style="color:#ff0000;">#ifndef NO_REGISTER</span>
static JNINativeMethod gMethods[] = {
	{"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState},
}; 

static int register_android_test_led(JNIEnv *env)
{
    jclass clazz;
    static const char* const kClassName =  "realarm/hardware/HardwareControl";

    /* look up the class */
    clazz = (*env)->FindClass(env, kClassName);
    <span style="color:#3333ff;">//clazz = env->FindClass(env,kClassBoa);</span>
    if (clazz == NULL) {
        LOGE("Can't find class %s\n", kClassName);
        return -1;
    }

    /* register all the methods */
    if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
    <span style="color:#3333ff;">//if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)</span>
    {
        LOGE("Failed registering methods for %s\n", kClassName);
        return -1;
    }

    /* fill out the rest of the ID cache */
    return 0;
}
<span style="color:#ff0000;">#endif</span>
jint JNI_OnLoad(JavaVM* vm, void* reserved) {

<span style="color:#ff0000;">#ifndef NO_REGISTER</span>
	JNIEnv *env = NULL;
	if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
	<span style="color:#3333ff;">//if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { </span><span style="color:#3366ff;"> </span>
		LOGI("Error GetEnv\n");
		return -1;
	}
	assert(env != NULL);
	if (register_android_test_led(env) < 0) {
		printf("register_android_test_led error.\n");
		return -1;
	}
<span style="color:#ff0000;">#endif</span>
    /* success -- return valid version number */
	LOGI("/*****************realarm**********************/");

    return JNI_VERSION_1_4;
}

本代码在了解其原理后做了一些处理,本代码可以使用两个方式来最终引用LedSetState的C/C++实现,具体的控制,就是上面代码的红色部分的宏。

1.如果定义了上面的宏,那么JNI_OnLoad函数里面有用的就是最后一句话,return JNI_VERSION_1_4;返回JNI版本,并没有对LedSetState的C/C++实现函数进行注册,那么Android又怎么识别呢。如果是这种情况的话,Android会根据java层对JNI引用的类里面的native定义自动搜索对应的JNI方法,那么这就对函数定义的名字有要求了,格式为java_包名_类名_函数名字,Android的app在调用HardwareControl类里面的LedSetState方法时,就会自动匹配到对应的JNI方法。这种方法有个缺点,就是需要消耗CPU资源去匹配函数,导致运行效率不高,当程序大时就麻烦了。

2.因此推荐使用不定义上面红色的宏的方式,这样的话,在调用这个库时,就会把LedSetState函数通过(*env)->RegisterNatives这个注册,把它与Java_realarm_hardware_HardwareControl_LedSetState绑定在一起,而apk在调用LedSetState方法时执行效率就高多了,不用去自动匹配了,因为已经明白告诉操作系统该调用哪个函数了。

Android.mk:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := led2.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
LOCAL_MODULE := libLedJni

include $(BUILD_SHARED_LIBRARY)

从上面代码可知道,我编译生成的库名为libLedJni.so,注意LOCAL_MODULE_PATH要使用上图所示的路径,也就是/system/lib目录,在动态加载时,也是去这个目录下寻找。

好,到这里JNI库就完成了,我们使用adb push命令把它发到开发板的/system/lib目录下,在下一篇将介绍怎么使用它。

时间: 2024-07-29 06:27:41

Android 4.4.2 动态添加JNI库方法记录 (一 JNI库层)的相关文章

android 在布局中动态添加控件

第一步 Java代码 final LayoutInflater inflater = LayoutInflater.from(this); 第二步:获取需要被添加控件的布局 Java代码 final LinearLayout lin = (LinearLayout) findViewById(R.id.LinearLayout01); 第三步:获取需要添加的布局(控件) Java代码 LinearLayout layout = (LinearLayout) inflater.inflate( R

Android在布局中动态添加view的两种方法

一.说明 添加视图文件的时候有两种方式:1.通过在xml文件定义layout:2.java代码编写 二.前言说明 1.构造xml文件 2.LayoutInflater 提到addview,首先要了解一下LayoutInflater类.这个类最主要的功能就是实现将xml表述的layout转化为View的功能.为了便于理解,我们可以将它与findViewById()作一比较,二者都是实例化某一对象,不同的是findViewById()是找xml布局文件下的具体widget控件实例化,而LayoutI

python3 - 动态添加属性以及方法

给实例动态添加方法,需引入types模块,用其的MethodType(要绑定的方法名,实例对象)来进行绑定:给类绑定属性和方法,可以通过 实例名.方法名(属性名) = 方法名(属性值) 来进行绑定.给类添加方法,通过@classmethod:给类添加静态方法通过@staticmethod import types #定义了一个类class Person(object): num = 0 def __init__(self, name = None, age = None): self.name

JS内置对象的原型不能重定义?只能动态添加属性或方法?

昨天马上就快下班了,坐在我对面的同事突然问我一个问题,我说“爱过”,哈哈,开个玩笑.情况是这样的,他发现JS的内置对象的原型好像不能通过字面量对象的形式进行覆盖, 只能动态的为内置对象的原型添加属性或方法,下面那个具体的例子说明: var arr=[]; Array.prototype={ push:function(){ alert("1"); } }; arr.push(); //没有任何输出 有人可能会说了“你先定义的arr,后来又修改了Array.prototype,这时Arr

python类对象动态添加属性和方法

class Person(): #创建一个类 def __init__(self,name): #定义初始化信息. self.name = name li = Person('李') #实例化Person('李'),给变量li li.age = 20 #再程序没有停止下,将实例属性age传入.动态语言的特点. Person.age = None #这里使用类名来创建一个属性age给类,默认值是None.Python支持的动态属性添加. def eat(self): #定义一个方法,不过这个方法再

easyui------向datagrid中动态添加不重复的记录

前一段时间,在界面中遇到从datagrid已有数据中选择数据到另一个datagrid,这时,是需要考虑到添加不重复的记录的. 如下图所示的效果: 在第一次添加的时候,其实是要将所有选中的学生进行添加,那么如果第二次进行添加,就要进行过滤操作,这时只需要添加那些之前,没有选中的学生,所以这里又一次用到了原来学习到的产生不重复的随机数里面的知识了,又回忆了一遍,印象深刻了许多. 具体实现如下: cshtml:这里第一个表格中的数据,通过级联查询,会将所有学生直接显示在表格中,第二个表格用来显示所有已

Android 4.4.2 动态添加JNI库方法记录 (二 app应用层)

欢迎转载,务必注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44458553 源码下载地址:http://download.csdn.net/detail/u010406724/8515377 本篇介绍怎么使用前面建立好的库文件. 要使用JNI库文件,那么首先我们是需要把它加载到系统中,并对其定义接口,供给应用来调用. 建立一个工程,我的工程名为RealArmTest,过程就省略了,完成后再在src下建立一个类,不继承其他类,包名

Android 4.4.2 动态加入JNI库方法记录 (一 JNI库层)

欢迎转载,务必注明出处.http://blog.csdn.net/wang_shuai_ww/article/details/44456755 本篇是继<s5p4418 Android 4.4.2 驱动层 HAL层 服务层 应用层 开发流程记录>之后的第二种加入JNI和服务的方法. 前面的方法是直接把HAL和服务层加入到了.Android的api中.这种方式优点是操作系统已开发完毕,剩下做APP的开发,那么我们仅仅须要一个classes.jar文件就可以使用我们自己Android系统的被隐藏的

Android 4.4.2 动态加入JNI库方法记录 (二 app应用层)

欢迎转载,务必注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44458553 源代码下载地址:http://download.csdn.net/detail/u010406724/8515377 本篇介绍怎么使用前面建立好的库文件. 要使用JNI库文件,那么首先我们是须要把它载入到系统中.并对其定义接口,供给应用来调用. 建立一个project,我的project名为RealArmTest.过程就省略了,完毕后再在src下建立一个