项目结构
插件内目录src/ios下面有一个Xcode项目和plugin接口。
Xcode项目TSBlueTherm 项目内主要有3个target,分别是:
-
TSBlueTherm
,实现BlueTherm测温仪交互功能的framework。 -
TSBlueTherm-Universal
,把前者编译成universal库,并且拷贝到项目的src/ios目录下进行发布。 -
TSBlueThermDemo
,测试用App,App运行framework中的功能。开发者无需把plugin引入到cordova项目中,通过该App可以大致测试和调试framework。
接口TSBlueThermPlugin.m 接口部分并不实现具体的功能,接口只是简单调用framework中的功能。
设计理念
项目的关键是,接口不实现具体功能而是通过调用framework提供功能。我们可以把这种设计模式总结成「源码封装抽离」。该模式有以下好处:
-
方便配置和管理。 相对于「源码封装抽离」,本项目将源码直接放置在plugin之内。后者设计较多的plugin.xml的配置问题,尤其当引入第三方库的时候,如果存在大量的源文件和资源,则plugin.xml的配置将非常繁琐。一旦配置出错的plugin很可能无法正常运作。这将会增加调试的工作量。 「抽离」模式plugin.xml的配置相对稳定,开发以及第三方资源封装在framework内。开发精力集中在framework上,而无需太多关注配置问题。如果有需要快速开发、调试,甚至可以直接替代项目中的framework(如果接口不变),而无需完整发布新的plugin。
-
方便调试。 Cordova项目引入、更新plugin的操作并不复杂,但相当繁琐。在开发调、试过程频繁操作会相当影响效率。「抽离」模式极大降低plugin的更新引入操作。开发者可以先对framework进行完整的开发、调试,待稳定可靠之后再引入到cordova项目下做完整的环境和系统测试。 另一方面,这种开发方式脱离具体的业务流程,只关注plugin的功能,同样可以提高开发效率。
项目配置
使用BlueTherm SDK需要对info.plist做一些额外的配置:
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>uk.co.etiltd.bluetherm1</string>
</array>
或者在xcode中info.plist添加下面配置
JavaScript接口
module.exports = {
takeTemp: function (success, error) {
exec(success, error, "blue-therm", "takeTemp", []);
},
stopTakeTemp: function () {
exec(null, null, "blue-therm", "stopTakeTemp", []);
},
getBatteryLevel: function (success, error) {
exec(success, error, "blue-therm", "getBatteryLevel", []);
}
};
兼容情况
过往项目,例如kudu-routines
,yum-routines
,也使用到BlueTherm插件com.myself.blueTherm
。由于有多个版本,因此插件的情况不能一概而论,但基本都类似。
为了兼容以往项目TSBlueTherm做了最大程度的兼容,正常情况开发者能够在不修改代码的情况下进行替换。 然而在逻辑方面新旧插件有着比较大的差异,下面只列出一些关键性的兼容问题。
插件id
本插件对插件id进行了修改,id从之前的com.myself.blueTherm
改为tts-plugin-blue-therm
。开发者如果使用本插件,必须先移除旧插件。
库冲突 由于两个插件使用的是同一个SDK库,本插件已经对该库进行封装。如果旧项目引入了BlueTherm的SDK库,开发者将之移除,否则可能造成冲突。
旧插件的接口缺陷
旧插件设计之初并未结合BlueTherm设备的实际运作方式进行开发,造成旧插件的接口实际并不切合设备的使用。考虑到已有项目的兼容问题,本插件在当下版本(1.0.0)仍然沿用过去的接口,但内部的运作大有不同。下面举两个例子:
-
getBatteryLevel
接口 旧插件误认为电池信息是直接访问,所以直接调用[ProbeProperties batteryLevel]
以获取数据。而事实上,SDK需要先链接设备,然后向设备请求数据包,返回的数据包包含设备状态,电池情况,温度等。访问电池信息的逻辑其实与获取温度一样。实现方面插件应该把SDK的数据包转换成json,通过success回调返回给前端。事实上更合理的接口类似:
readData: function (success, error) {
...
}
- 异常处理 旧接口并没有实际使用error回调进行异常处理。在实际的运行中,旧插件只对部分异常通过success回调传送回前端(通过不同的信息内容标识异常)。事实上旧插件并没有正确处理交互中的异常。正因如此插件的运作异常可能因为没有得到有效处理导致App的崩溃。此外,因为异常没有得到合适处理,插件内部的运作出现问题的时候,APP也很难得知情况。 新插件并没有修改接口,但在内部做了必要的处理。尽可能确保插件的异常不至于造成App崩溃。
接口方面还有其他一些问题,这里不再一一列举。这里只是作为一个备忘,在资源允许的情况下,未来的版本应该对接口做一些合理的修改。