Android 做完Message SDK后的一点点思考
思考与总结:
(1)数据页面分离,采用消息通知刷新页面(因为无法后台保存页面无论是fragment还是activity,因此就无法做到最小化,有人说采用1像素的透明activity,这样其实是不行的,焦点事件还是会被透明activit消费导致无法向下传递,会让你误以为你以为界面卡死掉了。除非自己处理各种焦点事件和兼容性问题)
(2)能用Fragment时BottomSheetDialogFragment少用,会与系统授权框冲突,导致界面点不动
(3)service使用,想要service拥有生命周期,配合LifecycleRegistry使用最佳
(4)抽象工厂创建页面
(5)公有的viewHolder里设置监听事件,一定要确保是同一对象
(6)recycleview需要结合handler使用,否则无法自动彻底的滑到底部,recycleview默认使用之后不该加的属性不加,莫名的crash.
(7)recycleview 加载图片时其实是在viewHolder里一直创建的, 并且在需要监听图片Glide加载过程状态,一定要有个全局map缓存池,否则上下重复滑动巨卡顿。
(8)surfaceview crash:1因为没有给线程sleep 2. 阻塞采用join等待task结束,防止draw时实质已经对象状态已经是异常状态
(9)自定画在docview上的view作为dialog时,@Note: the child layout of ViewGroup[rootContainer] cannot contains property: android:elevation="1dp",否则有layer视差
(10)SDK做成可配置资源时,多种颜色的svg线条图不能简单的用tint方案实现,自定义控件达到多种颜色同时改掉。除非是一张多种颜色图但只有主颜色变化其他颜色可更改主颜色alpha值则可以修改drawable使其不变,然后可以直接用tint,不需要自定义view
(11) 必须使用:-flattenpackagehierarchy com.yourOrganization.packagename ,避免打出来的sdk包被其他项目引用时包名冲突 example: dulpulicate a.a ....
(12)生成AAR后要么采用maven发布产生pom,要么host使用aar时自己配置aar所需要的dependencies,否则会找不到类crash
(13)R8优化了很多,gradle 7.0.4 默认带的是R8 x.36.x版本,会忽略kotlin 1.6.0版本的反射库,app运行会crash,需要手动配置升级R8 x.37.x以上版本。
(14)可以通过consumerProguardFiles配置,把少量需要外部配置的-keep配置直接打进aar,这样外面使用者就不用再配置了。(比如aar已经混淆过了外部无需再混否则不该混的被外面再混一次会找不到类crash)
(15)聊天应用程序应采用本地管理消息索引index,,因为服务器数据与本地数据存在gap,本地可能存在未成功需要重试的消息,索引与服务器不一定能完全对上,另外还需要全局map管理标记正在上传的消息,以便知道接下来如何处理异常消息,比如发送失败后该显示怎样的界面,另外多个前端设备同时聊,归类问题核心处理如下:
override fun onReceivedNewMessage(message: com.xxx.xxxx.Message?) {
val previousSize = mAllMsgList.size
Log.d(
TAG,
"AgentConnectionService onReceivedNewMessage: previous size=$previousSize"
)
if (message == null) {
Log.d(TAG, "Illegal messages ,message can not be null.")
return
}
//val newMsgIndex = mAllMsgList.size
if (ChatFloatingView.get().isFloatingVisible) {
QuickstartConversationsManager.getInstance()
.getUnreadMessagesCount { num ->
Log.d(TAG, "number=$num")
ChatFloatingView.get()
.setSpecifiedViewVisible<TextView>(R.id.tv_unread_num, View.VISIBLE)
}
}
if (previousSize > 0) {
//minus current and typing that represents previous Last Msg Index.
val previousLastMsgIndex = previousSize - 2
val typingMsgId = this.getSpecifiedMessageId(
previousLastMsgIndex,
MessageType.TYPING_MSG
)
if (!TextUtils.isEmpty(typingMsgId)) {
this.deleteByIdAndSendMsgBroadcast(
State.STATE_RECEIVED_MSG_NEW_MSG,
typingMsgId!!
)
}
}
//do not use twilioMsg index.
//val newIndex = message.messageIndex;
val size = mAllMsgList.size
val lastMsgIndex: Long = if (size > 0) {
((size - 1).toLong())
} else {
0L
}
val mediaList = message.attachedMedia
val user = MessagesFixtures.getUser(message.author, message.author, "")
val mCurrentAuthor = QuickstartManager.getInstance().myIdentity
val isMySelf = mCurrentAuthor != null && mCurrentAuthor == message.author
//handle image message.
if (mediaList.size > 0) {
val mediaMsg = message.attachedMedia[0]
mediaMsg.getTemporaryContentUrl { result ->
if (isMySelf) {
val loadingMsg = SDKProviderDelegate.getUploadingMessage(
mediaMsg.filename,
true,
"onReceivedNewMessage"
)
if (loadingMsg != null && !TextUtils.isEmpty(loadingMsg.id)) {
//update
// val loadingMsgIndex =
// this.getMessagePositionById(loadingMsg.id)
val newMsg = MessagesFixtures.getTextMessage(
loadingMsg.index,
message.sid,
user,
message.body
)
val imageMsg =
if (checkValidType(mediaMsg.contentType)) newMsg.setAsImageMsg(
Message.Image(result, 0), MessageState.UPLOADING
) else newMsg.setAsErrorTypeMsg(Message.Image("", 0))
this.syncMessageListWithBroadcast(
ACTION_UPDATE, State.STATE_RECEIVED_MSG_NEW_MSG,
imageMsg, loadingMsg.id
)
return@getTemporaryContentUrl
}
val newMsg = MessagesFixtures.getTextMessage(
lastMsgIndex + 1,
message.sid,
user,
message.body
)
val imageMsg =
if (checkValidType(mediaMsg.contentType)) newMsg.setAsImageMsg(
Message.Image(
result,
0
), MessageState.UPLOADING
) else newMsg.setAsErrorTypeMsg(Message.Image("", 0))
this.syncMessageListWithBroadcast(
ACTION_APPEND,
State.STATE_RECEIVED_MSG_NEW_MSG,
imageMsg
)
} else {
val newMsg = MessagesFixtures.getTextMessageWithDate(
lastMsgIndex + 1,
message.sid,
user,
message.body,
message.dateCreatedAsDate
)
val imageMsg =
if (checkValidType(mediaMsg.contentType)) newMsg.setAsImageMsg(
Message.Image(
result,
0
), MessageState.LOADING
)
else newMsg.setAsErrorTypeMsg(Message.Image("", 0))
this.syncMessageListWithBroadcast(
ACTION_APPEND, State.STATE_RECEIVED_MSG_NEW_MSG,
imageMsg
)
}
}
return
}
//handle text message
if (isMySelf) {
val loadingMsg =
SDKProviderDelegate.getUploadingMessage(
message.body,
true,
"onReceivedNewMessage"
)
if (loadingMsg != null && !TextUtils.isEmpty(loadingMsg.id)) {
//update
// val loadingMsgIndex =
// this.getMessagePositionById(loadingMsgId)
val newMsg = MessagesFixtures.getTextMessage(
loadingMsg.index,
message.sid,
user,
message.body
)
this.syncMessageListWithBroadcast(
ACTION_UPDATE,
State.STATE_RECEIVED_MSG_NEW_MSG,
newMsg,
loadingMsg.id
)
return
}
//new msg:
val newMsg = MessagesFixtures.getTextMessage(
lastMsgIndex + 1,
message.sid,
user,
message.body
)
this.syncMessageListWithBroadcast(
ACTION_APPEND,
State.STATE_RECEIVED_MSG_NEW_MSG,
newMsg
)
} else {
val newMsg = MessagesFixtures.getTextMessageWithDate(
lastMsgIndex + 1,
message.sid,
user,
message.body,
message.dateCreatedAsDate
)
this.syncMessageListWithBroadcast(
ACTION_APPEND,
State.STATE_RECEIVED_MSG_NEW_MSG,
newMsg
)
}
Log.d(
TAG,
"receivedNewMessage: localIndex=" + (lastMsgIndex + 1) + "|serverIndex=" + message.messageIndex + "|" + message.participantSid + "|" + message.author + "|" + message.conversationSid + "|" + message.subject
)
if (message.participant != null) {
Log.d(
TAG,
"receivedNewMessage: participantIdentity=" + message.participant.identity
)
}
}
@Nullable
public static Message getUploadingMessage(String uniqueKeyName, boolean handleFinished, String logTag) {
if (TextUtils.isEmpty(uniqueKeyName)) {
ConversationLog.INSTANCE.d("getLoadingMsg uniqueKeyName=null,please check params of 'msgId',source:" + logTag);
return null;
}
Message loadingMsg = getInstance().mGlobalLoadingMsgMap.get(uniqueKeyName);
if (handleFinished) {
getInstance().mGlobalLoadingMsgMap.remove(uniqueKeyName);
}
ConversationLog.INSTANCE.d("getLoadingMsg uniqueKeyName=" + uniqueKeyName + "|valueId=" + (loadingMsg != null ? loadingMsg.getId() : "") + "|leftSize=" + getInstance().mGlobalLoadingMsgMap.size() + "|source:" + logTag);
return loadingMsg;
}
public static void cacheUploadingMsgId(String uniqueKeyName, Message value) {
if (TextUtils.isEmpty(uniqueKeyName)) {
return;
}
ConversationLog.INSTANCE.d("putLoadingMsg uniqueKeyName=" + uniqueKeyName + "|valueId=" + value.getId());
//put a new or old will be replaced.
getInstance().mGlobalLoadingMsgMap.put(uniqueKeyName, value);
}
-----------------------------End-----------------------------