HAL中通過jni調用java方法的問題


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







 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM