在Android IM即时通信多进程中间件设计与实现 一文中主要的点有两个
- 多进程完成客户端和服务端的通信
- 通信的介质
让SDK更通用
整个实现中其实我是不关心你到底使用websocket还是自己撸的TCP还是其他,我关心的是多进程中间件的实现。
所以我们要做一个设计,我希望在我的整个Demo 中不关心长链接的具体实现,但是我能在Demo 对整个长连接进行管理、使用,我的目标是:
- 在使用Demo时不需要考虑长连接的实现方式
- 在任何已经存在的长连接代码中,此Demo 均能实现其多进程中间件的职责
这样设计的优势在哪里?
- 可以更多的作为一个开放平台
- 不管以后发展中出现多牛逼的长连接方式,都不影响我们的业务
这个设计会具备以下特点
- 涉及核⼼功能统⼀管理升级;
- 保证核⼼功能具备⾼灵活替换性;
- Demo与长连接框架完全剥离,项⽬⾯向中间层编码,⽆须了解实际实现SDK
物理结构
中间件形成独⽴SDK,项⽬⾯向图⽚加载框架编码,所有功能统⼀管理;依赖关系⻅下图:
多进程中间件
当前的组件设计核⼼思想是彻底弱化长连接在项⽬中的定义,达到项⽬直接与IMClient接触,终极⽬标是项⽬只知道IMClient⽽不知道长连接,所以整体SDK呈现出了如下图的组件架构模式: 可以包含了多个不同的组件
具体实现
使用依赖反转原则进行高层逻辑实现
- LongConnectService
/**
* Create by kpa(billkp@yeah.net) on 2023/3/13
* 16:58
* Describe :整个实现中其实我是不Core你到底使用websocket还是自己撸的TCP还是其他,
* 我关心的是多进程中间件的实现。所以我们对这部分做一个
* 抽象连接器
*/
interface LongConnectionService {
/**
* 初始化长连接实例
*/
fun initLongConnection()
/**
* 连接长连接
*/
fun connect()
/**
* 断开长连接
*/
fun disConnect()
/**
* 重新连接长连接
*/
fun reConnect()
}
复制代码
然后我们使用抽象工厂模式提供对长连接器的统一创建
修改中间件IMClient
对于SDK⽽⾔,长连接器操作、配置等,需要提供统⼀的配置类以供APP进⾏配,可使用Builder模式,封装其职责,总的来说他将变为APP对其配置和操作的工具
class IMClient private constructor(builder: Builder) {
private var longConnectionFactory: IMLongConnectionFactory<LongConnectionService>? = null
init {
longConnectionFactory = builder.getFactory()
}
//...
companion object {
@Volatile
private lateinit var mInstance: IMClient
fun isInstalled() = this::mInstance.isInitialized
@JvmStatic
fun init(imClient: IMClient): IMClient {
synchronized(IMClient::class) {
if (!isInstalled()) {
mInstance = imClient
} else {
throw RuntimeException("已经初始化")
}
}
return mInstance;
}
@JvmStatic
fun with(): IMClient {
if (isInstalled()) {
throw RuntimeException("未初始化")
}
return mInstance;
}
}
class Builder {
private var mFactory: IMLongConnectionFactory<LongConnectionService>? = null
fun withFactory(factory: IMLongConnectionFactory<LongConnectionService>) = apply {
this.mFactory = factory
}
//如果不设置长连接器,将默认本Demo 中的DefaultWebsocketFactory
fun getFactory(): IMLongConnectionFactory<LongConnectionService> {
if (mFactory == null) {
mFactory = DefaultWebsocketFactory.create()
}
return mFactory!!
}
fun build(): IMClient {
return init(IMClient(this))
}
}
//...
}
复制代码
中间件使用
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
// 初始化
IMClient.Builder()
.withFactory(DefaultWebsocketFactory.create()).build()
// 使用
IMClient.with().connect()
IMClient.with().send("我是发送的消息内容")
复制代码
怎么接入自己的长连接代码
通过上面的类图,相信大家已经知道怎么接入了。
- 实现LongConnectionService 高级抽象
class DefaultLongConnectionImpl(private val mContext: Context) : LongConnectionService {
override fun initLongConnection() {
TODO("Not yet implemented")
}
override fun connect() {
TODO("Not yet implemented")
}
override fun disConnect() {
TODO("Not yet implemented")
}
override fun reConnect() {
TODO("Not yet implemented")
}
}
复制代码
- 创建对应的工厂创建器
class DefaultWebsocketFactory : IMLongConnectionFactory<DefaultLongConnectionImpl>() {
companion object {
@JvmStatic
fun create(): DefaultWebsocketFactory {
return DefaultWebsocketFactory()
}
}
override fun createLongConnection(context: Context): DefaultLongConnectionImpl {
return DefaultLongConnectionImpl(context)
}
}
复制代码
详细的流程设计
拒绝侵入三方SDK,让你的业务独立起来
在⽅案设计中,完成了项⽬与IMClient 的连接,也设计了核⼼长连接的的切换、功能抽象等⼯作,但是还存在以下问题:
- 长连接的代码对于使用自己代码的同学,他是对于的无用的
- 组件的职责不明确
但是处理方式极其简单,我们只需要将抽象的高层逻辑部分移动到其他组件中,在组件层面,也就是代码仓库中隔离开,这将不再存在以上问题。
总结
这种设计方式在开发中应用场景众多,基本上所有使用第三方组件的场景都应该按照如此设计,这样既能保证业务不受侵害,在将来业务走上正轨也不影响你替换组件,这样我们就做到了:
- 控制反转
- 组件层级分明
- 各组件间物理隔离
- 根据需求单独打包 (⽐如项⽬研发接⼊了2款长连接框架,上线时只需要其中之一,即将需要的进⾏物 理依赖,其他的取消依赖即可)
- 去除IMClinet中⼼化,多⼈开发不受影响(开发中间件和长连接将变成两个业务)
- 提升了组件稳定性
当然,组件的业务复杂程度不是我关心的,但是大家可以进行提取,丰富自己的业务。
代码: