Android M中 JNI的入门学习
今年谷歌推出了Android 6.0,作为安卓开发人员,对其学习掌握肯定是必不可少的,今天小编和大家分享的就是Android 6.0中的 JNI相关知识,这是在一个安卓教程网上看到的内容,感觉很不错,一起来看看吧~~
初学者可能首先就会问,JNI是个什么鬼?
其实,JNI 的全称 Java Native Interface,Java 本地调用,主要用于实现两种功能:
1、Java 程序调用 Native(C/C++) 语言写的函数。
2、Native(C/C++) 程序调用 Java 层函数。
JNI 是 Java 提供的机制。Java 想要调用 C/C++ 库代码,在中间需要一个 JNI 库,结构像下面这样:
Java 代码
↓
JNI 库 (lib***_jni.so)
↓
Native (lib***.so)
Java 中加载 JNI 库
在 安卓开发中,需要调用 JNI 的类都要首先对 JNI 库进行加载:
System.loadLibrary("***_jni");
通过这个就可以知道某个类是否使用了 JNI 方法。
大家可以,在 Android 6.0 的代码中搜索一下,有数百处使用到的地方。例如 Android 系统中重要的 SystemServer 类,在其创建的时候就有一句:
// Initialize native services.
System.loadLibrary("android_servers");
看来 SystemServer 用到了 Native 库,其 JNI 库的名字叫 libandroid_servers.so。
System.loadLibrary 需要在进行 JNI 调用之前调用,先将 JNI 库加载进来。
JNI 中的 Native 方法,要在 Java 中使用,需要在 Java 中创建带有 native 关键字的同名函数。
接着在 SystemServer 的代码中搜索 native,只找到一处:
private static native void startSensorService();
因是 SystemServer 是用来启动 Android 中关键服务的,它自己加载 libandroid_servers.so 后,其他服务就可以直接声明 JNI 方法使用,而不用重复加载了。
例如,在 LightsService 中,有:
private static native long init_native();
private static native void finalize_native(long ptr);
static native void setLight_native(long ptr, int light, int color, int mode,
int onMS, int offMS, int brightnessMode);
...
在 PowerManagerService 中,有:
private native void nativeInit();
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
private static native void nativeSetInteractive(boolean enable);
private static native void nativeSetAutoSuspend(boolean enable);
private static native void nativeSendPowerHint(int hintId, int data);
private static native void nativeSetFeature(int featureId, int data);
...
他们都没有再 System.loadLibrary。
JNI 库的实现
这么多关键服务都需要 libandroid_servers.so 中的 JNI 方法,这个库也是够神通广大的。实现它的代码在哪里?怎样才能找到呢?
JNI 库中的函数的命名方式与 Java 中的方法有着严格的对应规则。例如,SystemServer 的类名的全称是 com.android.server.SystemServer,前文说过它自己有唯一一个 JNI 调用 startSensorService,那么 Android 的代码中必然有一个函数为 android_server_SystemServer_startSensorService 。
它位于 frameworks/base/services/core/jni/com_android_server_SystemServer.cpp ,方法实现为:
static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
char propBuf[PROPERTY_VALUE_MAXIEEWONG@GMAIL.COM];
property_get("system_init.startsensorservice", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
// Start the sensor service
SensorService::instantiate();
}
我们再来看 LightsService,它的类全称为 com.android.server.lights.LightsService,它用到了一个 JNI 调用为 setLight_native ,那么必然也有一个函数名为 android_server_lights_LightsService_setLight_native ,不过并没有找到 。但是找到了 setLight_native ,即同名的 Native 实现,这就是对应的 JNI 实现,它位于 frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp 下。
同名是如何做到的呢?我们一起来看看代码就明白了:
static void setLight_native(JNIEnv* /* env */, jobject /* clazz */, jlong ptr,
jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
...
}
static JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
{ "setLight_native", "(JIIIIII)V", (void*)setLight_native },
};
int register_android_server_LightsService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",
method_table, NELEM(method_table));
}
它通过一个 table 把前面那一大堆前缀("com/android/server/lights/LightsService")给提前注册了,这样 Java 调用时,JNI 层就知道有同名函数了。
libandroid_servers.so 是如何生成的?
通过前面两次 JNI 实现的寻找,我们发现它们位于 frameworks/base/services/core/jni/ 下的不同文件中。他们是怎么组织最终构建出 libandroid_servers.so 的呢?
首先, libandroid_servers 这个名字肯定是我们在 Makefile 里面起的,在 Android 下就是 Android.mk。在代码中搜索 libandroid_servers ,找到 frameworks/base/services/Android.mk :
# native library
# =============================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES :=
LOCAL_SHARED_LIBRARIES :=
# include all the jni subdirs to collect their sources
include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE:= libandroid_servers
include $(BUILD_SHARED_LIBRARY)
其中:
# include all the jni subdirs to collect their sources
include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
把所有子目录下的 jni 都收集起来,编译为 LOCAL_MODULE libandroid_servers 。并且是以动态库的形式,因为 JNI 库必需动态库。
至此对于 JNI 的入门学习就算完成了。在接下来学习 Android 代码时,遇到带 native 的、遇到一大堆下划线的方法,来者不拒了 。
以上 就是Android6.0中JNI的相关知识,这里由于篇幅问题,写得都比较浅,后续将继续为大家分享更深入的相关内容。建议大家可以在本地尝试一下上述介绍的内容,加深掌握。
相关文章:《29个android开发常用的类、方法及接口》
Android M中 JNI的入门学习的更多相关文章
- Android项目中JNI技术生成并调用.so动态库实现详解
生成 jni方式有两种:一种是通过SWIG从C++代码生成过度的java代码:另一种是通过javah的方式从java代码自动生成过度的C++代码.两种方式下的步骤流程正好相反. 第一种方式:由于需要配 ...
- android studio 中jni底层日志的打印
1 添加ndk对log支持若需要添加ndk对log的支持,只需要通过以下2步即可实现. 1.1 修改Android.mk如生成的库文件是“.so文件”,则在Android.mk中添加如下内容:LOCA ...
- Android Studio中JNI程序的单步调试和日志打印
近日有个算法(检测碰撞)需要用C++实现,目的是IOS和ANDROID中共享同一段程序. 下面说说android调用这段程序过程中遇到的一些事情.(过程中网上搜索了一些相关文章,大部分说的是eclip ...
- Android Studio中JNI -- 2 -- 编写c文件
继上一篇,我们在native接口中编写了2个方法 生成的相应.h文件 这时,需要我们自己去完善.c文件 /* DO NOT EDIT THIS FILE - it is machine generat ...
- Android Studio中JNI -- 1 -- 配置方法
1.配置NDK 1.1 下载NDK Android Studio 1.2 配 android-ndk-r10e,不同版本的Studio需要配置不同的ndk,下载完成后,随便解压放至某个文件目录下 1. ...
- Linux中的syslog 入门学习教程
syslog是linux系统中默认的日志守护进程,默认的syslog配置文件是/etc/syslog.conf文件.程序.守护进程和内核提供了访问系统的日志信息.任何希望生成日志信息的程序都可以向sy ...
- iOS中 Swift初级入门学习(三)
// // main.swift // LessonSwift-03 // // Copyright (c) 2015年 韩俊强. All rights reserved. // import Fou ...
- iOS中 Swift初级入门学习(二)
// Copyright (c) 2015年 韩俊强. All rights reserved. // import Foundation /* // 控制语句 // for - in // 遍历字符 ...
- iOS中 Swift初级入门学习(一)
/ // Copyright (c) 2015年 韩俊强. All rights reserved. // import Foundation // Swift当中的输出函数 // println S ...
随机推荐
- LeetCode106. Construct Binary Tree from Inorder and Postorder Traversal
题目 根据一棵树的中序遍历与后序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9, ...
- Maven_项目管理工具
Maven 一.Maven是apache下的一个开源项目,是纯java开发,并且只是用来管理java项目的 二.Maven的好处: 0.节省空间:对jar包做了统一管理 依赖管理,项目里无需放jar包 ...
- elasticsearch-dsl笔记
一.elasticsearch安装 安装java1.8以上 安装elasticsearch-rtf(https://github.com/medcl/elasticsearch-rtf) head插件 ...
- Dnsmasq域名解析系统安装配置
Dnsmasq使用上比bind要简便得多,可以做正向.反向dns解析,支持DHCP服务.也可以做内部dns服务器用. 默认下,dnsmasq使用系统的/etc/resolv.conf,并读取/etc/ ...
- c# 解决读取Excel混合文本类型,数据读取失败的解决方法
错误重现: ----------------------------------------------------------------------- 在导入Excel读取数据时,其中的一个字段保 ...
- js常用的2中排序方法:冒泡排序和快速排序
冒泡排序:例如9 4 5 6 8 3 2 7 10 1 首先:9和4比较 4放前 4 9 5 6 8 3 2 7 10 1 4和5比较 4不动 4 9 5 6 8 3 2 7 10 1 ...
- Json的用处一
今天,我们用到了json的的用处,其实也就是一个很简单的用处,就是点击一个按钮,触发一个事件,然后调用json, 之后我们就可以进行异步操作,其实只是针对于后台的操作,其实我们并没有对数据库进行刷新, ...
- 使用dataframe解决spark TopN问题:分组、排序、取TopN和join相关问题
package com.profile.mainimport org.apache.spark.sql.expressions.Windowimport org.apache.spark.sql.fu ...
- mysql in和exists性能比较和使用【转】
exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当 exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返回当前loop到的这条记录, ...
- MySQL之查询性能优化(一)
为什么查询速度会慢 通常来说,查询的生命周期大致可以按照顺序来看:从客户端,到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端.其中“执行”可以认为是整个生命周期中最重要的阶段, ...