在Android中,JNI主要用于实现一些性能较高的功能,如图像处理、音频处理、视频处理等。同时,JNI也可以用于实现一些特殊的功能,如与硬件交互、与系统服务交互等。
在某个对象中添加如下代码,例如我在/frameworks/base/services/core/java/com/android/server/keepalive/KeepAliveManagerService.java中去使用native方法
public class KeepAliveManagerService extends IKeepAliveManager.Stub {
private static final String TAG = "KeepAliveManagerService leilei";
private final Context mContext;
private final ActivityManagerService mActivityManagerService;
private WindowManagerService mWindowManagerService;
private boolean mSystemReady = false;
**public static native int resumeNative(int v);**
**public static native int pauseNative(int v);**
**public static native String stopNative(String v);**
// 应用保活方法,简单的调用了三个native方法:resumeNative,pauseNative,stopNative
@Override
public boolean keepAliveApplicationByPackage(String packageName) throws RemoteException {
Log.d(TAG, "keepAliveApplicationByPackage.packageName:"+packageName);
if (TextUtils.isEmpty(packageName) || mActivityManagerService == null
|| mContext == null || !mSystemReady){
return false;
}
int s = resumeNative(1);
int s1 = pauseNative(2);
String s2 = stopNative("leilei");
Log.d(TAG, "leilei keepAliveApplicationByPackage: onResumeNative:"+s);
Log.d(TAG, "leilei keepAliveApplicationByPackage: onPauseNative:"+s1);
Log.d(TAG, "leilei keepAliveApplicationByPackage: stopNative:"+s2);
int curCallingUid = Binder.getCallingUid();
return keepAliveApplicationByPackage(packageName,curCallingUid);
}
}
至此java层的代码就写好了
JNI文件创建
因为我写jni方法是需要在我的service对象里使用,所以我frameworks/base/services/core/jni/目录下创建为需要的文件,如下代码所示
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef LOG_TAG
#undef LOG_TAG
#define LOG_TAG "leilei"
#endif
// jni静态注册
/*extern "C" jstring
Java_com_android_server_keepalive_KeepAliveManagerService_onResumeNative(JNIEnv *env, jclass thiz, jlong value) {
// 进行本地处理,生成返回值
std::string hello = "Hello from C++";
jstring result = env->NewStringUTF(hello.c_str());
return result;
}*/
// jni动态注册
namespace android {
static jint pauseNative(JNIEnv* env, jobject thiz, jint value){
ALOGD("The leilei message is onPauseNative %d:",value);
return value;
}
static jint resumeNative(JNIEnv *env, jobject thiz, jint value){
ALOGD("The leilei message is onResumeNative %d:",value);
return value;
}
static jstring stopNative(JNIEnv *env, jobject thiz,jstring value){
const char* ptr = env->GetStringUTFChars(value, NULL);
ALOGD("The leilei message is stopNative %s:",ptr);
return value;
}
// 对应native的方法注册表
static const JNINativeMethod gKeepAliveManagerMethods[] = {
/* name, signature, funcPtr */
{"pauseNative","(I)I",(void *)pauseNative},
{"resumeNative","(I)I",(void *)resumeNative},
{"stopNative","(Ljava/lang/String;)Ljava/lang/String;",(void*) stopNative},
};
// 自己实现一个跟jniRegisterNativeMethods一样的功能
int registerNativeMethods(JNIEnv *env,std::string name,const JNINativeMethod *methods) {
// 反射拿到java对象
jclass klass = env->FindClass(name.c_str());
if (klass == NULL) {
return -1;
}
// 第一个参数:反射拿到的对象
// 第二个参数:类中的native方法--注册表
// 第三个参数:native方法对象的个数
return env->RegisterNatives(klass, methods,3);
}
// onload.cpp中调用了JNI_OnLoad,然后调用了register_android_server_KeepAliveManager进行注册
// jniRegisterNativeMethods对RegisterNatives封装了,所以可以很方便的使用,我们手动来实现一下
// JNI_OnLoad是jni.h中的对象,只有调用JNI_OnLoad和RegisterNatives才是动态注册
int register_android_server_KeepAliveManager(JNIEnv* env) {
// return jniRegisterNativeMethods(env, "com/android/server/keepalive/KeepAliveManagerService",gKeepAliveManagerMethods, NELEM(gKeepAliveManagerMethods));
return registerNativeMethods(env,"com/android/server/keepalive/KeepAliveManagerService",gKeepAliveManagerMethods);
}
};
文件名称必须规范:com.android.server.keepalive.KeepAliveManagerService.cpp,由包名+类名组成,然后实现对应上层的native方法即可,方法名称最好相同(也可以不同,只要在注册函数的第一个参数中对应起来就行),例如下方法,三个参数中,前两个参数必须有,而且不能变—>JNIEnv和jobject:
static jint pauseNative(JNIEnv* env, jobject thiz, jint value){
...
}
然后就是开始动态注册jni方法,如下代码所示,由于aosp已经封装好了jniRegisterNativeMethods
方法可以直接使用来注册jni方法,但是为了更深刻的理解,我们手动来实现registerNativeMethods
// native方法注册表
static const JNINativeMethod gKeepAliveManagerMethods[] = {
/* name, signature, funcPtr */
{"pauseNative","(I)I",(void *)pauseNative},
{"resumeNative","(I)I",(void *)resumeNative},
{"stopNative","(Ljava/lang/String;)Ljava/lang/String;",(void*) stopNative},
};
// 自己实现一个跟register_android_server_KeepAliveManager一样的功能
int registerNativeMethods(JNIEnv *env,std::string name,const JNINativeMethod *methods) {
jclass klass = env->FindClass(name.c_str());
if (klass == NULL) {
return -1;
}
// 第一个参数:反射拿到的对象
// 第二个参数:类中的native方法--注册表
// 第三个参数:native方法对象的个数
return env->RegisterNatives(klass, methods,3);
}
int register_android_server_KeepAliveManager(JNIEnv* env) {
// return jniRegisterNativeMethods(env, "com/android/server/keepalive/KeepAliveManagerService",gKeepAliveManagerMethods, NELEM(gKeepAliveManagerMethods));
return registerNativeMethods(env,"com/android/server/keepalive/KeepAliveManagerService",gKeepAliveManagerMethods);
}
先实现一个native方法注册表,代表需要对应java层native方法,返回的对象是JNINativeMethod
,该对象属于jni.h里的,结构如下
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个参数对应了java native方法的名称,第二个参数代表native方法里面的参数和返回值,第三个参数代表jni方法
回到registerNativeMethods
方法,主要就是通过env的FindClass反射获取Java对象,然后通过RegisterNatives(klass, methods,3);
进行注册即可,第二个参数就是native方法gKeepAliveManagerMethods
注册表
**思考一下:**既然需要注册jni,那么调用register_android_server_KeepAliveManager
函数的注册的入口又在哪里?下文分析
JNI文件引入和注册流程
android.bp引入编译
需要让我们的jni文件参与编译,需要在frameworks/base/services/core/jni/Android.bp中添加该文件,如下所示
cc_library_static {
name: "libservices.core",
defaults: ["libservices.core-libs"],
cpp_std: "c++2a",
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
],
srcs: [
"BroadcastRadio/JavaRef.cpp",
"BroadcastRadio/NativeCallbackThread.cpp",
"BroadcastRadio/BroadcastRadioService.cpp",
"BroadcastRadio/Tuner.cpp",
"BroadcastRadio/TunerCallback.cpp",
"BroadcastRadio/convert.cpp",
"BroadcastRadio/regions.cpp",
"gnss/GnssConfiguration.cpp",
"gnss/GnssMeasurement.cpp",
"gnss/GnssMeasurementCallback.cpp",
"gnss/Utils.cpp",
"stats/SurfaceFlingerPuller.cpp",
**"com.android.server.keepalive.KeepAliveManagerService.cpp",**
include_dirs: [
"frameworks/base/libs",
"frameworks/native/services",
"system/gatekeeper/include",
"system/memory/libmeminfo/include",
],
header_libs: [
"bionic_libc_platform_headers",
],
}
在此模块添加**“com.android.server.keepalive.KeepAliveManagerService.cpp”**,即可,模块名为libservices.core
,会生成对应的so库
JNI注册入口声明
在frameworks中,上文分析了如何调用jni注册native方法,但是调用注册的入口在哪里?就是通过frameworks/base/services/core/jni/onload.cpp文件进行调用的,需要在此文件中声明我们的注册入口,如下代码所示
namespace android {
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
**int register_android_server_KeepAliveManager(JNIEnv* env);**
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_PowerStatsService(JNIEnv* env);
int register_android_server_HintManagerService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
}
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_broadcastradio_BroadcastRadioService(env);
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
register_android_server_PowerStatsService(env);
register_android_server_HintManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputManager(env);
**register_android_server_KeepAliveManager(env);
}**
}
只需要在namespace android
中声明注册入口函数**register_android_server_KeepAliveManager
**,此函数在我们创建的jni文件中会实现。
然后在JNI_OnLoad
函数中添加**register_android_server_KeepAliveManager(env);
**,目的是为了方法可以被正确调用,以及传递了env对象(jni里的东西),再来看一遍我创建的jni文件,frameworks/base/services/core/jni/com.android.server.keepalive.KeepAliveManagerService.cpp
...
int **register_android_server_KeepAliveManager**(JNIEnv* env) {
// return jniRegisterNativeMethods(env, "com/android/server/keepalive/KeepAliveManagerService",gKeepAliveManagerMethods, NELEM(gKeepAliveManagerMethods));
return registerNativeMethods(env,"com/android/server/keepalive/KeepAliveManagerService",gKeepAliveManagerMethods);
}
};
**思考一下:**为什么需要在extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
方法中调用jni注册逻辑呢?这涉及到jni动态注册原理了,后面再分析,jni分为静态注册和动态注册
JNI注册表分析
Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod——该结构体位于jni.h
中,结构如下
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
三个参数代表着:native方法名称,签名—用字符串是描述了Java中函数的参数和返回值,jni函数对象-指向了java的native方法
具体用法如下
static const JNINativeMethod gKeepAliveManagerMethods[] = {
/* name, signature, funcPtr */
{"pauseNative","(I)I",(void *)pauseNative},
{"resumeNative","(I)I",(void *)resumeNative},
{"stopNative","(Ljava/lang/String;)Ljava/lang/String;",(void*) stopNative},
};
第三个参数前面必须带有(void *),这里主要分析第二个参数,()代表native方法的参数,()外面部分代表着返回值,I代表着java的int,jni的jint,具体如下
字符 | c/c++类型 | Java类型 |
---|---|---|
V | void | void |
Z | jboolean | boolean |
I | jint | int |
J | jlong | long |
D | jdouble | double |
F | jfloat | float |
B | jbyte | byte |
C | jchar | char |
S | jshort | short |
以上都是基本数据类,如果是数组,则用[代表,如整型数值 [I
来表示,具体如下
名称 | c/c++类型 | Java类型 |
---|---|---|
[I | jintArray | int[] |
[F | jfloatArray | float[] |
[B | jbyteArray | byte[] |
[C | jcharArray | char[] |
[S | jshortArray | short[] |
[D | jdoubleArray | double[] |
[J | jlongArray | long[] |
[Z | jbooleanArray | boolean[] |
那如果native参数中是对象呢,需要用如下方法表示—参数解释:
// 参数解释
"()" 中的字符表示参数,小括号后面的则代表返回值。
"()V" 就表示native void Fun();
"(II)V" 表示native void Func(int a, int b);参数是俩个整型。
"(Ljava/lang/String;)Ljava/lang/String;" 就表示native Sting Func(String value);
所以如果要用对象作为参数或者返回值,需要在前面加个”L”,中间是用”/" 隔开,后面跟包名和类名,以及分号即可。如果是对象数组,则在前面加个[
即可