转载请注明本文出处: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方法中通过广播发给对应的监听者!