jni回調(diào)是指在c/c++代碼中調(diào)用java函數(shù),當(dāng)在c/c++的線程中執(zhí)行回調(diào)函數(shù)時(shí),會(huì)導(dǎo)致回調(diào)失敗。
創(chuàng)新互聯(lián)建站是一家專注于成都網(wǎng)站制作、成都網(wǎng)站建設(shè)與策劃設(shè)計(jì),光澤網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)建站做網(wǎng)站,專注于網(wǎng)站建設(shè)十余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:光澤等地區(qū)。光澤做網(wǎng)站價(jià)格咨詢:18980820575
其中一種在Android系統(tǒng)的解決方案是:
把c/c++中所有線程的創(chuàng)建,由pthread_create函數(shù)替換為由Java層的創(chuàng)建線程的函數(shù)AndroidRuntime::createJavaThread。
假設(shè)有c++函數(shù):
[cpp] view plaincopy
void *thread_entry(void *args)
{
while(1)
{
printf("thread running...\n");
sleep(1);
}
}
void init()
{
pthread_t thread;
pthread_create(thread,NULL,thread_entry,(void *)NULL);
}
init()函數(shù)創(chuàng)建一個(gè)線程,需要在該線程中調(diào)用java類Test的回調(diào)函數(shù)Receive:
[cpp] view plaincopy
public void Receive(char buffer[],int length){
String msg = new String(buffer);
msg = "received from jni callback:" + msg;
Log.d("Test", msg);
}
首先在c++中定義回調(diào)函數(shù)指針:
[cpp] view plaincopy
//test.h
#include pthread.h
//function type for receiving data from native
typedef void (*ReceiveCallback)(unsigned char *buf, int len);
/** Callback for creating a thread that can call into the Java framework code.
* This must be used to create any threads that report events up to the framework.
*/
typedef pthread_t (* CreateThreadCallback)(const char* name, void (*start)(void *), void* arg);
typedef struct{
ReceiveCallback recv_cb;
CreateThreadCallback create_thread_cb;
}Callback;
再修改c++中的init和thread_entry函數(shù):
[cpp] view plaincopy
//test.c
#include stdio.h
#include stdlib.h
#include pthread.h
#include sys/wait.h
#include unistd.h
#include "test.h"
void *thread_entry(void *args)
{
char *str = "i'm happy now";
Callback cb = NULL;
int len;
if(args != NULL){
cb = (Callback *)args;
}
len = strlen(str);
while(1)
{
printf("thread running...\n");
//invoke callback method to java
if(cb != NULL cb-recv_cb != NULL){
cb-recv_cb((unsigned char*)str, len);
}
sleep(1);
}
}
void init(Callback *cb)
{
pthread_t thread;
//pthread_create(thread,NULL,thread_entry,(void *)NULL);
if(cb != NULL cb-create_thread_cb != NULL)
{
cb-create_thread_cb("thread",thread_entry,(void *)cb);
}
}
然后在jni中實(shí)現(xiàn)回調(diào)函數(shù),以及其他實(shí)現(xiàn):
[cpp] view plaincopy
//jni_test.c
#include stdlib.h
#include malloc.h
#include jni.h
#include JNIHelp.h
#include "android_runtime/AndroidRuntime.h"
#include "test.h"
#define RADIO_PROVIDER_CLASS_NAME "com/tonny/Test"
using namespace android;
static jobject mCallbacksObj = NULL;
static jmethodID method_receive;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env-ExceptionCheck()) {
LOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env-ExceptionClear();
}
}
static void receive_callback(unsigned char *buf, int len)
{
int i;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jcharArray array = env-NewCharArray(len);
jchar *pArray ;
if(array == NULL){
LOGE("receive_callback: NewCharArray error.");
return;
}
pArray = (jchar*)calloc(len, sizeof(jchar));
if(pArray == NULL){
LOGE("receive_callback: calloc error.");
return;
}
//copy buffer to jchar array
for(i = 0; i len; i++)
{
*(pArray + i) = *(buf + i);
}
//copy buffer to jcharArray
env-SetCharArrayRegion(array,0,len,pArray);
//invoke java callback method
env-CallVoidMethod(mCallbacksObj, method_receive,array,len);
//release resource
env-DeleteLocalRef(array);
free(pArray);
pArray = NULL;
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
{
return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
}
static Callback mCallbacks = {
receive_callback,
create_thread_callback
};
static void jni_class_init_native
(JNIEnv* env, jclass clazz)
{
method_receive = env-GetMethodID(clazz, "Receive", "([CI)V");
}
static int jni_init
(JNIEnv *env, jobject obj)
{
if (!mCallbacksObj)
mCallbacksObj = env-NewGlobalRef(obj);
return init(mCallbacks);
}
static const JNINativeMethod gMethods[] = {
{ "class_init_native", "()V", (void *)jni_class_init_native },
{ "native_init", "()I", (void *)jni_init },
};
static int registerMethods(JNIEnv* env) {
const char* const kClassName = RADIO_PROVIDER_CLASS_NAME;
jclass clazz;
/* look up the class */
clazz = env-FindClass(kClassName);
if (clazz == NULL) {
LOGE("Can't find class %s/n", kClassName);
return -1;
}
/* register all the methods */
if (env-RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK)
{
LOGE("Failed registering methods for %s/n", kClassName);
return -1;
}
/* fill out the rest of the ID cache */
return 0;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
LOGI("Radio JNI_OnLoad");
if (vm-GetEnv((void**) env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed/n");
goto fail;
}
if(env == NULL){
goto fail;
}
if (registerMethods(env) != 0) {
LOGE("ERROR: PlatformLibrary native registration failed/n");
goto fail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
fail:
return result;
}
jni的Android.mk文件中共享庫設(shè)置為:
[cpp] view plaincopy
LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper
最后再實(shí)現(xiàn)Java中的Test類:
[java] view plaincopy
//com.tonny.Test.java
public class Test {
static{
try {
System.loadLibrary("test");
class_init_native();
} catch(UnsatisfiedLinkError ule){
System.err.println("WARNING: Could not load library libtest.so!");
}
}
public int initialize() {
return native_radio_init();
}
public void Receive(char buffer[],int length){
String msg = new String(buffer);
msg = "received from jni callback" + msg;
Log.d("Test", msg);
}
protected static native void class_init_native();
protected native int native_init();
}
android的jni可以使用c/c++來開發(fā),相比java而言,運(yùn)行的效率提高了很多,特別是在做一些圖像算法,或者游戲邏輯的時(shí)候,使用jni將大大的提高效率。比如某個(gè)游戲要采用opengl,同樣加載一個(gè)由1000個(gè)多邊形組成的3d模型,jni要比java運(yùn)算快好幾倍,這樣就保證了游戲運(yùn)行的fps不會(huì)太低。
另外一個(gè)好處就是內(nèi)存管理上面,java的內(nèi)存管理全部由虛擬機(jī)來管理,C++由程序員來管理,利用率上面就好多了。
等等其他優(yōu)點(diǎn)。
既然這么多的優(yōu)點(diǎn),為什么一個(gè)android程序不采用純c來開發(fā)呢?因?yàn)槭莂ndroid的 UI framework采用java,所以,在UI上面還是采用java來開發(fā)。
方法步驟:
第一步:
使用Java編寫HelloWorld 的Android應(yīng)用程序:
代碼如下
package com.lucyfyr;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class HelloWorld extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.v("dufresne", printJNI("I am HelloWorld Activity"));
}
static
{
//加載庫文件
System.loadLibrary("HelloWorldJni");
}
//聲明原生函數(shù) 參數(shù)為String類型 返回類型為String
private native String printJNI(String inputStr);
}
這一步可以使用eclipse來生成一個(gè)App;
因?yàn)閑clipse會(huì)自動(dòng)編譯此Java文件,后面要是用到。
第二步:
生成共享庫的頭文件:
進(jìn)入到eclipse生成的Android Project中 :/HelloWorld/bin/classes/com/lucyfyr/ 下:
可以看到里面后很多后綴為.class的文件,就是eclipse為自動(dòng)編譯好了的java文件,其中就有:
HelloWorld.class文件。
退回到classes一級(jí)目錄:/HelloWorld/bin/classes/
執(zhí)行如下命令:
javah com.lucyfyr.HelloWorld
生成文件:com_lucyfyr_HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include jni.h
/* Header for class com_lucyfyr_HelloWorld */
#ifndef _Included_com_lucyfyr_HelloWorld
#define _Included_com_lucyfyr_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_lucyfyr_HelloWorld
* Method: printJNI
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
可以看到自動(dòng)生成對(duì)應(yīng)的函數(shù):Java_com_lucyfyr_HelloWorld_printJNI
Java_ + 包名(com.lucyfyr) + 類名(HelloWorld) + 接口名(printJNI):必須要按此JNI規(guī)范來操作;
java虛擬機(jī)就可以在com.simon.HelloWorld類調(diào)用printJNI接口的時(shí)候自動(dòng)找到這個(gè)C實(shí)現(xiàn)的Native函數(shù)調(diào)用。
當(dāng)然函數(shù)名太長,可以在.c文件中通過函數(shù)名映射表來實(shí)現(xiàn)簡化。
第三步:
實(shí)現(xiàn)JNI原生函數(shù)源文件:
新建com_lucyfyr_HelloWorld.c文件:
#include jni.h
#define LOG_TAG "HelloWorld"
#include utils/Log.h
/* Native interface, it will be call in java code */
JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI(JNIEnv *env, jobject obj,jstring inputStr)
{
LOGI("dufresne Hello World From libhelloworld.so!");
// 從 instring 字符串取得指向字符串 UTF 編碼的指針
const char *str =
(const char *)(*env)-GetStringUTFChars( env,inputStr, JNI_FALSE );
LOGI("dufresne---%s",(const char *)str);
// 通知虛擬機(jī)本地代碼不再需要通過 str 訪問 Java 字符串。
(*env)-ReleaseStringUTFChars(env, inputStr, (const char *)str );
return (*env)-NewStringUTF(env, "Hello World! I am Native interface");
}
/* This function will be call when the library first be load.
* You can do some init in the libray. return which version jni it support.
*/
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
void *venv;
LOGI("dufresne-----JNI_OnLoad!");
if ((*vm)-GetEnv(vm, (void**)venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("dufresne---ERROR: GetEnv failed");
return -1;
}
return JNI_VERSION_1_4;
}
OnLoadJava_com_lucyfyr_HelloWorld_printJNI
函數(shù)里面做一些log輸出 注意JNI中的log輸出的不同。
JNI_OnLoad函數(shù)JNI規(guī)范定義的,當(dāng)共享庫第一次被加載的時(shí)候會(huì)被回調(diào),
這個(gè)函數(shù)里面可以進(jìn)行一些初始化工作,比如注冊(cè)函數(shù)映射表,緩存一些變量等,
最后返回當(dāng)前環(huán)境所支持的JNI環(huán)境。本例只是簡單的返回當(dāng)前JNI環(huán)境。
第四步:
編譯生成so庫
編譯com_lucyfyr_HelloWorld.c成so庫可以和app一起編譯,也可以都單獨(dú)編譯。
在當(dāng)前目錄下建立jni文件夾:HelloWorld/jni/
下建立Android.mk ,并將com_lucyfyr_HelloWorld.c和 com_lucyfyr_HelloWorld.h 拷貝到進(jìn)去
編寫編譯生成so庫的Android.mk文件:
LOCAL_PATH:= $(call my-dir)
# 一個(gè)完整模塊編譯
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=com_lucyfyr_HelloWorld.c
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_MODULE := libHelloWorldJni
LOCAL_SHARED_LIBRARIES := libutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_TAGS :=optional
include $(BUILD_SHARED_LIBRARY)
系統(tǒng)變量解析:
LOCAL_PATH - 編譯時(shí)的目錄
$(call 目錄,目錄….) 目錄引入操作符
如該目錄下有個(gè)文件夾名稱 src,則可以這樣寫 $(call src),那么就會(huì)得到 src 目錄的完整路徑
include $(CLEAR_VARS) -清除之前的一些系統(tǒng)變量
LOCAL_MODULE - 編譯生成的目標(biāo)對(duì)象
LOCAL_SRC_FILES - 編譯的源文件
LOCAL_C_INCLUDES - 需要包含的頭文件目錄
LOCAL_SHARED_LIBRARIES - 鏈接時(shí)需要的外部庫
LOCAL_PRELINK_MODULE - 是否需要prelink處理
include$(BUILD_SHARED_LIBRARY) - 指明要編譯成動(dòng)態(tài)庫
網(wǎng)頁標(biāo)題:jniandroid的簡單介紹
標(biāo)題鏈接:http://vcdvsql.cn/article18/dsdesgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、響應(yīng)式網(wǎng)站、網(wǎng)站營銷、全網(wǎng)營銷推廣、企業(yè)建站、建站公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)