转载请注明本文出处:http://www.cnblogs.com/xl19862005

作者:Xandy

由于工作的需要,最近一直在研究HAL、JNI、Java方法之间互调的问题,并做了如下一些记录和大家一起分享!

工作背景:所调试的是一款叫goc-md-102的车载蓝牙模块,由于这款蓝牙模块无法直接用HCI的方式控制,而它已经有了现成的一套AT命令集进行控制,所以我在HAL中直接通过串口读写的方式进行通信,然后通过JNI和java层建立联系。

考虑到效率的问题,我在HAL中用回调函数的方式通过JNI与java层交换数据,看了一下GPS数据上报的方法正和我用的这个方法一样!

1、首先是在HAL中串口的开启、初始化和读写,这些都比较简单,主要看看初始化这个函数中的代码,如下:

/***************************************************************
** fun: init gocmd102_init(/dev/ttymxc1);
** in:
** out: fd sucess, -1 false;
** gocmd102_init
***************************************************************/
static int gocmd102_init(BluetoothCallback *callBack)
{
int fd,var;
btportinfo pPort_info;
int err; pReceiveCmdPackage = malloc(sizeof(bluetooth)); memset(pReceiveCmdPackage,0,sizeof(bluetooth));
memset(recCmdBuf,0,RECCMDBUFLEN);
//clear message buf
memset(&pPort_info,0,sizeof(btportinfo)); fd=open_bluetoothPort(); if(fd < 0)
{
LOGE("gocmd102_init open port error!");
return -1;
} pReceiveCmdPackage->fd= fd;
FD = fd; pPort_info.baud_rate=GOCMD102_BAUD;
pPort_info.data_bits=GOCMD102_DATABIT;
pPort_info.flow_ctrl=GOCMD102_CTRL;
pPort_info.stop_bit=GOCMD102_STOPBIT;
pPort_info.parity=GOCMD102_PARITY;
pPort_info.port_fd=fd; //pthread_mutex_lock(&pPort_info.portlock);
var = set_btportLocked(&pPort_info);
//pthread_mutex_unlock(&pPort_info.portlock); if(var < 0)
{
LOGE("set_portLocked error!");
return -1;
} //在这里将获得输入的函数结构体指针,在后继数据上报的时候将通过这个函数结构体指针来实现
if(callBack != NULL)
pReceiveCmdPackage->callback = *callBack;
else
{
LOGE("BluetoothCallback struct is empty!");
return -1;
} //uart receive message thread and analyze it
sem_init(&pReceiveCmdPackage->uart_end, 0, 0);
pReceiveCmdPackage->uart_inited = true; //err = pthread_create(&pReceiveCmdPackage->thread_id, NULL, &BTuartDownloadData, (void *)pReceiveCmdPackage);   //在这这里,通过callback的方式创建了一个线程,用于串口数据的读取和上报,这个线程是在VM中创建的一个java线程,一定要用这个,而不能用pthread_create,否则会出问题!
pReceiveCmdPackage->thread_id = callBack->bluetooth_thread("gocmd102_bluetooth", BTuartDownloadData, pReceiveCmdPackage); if (!pReceiveCmdPackage->thread_id)
{
LOGE("could not create bluetooth thread: %s", strerror(errno));
return -1;
} return fd;
}

当蓝牙打开时,上层app通过JNI调用到这个init函数完成串的初始,同时将JNI中调用java方法的函数结构体地址传入了进来,这个函数结构体如下:

typedef struct
{
size_t size;
void (*callIn_bt)(telephoneIn *callIn);
void (*state_bt)(int state);
void (*getVol_bt)(BtVol *vol);
void (*connect_bt)(matchDev *btDevice);
void (*match_bt)(matchDev *btDevice);
void (*downPHBook_bt)(phoneNumber *phoneNum);
void (*callOut_bt)(phoneNumber *dailNum);
pthread_t (* bluetooth_thread)(const char* name, void (*start)(void *), void* arg);
}BluetoothCallback,*pBluetoothCallback;

这个函数结构体里的回调函数在JNI中实现,我们来看看电话打入的时候上报电话号码的这个回调函数:

static void telephoneIn_callback(telephoneIn *callIn)
{
JNIEnv* env = AndroidRuntime::getJNIEnv();
jstring number = env->NewStringUTF(callIn->number); dbg(DBG_DEBUG," JNI telephoneIn_callback");
//调用java方法上报数据
env->CallVoidMethod(mBTCallbackObj,method_reportCallIn,callIn->Len,number); if(number)
env->DeleteLocalRef(number);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}

这里mBTCallbackObj(jobject)是在java调用jni初始化的时候赋值的,应该是获得对应的java类,而method_reportCallIn(jmethodID)是获得的java中对应的java方法ID,如下:

static void android_location_BlueToothLocationProvider_class_init_native(JNIEnv* env, jclass clazz) 
{
method_reportCallIn = env->GetMethodID(clazz, "telephoneCallIn", "(ILjava/lang/String;)V");
method_reportState = env->GetMethodID(clazz, "bluetoothState", "(I)V");
method_reportVol = env->GetMethodID(clazz, "reportVol", "(II)V");
method_reportConnect = env->GetMethodID(clazz,"reportConnect","(Ljava/lang/String;[I)V");
method_reportMatch = env->GetMethodID(clazz,"reportMatch","(ILjava/lang/String;[I)V");
method_reportPhoneBook = env->GetMethodID(clazz,"reportPhoneBook","(IILjava/lang/String;Ljava/lang/String;)V");
method_reportDailNum = env->GetMethodID(clazz,"reportDailNumber","(Ljava/lang/String;)V");
}

再来看看jni中创建java线程的回调函数:

static pthread_t bluetooth_thread_callback(const char* name, void (*start)(void *), void* arg)
{
return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
}

在相应的java类中电话打入的时候,号码上报的方法如下:

    /**
* called from native code to update call in telephone number
*/
private void telephoneCallIn(int numberLen, String number)
{
if(DEBUG)
Log.v(TAG, "telephoneCallIn number: " + number); if(numberLen <= 0)
Log.e(TAG,"telpphone call in,but the phone number is null"); if(number != null)
{
// send an intent to notify there is a telephone call in.
Intent intent = new Intent(TELEPHONE_CALLIN_ACTION);
intent.putExtra(EXTRA_BT_PHONENUMBER, number);
mContext.sendBroadcast(intent);
} }

最后再来看看HAL中是如何通过这个回调函数上报数据的,当解析得到电话打入时,将会进入到如下黄色标注的这部分代码上报打入的电话号码:

static void processCharacterI(pBluetooth bt,const uuint8 *data)
{
const uuint8 *pdata = data; dbg(DBG_DEBUG,"processCharacterI : %s",pdata); switch(*pdata)
{
case 'D':
{
telephoneIn callIn; memset(&callIn,0,sizeof(telephoneIn)); callIn.Len = (*(++pdata)-0x30)*10;
callIn.Len += *(++pdata)-0x30;
callIn.number = ++pdata;
BLUETOOTH_CALLIN_CB(bt->callback,callIn);
}
break; case 'S':
BLUETOOTH_STATE_CB(bt->callback,uartInitOK);
break; case 'C':
{
phoneNumber CallOut;
uuint32 tmp=0; memset(&CallOut,0,sizeof(phoneNumber)); tmp = (*(++pdata)-0x30)*10;
tmp += *(++pdata)-0x30;
CallOut.nameLen = tmp; CallOut.number = ++pdata; BLUETOOTH_CALLOUT_CB(bt->callback,CallOut);
}
break; default:
LOGW(" Unknow command : I%s",pdata);
break;
}
}

其中BLUETOOTH_CALLIN_CB这个是如下定义的:

#define BLUETOOTH_CALLIN_CB(_cb,_in)    \
if((_cb).callIn_bt){ \
(_cb).callIn_bt(&(_in)); \
}

这样完整的数据上报链路就已经打通了,当有相应的数据从串口发送出来时就会通过这套回调函数将数据上报至java方法中,最后在java方法中通过广播发给对应的监听者!

HAL中通过JNI调用java方法【转】的更多相关文章

  1. java native interface JNI 调用Java方法

    在上一篇文章中介绍了JNI.以及java调用JNI.这篇讲一下 JNI调用java方法. 通过使用合适的JNI函数,你能够创建Java对象,get.set 静态(static)和 实例(instanc ...

  2. Android Studio NDK开发-JNI调用Java方法

    相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如callback,系统信息等.... 如何在JNI中调用Java方法呢?就需 ...

  3. cocos2d-x中使用JNI的调用JAVA方法

    用cocos2d-x公布Android项目时.都应该知道要用JAVA与C/C++进行交互时会涉及到JNI的操作(Java Native Interface).JNI是JAVA的一个通用接口.旨在本地化 ...

  4. 第5篇-调用Java方法后弹出栈帧及处理返回结果

    在前一篇 第4篇-JVM终于开始调用Java主类的main()方法啦 介绍了通过callq调用entry point,不过我们并没有看完generate_call_stub()函数的实现.接下来在ge ...

  5. java 中使用ajax调用后台方法注意事项

    java 中使用ajax调用后台方法注意事项,后台方法一定要加@ResponseBody jQuery.validator.addMethod("checkRuleName",fu ...

  6. init.rc文件中面启动c++程序,通过jni调用java实现

    </pre><p>注:假设是自己的myself.jar包,还要修改例如以下:</p><p>target/product/core_base.mk PRO ...

  7. C++调用JAVA方法详解

    C++调用JAVA方法详解          博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...

  8. C#调用Java方法(详细实例)

    C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具体例子把一个JAVA方法转换成可以由C#直接调用的DLL C#调用c++ C#调用 ...

  9. C#调用Java方法

    C#调用Java方法(详细实例) 阅读目录 C#调用c++ C#调用JAVA方法 C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具 ...

随机推荐

  1. LeetCode Longest Increasing Path in a Matrix

    原题链接在这里:https://leetcode.com/problems/longest-increasing-path-in-a-matrix/ Given an integer matrix, ...

  2. LeetCode Basic Calculator

    原题链接在这里:https://leetcode.com/problems/basic-calculator/ Implement a basic calculator to evaluate a s ...

  3. route命令(转)

    Linux系统的route命令用于显示和操作IP路由表(show / manipulate the IP routing table).要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或 ...

  4. [BS-09] UITabBarController简单介绍

    iOS开发UI篇—UITabBarController简单介绍 一.简单介绍 UITabBarController和UINavigationController类似,UITabBarControlle ...

  5. 再探Java基础——throw与throws

    http://blog.csdn.net/luoweifu/article/details/10721543 异常处理机制 异常处理是对可能出现的异常进行处理,以防止程序遇到异常时被卡死,处于一直等待 ...

  6. Hadoop学习(4)-- MapReduce

    MapReduce是一种用于大规模数据集的并行计算编程模型,由Google提出,主要用于搜索领域,解决海量数据的计算问题.其主要思想Map(映射)和Reduce(规约)都是从函数是编程语言中借鉴而来的 ...

  7. Baseline模板管理

    SQL> alter session set NLS_DATE_FORMAT= 'yyyy-mm-dd hh24:mi:ss'; 创建单一基线模板: SQL> exec dbms_work ...

  8. css 正方体

    <!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf ...

  9. Java基础之写文件——在通道写入过程中的缓冲区状态(BufferStateTrace)

    控制台程序,在Junk目录中将字符串“Garbage in, garbage out\n”写入到名为charData.txt的文件中. import static java.nio.file.Stan ...

  10. linux: centos设置ip以及连接外网

    注明:我使用的的使centos 7,所有文件名是ifcfg-enp0s3, 一. 设置虚拟机中linux的ip,使本地能连通虚拟机的linux系统 1>.进入本地windows的cmd,输入ip ...