轉載請註明本文出處: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方法中通過廣播發給對應的監聽者!