转载请注明本文出处: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. .net变量判断

    <div class="AccountLevel" style="margin-top: 15px;">                <sp ...

  2. 测试环境下将centos6.8升级到centos7的操作记录(转)

    在测试环境下安装openstack,由于在centos6下安装openstack,针对源的问题有很多,安装起来很不顺利! 但是在centos7下安装却很顺利,所以考虑将服务器由centos6升级到ce ...

  3. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  4. PHP 数组和字符串互相转换实现方法

    $array=explode(separator,$string); $string=implode(glue,$array);

  5. tomcat 禁用不安全的http请求模式 .

    HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的.当然,所有的方法支持的实现都应当符合下述的方法各自的语义定义.此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法. ht ...

  6. 执行sql失败之后,再次执行的时候提示:ora-02429:无法删除用于强制唯一/主键的索引

    SQL 1:select segment_name,partition_name,tablespace_name from --显示出表的主键的表空间是:SYSTEM --以下是生成删除主键约束的脚本 ...

  7. Java控制语句——while语句

    while循环 在循环刚开始时,会计算一次“布尔表达式”的值,若条件为真,执行循环体,而对于后来每一次额外的循环,都会在开始前重新计算一次. 注意:语句中应有使循环趋向于结束的语句,否则会出现无限循环 ...

  8. PLSQL 申明和游标

    --从键盘输入一个数 accept b prompt '请输入一个大于零的数字'; declare anum number := &b; begin loop dbms_output.put_ ...

  9. iOS - (几个 button 按钮之间的单选与多选)

    先来看看效果图: 下面是实现的代码: 首先创建10个button(一个一个写太麻烦了,个人认为还是用一个 for 循环来创建比较好) 下面就是 button 的点击方法实现单选 多选的比较好做,写法也 ...

  10. Linux启动时卡住

    该系统本是oracle rac的测试环境,在删除oracle软件后重启时系统卡住(没有按照oracle官方要求删除oracle软件).如下图: 处理过程: 1.使用单用户模式登陆 先在GRUB启动菜单 ...