亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Android下如何通過JNI方法向上提供接口總結

系統 1866 0

參考文獻:

http://blog.csdn.net/luoshengyang/article/details/6575988

http://www.androidmi.com/Androidkaifa/rumen/201005/633.html

1 什么是JNI

JNI是Java Native Interface的縮寫,即Java本地接口.從Java1.1開始,JNI標準成為Java平臺的一部分,它允許java代碼和用其它語言編寫的代碼進行交互.JNI是本地編程接口,它使得在Java虛擬機(VM)內部運行的Java代碼能夠與用其他編程語言(如C,C++和匯編語言)的應用程序和庫進行交互操作.

在Android中提供的JNI的方式,讓Java程序可以調用C語言程序。Android中很多Java類都具有native接口,這些native接口就是同本地實現,然后注冊到系統中的.

JNI在Android層次結構中的作用如下圖所示:

Android下如何通過JNI方法向上提供接口總結

在Android中,主要的JNI代碼在以下的路徑中:

Android源碼根目錄/frameworks/base/core/jni/

這個路徑中的內容將被編譯成庫libandroid_runtime.so,這就是一個普通的動態庫,被放置在目標系統的/system/lib目錄中.

除此之外,Android還包含其他的JNI庫,例如,媒體部分的JNI目錄frameworks/base/media/jni/中,被編譯成庫libmedia_jni.so.

JNI中的各個文件實際上就是C++的普通文件,其命名一般和支持的Java類有對應關系。這種關系是習慣上的寫法,而不是強制的。

在Android中實現的JNI庫,需要連接動態庫libnativehelper.so.

2 注冊JNI方法

在Android源碼根目錄/frameworks/base/services/jni/目錄下有一個onload.cpp文件,其內容如下:

    /*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"

namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryService(JNIEnv* env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_HelloService(JNIEnv *env);
};

using namespace android;

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) {
        LOGE("GetEnv failed!");
        return result;
    }
    LOG_ASSERT(env, "Could not retrieve the env!");

    register_android_server_PowerManagerService(env);
    register_android_server_InputApplicationHandle(env);
    register_android_server_InputWindowHandle(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_BatteryService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbHostManager(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GpsLocationProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_HelloService(env);

    return JNI_VERSION_1_4;
}

  

onload.cpp文件上部分為注冊函數的聲明,下部分為調用各種注冊函數,而 這些注冊函數就是JNI方法的注冊函數 ! 正有通過這些注冊函數,上層才有可能調用注冊的JNI方法.

這些注冊函數是由同目錄下的其他.cpp文件中實現,如上面的register_android_server_HelloService(env)這個函數是在com_android_service_HelloService.cpp文件中實現的.那么編譯器又是如何知道這點的呢? 答案當然是Android.mk這個文件,打開這個文件,其內容如下:

    LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    com_android_server_AlarmManagerService.cpp \
    com_android_server_BatteryService.cpp \
    com_android_server_InputApplicationHandle.cpp \
    com_android_server_InputManager.cpp \
    com_android_server_InputWindowHandle.cpp \
    com_android_server_LightsService.cpp \
    com_android_server_PowerManagerService.cpp \
    com_android_server_SystemServer.cpp \
    com_android_server_UsbDeviceManager.cpp \
    com_android_server_UsbHostManager.cpp \
    com_android_server_VibratorService.cpp \
    com_android_server_location_GpsLocationProvider.cpp \
    com_android_server_connectivity_Vpn.cpp \
    com_android_server_HelloService.cpp \
    onload.cpp

LOCAL_C_INCLUDES += \
    $(JNI_H_INCLUDE) \
    frameworks/base/services \
    frameworks/base/core/jni \
    external/skia/include/core

LOCAL_SHARED_LIBRARIES := \
    libandroid_runtime \
    libcutils \
    libhardware \
    libhardware_legacy \
    libnativehelper \
    libsystem_server \
    libutils \
    libui \
    libinput \
    libskia \
    libgui \
    libusbhost

ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
    LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endif

LOCAL_MODULE:= libandroid_servers

include $(BUILD_SHARED_LIBRARY)

  
在LOCAL_SRC_FILE中給出了所有實現文件(cpp文件)的路徑,因此編譯就能找到各個注冊函數對應的實現文件了.

接下來讓我們來看看其中一個注冊函數的具體實現過程是如何的,比如:register_android_server_HelloService(env),打開com_android_service_HelloService.cpp文件,其下有注冊函數的實現代碼,如下:

    int register_android_server_HelloService(JNIEnv *env) {
    	return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
  
其中jniRegisterNativeMethods為注冊JNI方法函數,此函數在JNI方法使用中非常重要,此函數的第二個參數為對應著java類即HelloService.java的文件名,第三個參數為注冊的方法表:

    /*JNI方法表*/
static const JNINativeMethod method_table[] = {
	{"init_native", "()Z", (void*)hello_init},
	{"setVal_native", "(I)V", (void*)hello_setVal},
	{"getVal_native", "()I", (void*)hello_getVal},
};
  
接下來就是方法表內各個接口的實現代碼了.

如hello_setVal函數的實現:

    /*通過硬件抽象層定義的硬件訪問接口設置硬件寄存器val的值*/
static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
	int val = value;
	LOGI("Hello JNI: set value %d to device.", val);
	if(!hello_device) {
		LOGI("Hello JNI: device is not open.");
		return;
	}
		
	hello_device->set_val(hello_device, val);
}
  
方法列表中的hello_init的實現代碼中展現了如何調用下層HAL提供的接口, 還記得上一章: Android中HAL如何向上層提供接口總結 一文中描述HAL是如何向上層提供接口的嗎?這個hello_init函數的實現就是典型的調用HAL提供的初始化接口的例子,下面見hello_init這個函數的實現代碼:

    /*通過硬件抽象層定義的硬件模塊打開接口打開硬件設備*/
	static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {
		return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
	}
  /*通過硬件模塊ID來加載指定的硬件抽象層模塊并打開硬件*/
	static jboolean hello_init(JNIEnv* env, jclass clazz) {
		hello_module_t* module;
		
		LOGI("Hello JNI: initializing......");
		if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
			LOGI("Hello JNI: hello Stub found.");
			if(hello_device_open(&(module->common), &hello_device) == 0) {
				LOGI("Hello JNI: hello device is open.");
				return 0;
			}
			LOGE("Hello JNI: failed to open hello device.");
			return -1;
		}
		LOGE("Hello JNI: failed to get hello stub module.");
		return -1;		
	}
  
上述的module->methods->open這個open函數就是HAL提供的接口,其函數原型在hardware.h頭文件中有定義,只能返回struct hw_device_t類型的指針,而在JNI方法中,我們關心的是struct hello_device_t,只有通過struct hello_device_t,我們才能獲取其所有的成員函數(接HAL提供的接口函數),由于struct hello_device_t的第一個成員就是struct hw_device_t類型的數據,因此在這里可以將獲取的struct hw_device_t強制轉化為struct hello_device_t來用。還沒有明白過來的,建議回過頭去看上一篇文章: Android中HAL如何向上層提供接口總結 .

3 方法列表說明

關于static const JNINativeMethod method_table[]方法列表的原型如下:

    typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
  
Andoird 中使用了一種不同傳統Java JNI的方式來定義其native的函數。其中很重要的區別是Andorid使用了一種Java 和 C 函數的映射表數組,并在其中描述了函數的參數和返回值。這個數組的類型就是JNINativeMethod,見上述定義.

第一個變量name是Java中函數的名字。第二個變量signature,用字符串是描述了函數的參數和返回值.第三個變量fnPtr是函數指針,指向C函數。

其中比較難以理解的是第二個參數,例如:

"()V"

"(II)V"

"(Ljava/lang/String;Ljava/lang/String;)V"

實際上這些字符是與函數的參數類型一一對應的。

"()" 中的字符表示參數,后面的則代表返回值。例如"()V" 就表示void Func();

"(II)V" 表示 void Func(int, int);


具體的每一個字符的對應關系如下
字符 Java類型 C類型

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 jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]


上面的都是基本類型。如果Java函數的參數是class,則以"L"開頭,以";"結尾.中間是用"/" 隔開的包及類名。而其對應的C函數名的參數則為jobject. 一個例外是String類,其對應的類為jstring


Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject


如果JAVA函數位于一個嵌入類,則用$作為類名間的分隔符。

例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"




Android下如何通過JNI方法向上提供接口總結


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 激情都市久久爱 | 狠狠色婷婷丁香六月 | 中文字幕一视频97色伦 | 在线播放亚洲视频 | 久久爱99re | 亚洲精品亚洲人成在线 | 91久久精品国产免费一区 | 被公侵犯肉体中文字幕一区二区 | 99爱免费观看视频在线 | 色综合久久久久久中文网 | 国产亚洲精品自在久久77 | 国产精品久久久影院 | 99热这里只有精品国产动漫 | 中文日韩字幕一区在线观看 | 亚洲日本va中文字幕在线不卡 | 欧美最猛性xxxx69交 | 伊人久久大香线蕉综合bd高清 | 国产日本久久久久久久久婷婷 | 国产一级免费在线观看 | 久久艹综合 | www.色午夜| 久久青草网站 | 福利午夜最新 | 伊人色综合网一区二区三区 | 欧美干干 | 五月精品 | 欧美一级毛片不卡免费观看 | 狠狠色成人综合首页 | 国产精品99久久久久久夜夜嗨 | 四虎永久网址 | 日本欧美强乱视频在线 | 欧美videos粗暴高清性 | 国产成人免费a在线资源 | 999国产高清在线精品 | 99视频在线观看视频一区 | 久久精品中文字幕有码日本 | 7777精品久久久大香线蕉 | 久久中文在线 | 在线精品国产一区二区 | 特级做人爱c级特级aav毛片 | 亚洲伦理中文字幕 |