你的位置:首页 > 操作系统

[操作系统]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构成):

 
主要步骤在于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类如下:


 
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;")