IOT command (based on sip)client API設計 for java,iotsip
我們實現的物聯網裝置控制是通過擴充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;")