IOT command (based on sip)client API设计 for java
我们实现的物联网设备控制是通过扩展sip协议来实现的。
由于是基于pjsip来实现的,而pjsip是使用C编程,如何使得业务层(android端,使用java)更容易使用提供的command API
是重点,原始的方法就是从底层C开始往上层层封装(c--->jni--->java),这样存在明显的缺陷:
1. 对于第三方开发开发难度大,工作量多,API设计不合理;
2. 不同的设备控制业务接入代码都需要集成到主程序iot_sip_cli,高耦合,这是不能接受的(特别对于第三方开发)。
所以倒过来考虑,利用IOC原则(don't call us, we'll call you),让第三方开发直接在java层来实现,提供接口给第三方让其将实现注册到iot_sip_cli_jni层,最后jni层的调用被iot_sip_cli驱动。
举例: 接入一个叫test_plugin的设备类型, 它有两个方法test1, test5.
1. 第三方开发首先定义方法的响应处理:
public class TestPluginCallBack {
//以下两个方法最终被iot_sip_cli调用
@CmdCBAnnotation(dev_type = "test_plugin", method_name = "test1")public void setId(int ret, String retJson) {}@CmdCBAnnotation(dev_type = "test_plugin", method_name = "test5")public void setId2(int ret, String retJson) {}
};
2.第三方开发将上面的对象,方法传递给 sdk(由sdk内部的IOTCmdAttachment负责):
IOTCmdAttachment att = IOTCmdAttachment.getSingleton();TestPluginCallBack cbObj = new TestPluginCallBack();att.registerCmdHandlers(cbObj);
3. 第三方开发,写设备控制的请求发送:
att.inputCommand("test_plugin", "test1", reqJson, toUri); //响应结果retJson通过第一步的方法传到业务层。
这样,第三方开发只要通过上面3个步骤,就能完成对接入设备控制的实现。
过程原理如下图所示(提供给第三方的sdk由CMD_API_4JAVA jar包,和IOT_CMD_JNI so构成):
![](https://images0.cnblogs.com/blog2015/342517/201508/181750219259512.png)
主要步骤在于IOTCmdAttachment方法registerCmdHandlers的使用:
利用自定义的java注解CmdCBAnnotation, 识别出需要调用的函数,将其传给 jni_reg_cmdcb(它内部调用reg_handler),
public int registerCmdHandlers(Object obj) { int status = 0; for (Method m : obj.getClass().getMethods()) { CmdCBAnnotation a = m.getAnnotation(CmdCBAnnotation.class); if (a != null) { Log.d(LOG_TAG, "" + a.dev_type() + ":" + a.method_name()); status = jni_reg_cmdcb(a.dev_type(), a.method_name(), obj, m); if (0 != status) return status; } } return status; }
static int reg_handler(jstring dev_type, jstring method_name, jobject jobj, jobject jmeth) { __android_log_print(ANDROID_LOG_INFO, TAGSTR, "reg_handler"); JNIEnv *env = get_jni_env(); std::string str_devtype = jstring2str(env, dev_type); std::string str_methname = jstring2str(env, method_name); //生成DeviceCmdHandler对象,存储设备类型名,设备方法名(即上面注解@CmdCBAnnotation dev_type, method_name值), //TestPluginCallBack对象,和被注解的java方法 DeviceCmdHandler * pt_dchdl = new DeviceCmdHandler(str_devtype, str_methname, jobj, jmeth); //DeviceCmdHandler::methodCallBack4c会使用pt_dchdl作为参数,并且它作为回调函数将被iot_sip_cli调用 int ret = cmd_cb_reg4cpp(str_devtype.c_str(), str_methname.c_str(), &DeviceCmdHandler::methodCallBack4c, pt_dchdl); if (0 != ret) { __android_log_print(ANDROID_LOG_ERROR, TAGSTR, "reg_handler fail ret %d:%s %s", ret, str_devtype.c_str(), str_methname.c_str()); } else { __android_log_print(ANDROID_LOG_INFO, TAGSTR, "reg_handler succ:%s %s", str_devtype.c_str(), str_methname.c_str()); } return ret; }
DeviceCmdHandler类如下:
![](https://images0.cnblogs.com/blog2015/342517/201508/181750452853508.png)
int DeviceCmdHandler::methodCallBack4c(int ret_i, const char * ret_json,
void * pt_obj) {
return ((DeviceCmdHandler *) pt_obj)->methodCallBack(ret_i, ret_json);
}
而 DeviceCmdHandler::methodCallBack(int ret_i, const char * ret_json)方法,最终调用
CallObjectMethod(env, this->mJmethod, mid, jo, (jobjectArray) texts); 即调用java层的TestPluginCallBack两个注解的方法。
mid获取, 使用java.lang.reflect.Method:
JLocalRef<jclass> clazz = env->GetObjectClass(this->mJmethod);
jmethodID mid = env->GetMethodID(clazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")