因为我写文章水平不好,解释的也不好,建议下载代码一起食用
GitHub
里面可能写了一些其他我在尝试的东西,但是不影响根据文章了解框架
整体框架图
消息Bean的关系图
消息Bean类看起来好像比较多层级,但是我感觉这样分比较清晰,而且也可以无限扩展
消息派发流程图
一个非常简单的流程和设计,但是消息派发的过程十分清晰
进入代码
IMManger 消息的入口
这里可以看到,添加了IM的消息监听后,直接把消息传递给了 IMDispatcher,让他去做消息的处理(这里取名字取得不是很好)
IMDispatcher 消息的处理器 首先看到IMDispatcher定义的一些变量
有解析器的List,拦截器对象,分发器工厂List
而解析消息就会用到上面几个变量,而整个调用过程是这样的
-
首先会遍历解析器集合,只要添加到解析器List中,他就会去遍历他,直到解析出该消息为止。这里为什么要用集合呢,因为你的解析器可能会有多种,假设你System消息分成了5组大的类型,他们的解析方法可能有一些差异,这时,你只需要写5个解析器并且添加到List就行了,省去了只有一个解析器时,做各种if判断操作
-
接着到拦截器,拦截器就是一个实例对象,我想着拦截消息应该不会有太多,所以直接弄成实例好了
-
最后是分发器,只要添加到分发器工厂List,实现接口方法,就能获得你想要的消息,对于使用者来说,根本不需要关心你之前的操作,我只要实现接口就行了
实际应用
以传递一个自定义消息来举例
1.看一下自定义消息的基类
abstract class BaseCustomMsg<T> : BaseMsgBean() {
// 当你发送一条消息时,会传入一个你的Bean,会转换成String保存在这
private var param = ""
// 当你获取消息时候,paramBean就是你当前消息的Bean类
@Expose(serialize = false)
var paramBean: T? = null
get() {
if (field == null) {
val t = TypeUtils.findNeedType(javaClass)
// 因为发送时,param已经存着发送方Bean类的String
// 所以你获取消息时,获取paramBean,这里就会做转化,确保你拿到的是是一个Bean实例
field = TypeUtils.fromJson<T>(param, t)
}
return field
}
private set
/**
* 这里是创建消息时调用的,所有自定义消息创建发送时,都要调用此方法,目的是
* 1.将你要传递的Bean转换成String传递
* 2.将你的消息赋值一个msgAction,作为消息的标识
*/
open fun createMsg(paramBean :T){
// 新建一个传递的数据对象
val dataObj = JSONObject()
dataObj.put("msgAction", getAction())
// 转成String
param = TypeUtils.toJson(paramBean)
// 放入value保存
dataObj.put("value", param)
val timCustomElem = TIMCustomElem()
timCustomElem.data = dataObj.toString().toByteArray()
mTxMessage.addElement(timCustomElem)
}
/**
* 这里是获取消息解析时调用的,所有自定义消息解析时,都要调用此方法
* 为了解析出msgAction和传递过来的值
*/
override fun addMsgContent(mTxMessage: TIMMessage): BaseMsgBean {
val element = mTxMessage.getElement(0) as TIMCustomElem
val dataJson = String(element.data)
val jsonObject = JSONObject(dataJson)
msgAction = jsonObject.optString("msgAction")
param = jsonObject.optString("value")
return this
}
}
复制代码
2.接着如果我想定义一个Pk请求消息,并且需要传递一些参数,那么我就可以这样定义了,非常的简单,也不需要什么其他东西,只需要你的bean类和给该消息一个标识msgAction
class PkReqMsg : BaseCustomMsg<PkReqMsg.PkMsgParam>() {
data class PkMsgParam(
var playUrl: String = "",
var isAgain: Boolean = false,
var pk_id: String = ""
)
override fun getAction() = MsgType.CUSTOM_PK_REQ
}
复制代码
3.那么怎么发送该Pk请求消息呢?
- 可以看到createMsg接受了我们想要携带的参数Bean,而基类的createMsg会将bean类转成String,再赋值给param,接着和msgAction一起打包发送出去,可以回顾一下基类看一下
// 消息请求bean
val pkReqMsg = PkReqMsg()
// 你要携带的参数Bean
val pkMsgParam = PkReqMsg.PkMsgParam("pk消息", false, "12345")
// 调用基类的方法
pkReqMsg.createMsg(pkMsgParam)
// 发送
IMManager.sendMessage(pkReqMsg)
复制代码
4.那么怎么解析消息
- 解析消息(注意TIMElemType的类型是腾讯自己定义的)
- 根据MsgAction去找到对应的消息类型,newInstance实例化出来,接着调用基类的方法的decorateMsg,最终最调用到 addMsgContent 去获取内容
5.怎么获取消息呢?
- 添加,获取,移除
6.更加简单的获取方式,这里不做阐述,可以根据代码看一下
只需要填入msgAction对应的消息bean,就可以直接获取消息了,
避免了像上面一个,在一个方法中,做各种判断去转型再拿消息
结尾
这个框架只是用腾讯IM做个例子,其他即时通讯应该可以套用一下,如果你有更加好的架构想法,欢迎加好友交流,评论区交流哈,多交流才能发现不足,也能发觉到自己想不到的层面