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;")
原文地址:https://www.cnblogs.com/europelee/p/4740118.html