这里会显示出您选择的修订版和当前版本之间的差别。
android_sdk详解 [2017/08/23 08:44] |
android_sdk详解 [2017/08/23 08:44] (当前版本) |
||
---|---|---|---|
行 1: | 行 1: | ||
+ | ======Android SDK详解 ====== | ||
+ | 云之讯建议开发者不仅要关心如何使用云之讯sdk,同时也需要理解云之讯sdk的架构设计和内部实现原理,这样方便开发者排查问题。要理解云之讯sdk,需要开发者具有一定的开发经验和编程基础。 | ||
+ | ===== 一、 架构设计 ===== | ||
+ | 云之讯提供uikit sdk、im sdk、tcp sdk方便开发者调用,它们之间的关系可以用一幅图来表示:\\ | ||
+ | {{ :android_sdk_r.png?nolink&180 }} | ||
+ | * 1) TCP SDK:负责与服务器的交互,包括收发消息、断开与连接。 | ||
+ | * 2) IM SDK:负责消息的存储和转发。 | ||
+ | |||
+ | * 3) UIKIT SDK:负责上层UI的展示和用户自定义界面。 | ||
+ | 这里以发送消息和收到消息为例,详解sdk之间的调用流程。\\ | ||
+ | <html><span style="font-size:130%;">发送消息流程:</span></html> | ||
+ | {{ :android_sendMessage_process.png?nolink&680 }} | ||
+ | <html><span style="font-size:130%;">接受消息流程:</span></html> | ||
+ | {{ :android_receiveMessage_process_01.png?nolink&680 }} | ||
+ | ===== 二、 会话与消息 ===== | ||
+ | 会话与消息属于一对多关系,消息不可能脱离会话而单独存在。消息可以根据<html><font color = red>targetId</font></html>找到所属会话,会话也能通过<html><font color = red>targetId</font></html>获取到消息列表。会话记录的入库操作是<html><font color = red>sdk</font></html>自动完成的,当发送或者受到消息所属的会话不存在时会创建会话,否则只会更新会话。\\ | ||
+ | 对于发送消息来讲,只有<html><font color = red>sendMessage</font></html>函数返回成功之后才会将消息记录入库。如果该消息所属会话不存在会创建会话,否则只会更新会话。待收到发送结果之后,会更新消息发状态和会话,同时回调下面两个函数: | ||
+ | * 1) <html><font color = red>onSendMsgRespone</font></html>:将已经更新好的消息回调给开发者。 | ||
+ | |||
+ | * 2) <html><font color = red>onUpdateConversation</font></html>:将已经更新好的会话(该会话可能是创建也有可能是更新)回调给开发者。 | ||
+ | 对于接收消息来讲,<html><font color = red>sdk</font></html>收到新消息自动将消息记录入库,如果该消息所属会话不存在会创建会话,否则只会更新会话,同时回调下面两个函数:\\ | ||
+ | * 1) <html><font color = red>onReceiveMessage</font></html>:将收到的消息回调给开发者。 | ||
+ | |||
+ | * 2) <html><font color = red>onUpdateConversation</font></html>:将已经更新好的会话(该会话可能是创建也有可能是更新)回调给开发者。 | ||
+ | <html><span style="font-size:130%;">会话与消息更新流程:</span></html> | ||
+ | {{ :messageAndCon_update_01.png?nolink }} | ||
+ | ==== 1、消息体系 ==== | ||
+ | 消息与会话属于多对一关系,云之讯为了区分不同会话类型的消息,将消息又分为<html><font color = red>SingleChat</font></html>(单聊消息)、<html><font color = red>DiscussionChat</font></html>(讨论组消息)、<html><font color = red>GroupChat</font></html>(群组消息)。开发者在发送消息时,应该可以根据会话类型创建不同的消息发送,也可以创建<html><font color = red>ChatMessage</font></html>通过<html><font color = red>setCategoryId</font></html>标识消息类型再发送,关系图如下: | ||
+ | {{ :messageAndCon_r_01.png?nolink&220 }} | ||
+ | ==== 2、发送消息 ==== | ||
+ | 在发送消息之前,开发者得新建一个消息对象,然后在调用云之讯<html><font color = red>sdk</font></html>的<html><font color = red>sendMessage</font></html>方法发送,以下是两种新建消息对象的方法:\\ | ||
+ | <html><span style="font-size:130%;">方法一:</span></html> | ||
+ | <code java> | ||
+ | /** | ||
+ | * 根据会话类型创建不同的消息 | ||
+ | */ | ||
+ | ChatMessage msg = null; | ||
+ | switch (conversationinfo.getCategoryId()) { | ||
+ | case PERSONAL: | ||
+ | /** | ||
+ | * 创建单聊消息 | ||
+ | */ | ||
+ | msg = new SingleChat(); | ||
+ | break; | ||
+ | case GROUP: | ||
+ | /** | ||
+ | * 创建群聊消息 | ||
+ | */ | ||
+ | msg = new GroupChat(); | ||
+ | break; | ||
+ | case DISCUSSION: | ||
+ | /** | ||
+ | * 创建讨论组消息 | ||
+ | */ | ||
+ | msg = new DiscussionChat(); | ||
+ | break; | ||
+ | } | ||
+ | </code> | ||
+ | <html><span style="font-size:130%;">方法二:</span></html> | ||
+ | <code java> | ||
+ | /** | ||
+ | * 根据会话类型创建不同的消息 | ||
+ | */ | ||
+ | ChatMessage msg = new ChatMessage(); | ||
+ | switch (conversationinfo.getCategoryId()) { | ||
+ | case PERSONAL: | ||
+ | /** | ||
+ | * 设置消息类型为单聊 | ||
+ | */ | ||
+ | msg.setCategoryId(CategoryId.PERSONAL); | ||
+ | break; | ||
+ | case GROUP: | ||
+ | /** | ||
+ | * 设置消息类型为群聊 | ||
+ | */ | ||
+ | msg.setCategoryId(CategoryId.GROUP); | ||
+ | break; | ||
+ | case DISCUSSION: | ||
+ | /** | ||
+ | * 设置消息类型为讨论组 | ||
+ | */ | ||
+ | msg.setCategoryId(CategoryId.DISCUSSION); | ||
+ | break; | ||
+ | } | ||
+ | </code> | ||
+ | 1) 发送文本消息 | ||
+ | <code java> | ||
+ | msg.setTargetId("接收方userId"); | ||
+ | msg.setNickName("发送方昵称"); | ||
+ | msg.setSenderId("发送方userId"); | ||
+ | msg.setMsgType(MSGTYPE.MSG_DATA_TEXT); | ||
+ | msg.setContent("消息内容"); | ||
+ | msg.setFromMyself(true); | ||
+ | IMManager.getInstance(mContext).sendmessage(msg) | ||
+ | </code> | ||
+ | 2) 发送语音消息 | ||
+ | <code java> | ||
+ | msg.setTargetId("接收方userId"); | ||
+ | msg.setNickName("发送方昵称"); | ||
+ | msg.setSenderId("发送方userId"); | ||
+ | msg.setMsgType(MSGTYPE.MSG_DATA_VOICE); | ||
+ | msg.setContent("语音时间(如30)"); | ||
+ | msg.setPath("语音文件路径"); | ||
+ | IMManager.getInstance(mContext).sendmessage(msg) | ||
+ | </code> | ||
+ | 3) 发送图片消息 | ||
+ | <code java> | ||
+ | msg.setTargetId("接收方userId"); | ||
+ | msg.setNickName("发送方昵称"); | ||
+ | msg.setSenderId("发送方userId"); | ||
+ | msg.setMsgType(MSGTYPE.MSG_DATA_IMAGE); | ||
+ | msg.setContent("要显示在页面的内容(如:图片)"); | ||
+ | msg.setPath("图片文件路径"); | ||
+ | IMManager.getInstance(mContext).sendmessage(msg) | ||
+ | </code> | ||
+ | 4) 发送地图消息 | ||
+ | <code java> | ||
+ | /** | ||
+ | * 构造地图对象。 | ||
+ | * | ||
+ | * @param latitude 地图经度。 | ||
+ | * @param longitude 地图纬度。 | ||
+ | * @param detailAddr 详细地址。 | ||
+ | * @param thumbnailPath 地图截图路径。 | ||
+ | */ | ||
+ | LocationMapMsg locationMsg = new LocationMapMsg(latitude, longitude, detailAddr , thumbnailPath); | ||
+ | msg.setTargetId("接收方userId"); | ||
+ | msg.setNickName("发送方昵称"); | ||
+ | msg.setSenderId("发送方userId"); | ||
+ | msg.setMsgType(MSGTYPE.MSG_DATA_LOCALMAP); | ||
+ | msg.setContent("要显示在页面的内容(如:地图)"); | ||
+ | msg.setLocationMapMsg(locationMsg); | ||
+ | IMManager.getInstance(mContext).sendmessage(msg) | ||
+ | </code> | ||
+ | 5) 发送自定义消息 | ||
+ | <code java> | ||
+ | /** | ||
+ | * 构造自定义对象。 | ||
+ | * | ||
+ | * @param data 要发送字节数组。 | ||
+ | * @param length 字节数组长度。 | ||
+ | */ | ||
+ | CustomMsg customMsg = new CustomMsg(data, length); | ||
+ | msg.setTargetId("接收方userId"); | ||
+ | msg.setNickName("发送方昵称"); | ||
+ | msg.setSenderId("发送方userId"); | ||
+ | msg.setMsgType(MSGTYPE.MSG_DATA_CUSTOMMSG); | ||
+ | msg.setContent("要显示在页面的内容(如:自定义消息)"); | ||
+ | msg.setCustomMsg(customMsg) | ||
+ | IMManager.getInstance(mContext).sendmessage(msg) | ||
+ | </code> | ||
+ | ===== 三、 设置监听 ===== | ||
+ | 云之讯为了方便开发者能随时监听<html><font color = red>sdk</font></html>的状态,提供了各式各样的监听回调,开发者需要根据自己的业务需求设置自己关心的回调事件,所有的设置监听事件都由<html><font color = red>IMManager</font></html>控制类完成,这里需要注意的是:所有的监听回调都运行在<html><font color = red>子线程</font></html>,开发者不应该在回调里面直接更新UI。 | ||
+ | ==== 1、设置发送回调 ==== | ||
+ | <code java> | ||
+ | IMManager.getInstance(context).setSendMsgListener(new MessageListener(){ | ||
+ | @Override | ||
+ | public void onSendMsgRespone(ChatMessage msg) { | ||
+ | /** | ||
+ | * 发送消息结果回调,开发者应该更新发送状态 | ||
+ | */ | ||
+ | if(msg.getSendStatus() == ChatMessage.MSG_STATUS_SUCCESS){ | ||
+ | Log.i("YZX","发送成功"); | ||
+ | }else{ | ||
+ | Log.i("YZX","发送失败"); | ||
+ | } | ||
+ | } | ||
+ | @Override | ||
+ | public void onReceiveMessage(List msgs) { | ||
+ | /** | ||
+ | * 接收到新消息回调,开发者应该将msgs添加到当前的消息集合,并刷新界面 | ||
+ | */ | ||
+ | } | ||
+ | @Override | ||
+ | public void onDownloadAttachedProgress(String msgId, String path, int totalSize,int currentProgress) { | ||
+ | /** | ||
+ | * 开发者调用了downloadAttached()接口下载后,sdk会回调该方法 | ||
+ | */ | ||
+ | } | ||
+ | }) | ||
+ | </code> | ||
+ | ==== 2、设置会话回调 ==== | ||
+ | 云之讯<html><font color = red>sdk</font></html>在发送消息和收到消息时会更新会话,开发者需要设置会话回调,监听会话的更新、创建、和删除事件。 | ||
+ | <code java> | ||
+ | IMManager.getInstance(context).setConversationListener(new IConversationListener() { | ||
+ | @Override | ||
+ | public void onUpdateConversation(List conversations) { | ||
+ | /** | ||
+ | * 需要更新的会话集合,会话有可能是新创建的,有可能是更新的。 | ||
+ | * 开发者需要根据targetId匹配上层应用会话集合。 | ||
+ | * 如果targetId相同,则是更新会话,需要将sdk回调的会话替换掉上层应用的会话。 | ||
+ | * 如果targetId不同,则是新创建的会话,添加到上层会话集合。 | ||
+ | */ | ||
+ | } | ||
+ | @Override | ||
+ | public void onUpdateConversation(ConversationInfo conversationInfo) { | ||
+ | /** | ||
+ | * nothing to do(兼容使用,开发者不用关心) | ||
+ | */ | ||
+ | } | ||
+ | @Override | ||
+ | public void onCreateConversation(ConversationInfo conversationInfo) { | ||
+ | /** | ||
+ | * nothing to do(兼容使用,开发者不用关心) | ||
+ | */ | ||
+ | } | ||
+ | @Override | ||
+ | public void onDeleteConversation(ConversationInfo conversationInfo) { | ||
+ | /** | ||
+ | * 当删除并退出讨论组的时候,sdk会删除会话,同时回调该事件 | ||
+ | */ | ||
+ | } | ||
+ | }); | ||
+ | </code> | ||
+ | ==== 3、设置sdk状态回调 ==== | ||
+ | 开发者如果想监听<html><font color = red>sdk</font></html>的状态可以设置该回调,当<html><font color = red>sdk</font></html>状态发生改变时会回调该方法。 | ||
+ | <code java> | ||
+ | IMManager.getInstance(context).setISdkStatusListener(new ISdkStatusListener() { | ||
+ | @Override | ||
+ | public void onSdkStatus(UcsReason reason) { | ||
+ | /** | ||
+ | * sdk状态改变时回调,开发者可以通过reason.getReason()区分各个事件,下面是几种常见事件监听 | ||
+ | */ | ||
+ | if(reason.getReason() == UcsErrorCode.NET_ERROR_KICKOUT) { | ||
+ | Log.i("YZX","服务器强制下线通知,账号在别处登录"); | ||
+ | } else if(reason.getReason() == UcsErrorCode.NET_ERROR_TCPCONNECTOK){ | ||
+ | Log.i("YZX","连接服务器成功"); | ||
+ | } else if(reason.getReason() == UcsErrorCode.NET_ERROR_TCPCONNECTFAIL){ | ||
+ | Log.i("YZX","连接服务器失败"); | ||
+ | } else if(reason.getReason() == UcsErrorCode.NET_ERROR_TCPCONNECTING){ | ||
+ | Log.i("YZX","正在连接服务器"); | ||
+ | } else if(reason.getReason() == UcsErrorCode.PUBLIC_ERROR_NETUNCONNECT){ | ||
+ | Log.i("YZX","网络断开"); | ||
+ | } else if(reason.getReason() == UcsErrorCode.PUBLIC_ERROR_NETCONNECTED){ | ||
+ | Log.i("YZX","网络连接上"); | ||
+ | } | ||
+ | } | ||
+ | }); | ||
+ | </code> | ||
+ | ===== 四、 工具说明 ===== | ||
+ | 云之讯提供图片压缩、录音和播放、下载接口,方便开发者使用,开发者可以选择性使用云之讯<html><font color = red>sdk</font></html>提供的工具。 | ||
+ | ==== 1、图片压缩 ==== | ||
+ | 云之讯<html><font color = red>sdk</font></html>默认发送的图片不能超过20M,为了降低用户的流量,开发者需要将图片压缩后发送,这里云之讯提供了压缩接口方便使用。在使用前,开发者需要将下载的压缩包中libs目录下<html><font color = red>libjpegbither.so</font></html>和<html><font color = red>libbitherjni.so</font></html>文件,拷贝到自己工程的libs/armeabi/目录。 | ||
+ | <code java> | ||
+ | /** | ||
+ | * 压缩图片并保存。 | ||
+ | * | ||
+ | * @param src 要压缩的图片源。 | ||
+ | * @param quality 压缩的图片质量(0-100),100表示不压缩。 | ||
+ | * @param savePath 压缩后图片保存路径。 | ||
+ | */ | ||
+ | IMManager.compressBitmap(src,quality,savePath) | ||
+ | </code> | ||
+ | |||
+ | ==== 2、录音和播放 ==== | ||
+ | 开发者可以通过云之讯<html><font color = red>sdk</font></html>完成音频的录制和播放,设置<html><font color = red>RecordListener</font></html>监听录音完成和和播放录音完成,这里需要注意的是:<html><font color = red>RecordListener</font></html>回调运行在<html><font color = red>子线程</font></html>,开发者不应该在回调里面直接更新UI。 | ||
+ | <code java> | ||
+ | /** | ||
+ | * 开始录音。 | ||
+ | * | ||
+ | * @param savePath 录音文件保存路径,不能为空。 | ||
+ | * @param recordListener 录音回调事件,不能为空。 | ||
+ | */ | ||
+ | IMManager.getInstance(context).startVoiceRecord(savePath,new RecordListener() { | ||
+ | @Override | ||
+ | public void onFinishedRecordingVoice(int time) { | ||
+ | /** | ||
+ | * 调用stopVoiceRecord()方法后停止录音,sdk回调该事件返回录音时长 | ||
+ | */ | ||
+ | Log.i("YZX","录音时长:"+time+"秒...") | ||
+ | } | ||
+ | public void onFinishedPlayingVoice() { | ||
+ | /** | ||
+ | * nothing to do(录音不需要关注该方法) | ||
+ | */ | ||
+ | } | ||
+ | }) | ||
+ | /** | ||
+ | * 停止录音。 | ||
+ | * | ||
+ | * 停止录音后,sdk会回调RecordListener的onFinishedRecordingVoice方法。 | ||
+ | * 一般录音时长不超过60秒,开发者应该在60秒前调用stopVoiceRecord()停止录音。 | ||
+ | */ | ||
+ | IMManager.getInstance(context).stopVoiceRecord() | ||
+ | /** | ||
+ | * 开始播放录音。 | ||
+ | * | ||
+ | * @param srcPath 录音文件路径,不能为空。 | ||
+ | * @param recordListener 录音回调事件,不能为空。 | ||
+ | */ | ||
+ | IMManager.getInstance(context).startPlayerVoice(srcPath,new RecordListener() { | ||
+ | @Override | ||
+ | public void onFinishedRecordingVoice(int time) { | ||
+ | /** | ||
+ | * nothing to do(播放录音不需要关注该方法) | ||
+ | */ | ||
+ | } | ||
+ | public void onFinishedPlayingVoice() { | ||
+ | /** | ||
+ | * 播放录音完成之后回调该方法。 | ||
+ | */ | ||
+ | Log.i("YZX","play voice onFinished...") | ||
+ | } | ||
+ | }) | ||
+ | /** | ||
+ | * 停止播放录音。 | ||
+ | * | ||
+ | */ | ||
+ | IMManager.getInstance(context).stopPlayerVoice() | ||
+ | </code> | ||
+ | ==== 3、下载 ==== | ||
+ | 为了方便开发者下载图片消息的原图,云之讯<html><font color = red>sdk</font></html>提供下载接口方便调用,可以通过<html><font color = red>MessageListener</font></html>监听下载进度和完成。 | ||
+ | <code java> | ||
+ | /** | ||
+ | * 下载指定消息的文件,如某图片消息的原图。 | ||
+ | * | ||
+ | * @param url 文件url。 | ||
+ | * @param savePath 文件要保存的路径。 | ||
+ | * @param msgId 消息id。 | ||
+ | * @param messageListener 下载消息监听。 | ||
+ | */ | ||
+ | IMManager.downloadAttached(url,savePath,msgId,new MessageListener(){ | ||
+ | @Override | ||
+ | public void onSendMsgRespone(ChatMessage msg) { | ||
+ | /** | ||
+ | * nothing to do(发送消息回调该事件) | ||
+ | */ | ||
+ | } | ||
+ | @Override | ||
+ | public void onReceiveMessage(List msgs) { | ||
+ | /** | ||
+ | * nothing to do(收到新消息回调该事件) | ||
+ | */ | ||
+ | } | ||
+ | @Override | ||
+ | public void onDownloadAttachedProgress(String msgId, String path, int totalSize,int currentProgress) { | ||
+ | /** | ||
+ | * 下载进度更新,当currentProgress >= totalSize,下载完毕 | ||
+ | * 如果currentProgress == 0或者totalSize == 0下载失败 | ||
+ | */ | ||
+ | Log.i("YZX","文件总大小:"+totalSize+",当前下载进度:"+currentProgress); | ||
+ | } | ||
+ | }) | ||
+ | </code> | ||
+ | ===== 五、 断线重连 ===== | ||
+ | 开发者成功登录之后,不需要关心与服务器连接和断开,<html><font color = red>sdk</font></html>会自动重连,以下是<html><font color = red>sdk</font></html>断线重连机制: | ||
+ | | 重连场景 ^ 描述 ^ | ||
+ | ^ 传输数据时,连接不可用 | 与服务器交互时,如果socket抛异常将会重连 | | ||
+ | ^ 服务器或者路由断开连接 | 服务器或者路由器判断当前连接无效,断开连接,sdk将会重连 | | ||
+ | ^ 心跳回执超时 | sdk发送心跳之后,30秒没有收到服务器回执,sdk将会重连 | | ||
+ | ^ 网络切换 | 断开网络之后,sdk将主动断开连接;网络链接成功之后,sdk将会重连。网络类型变化(如:3G切换到4G,移动网络切换到wifi网络等...),sdk也会重连。 | | ||
+ | ^ 连接失败 | 连接服务器失败之后,sdk默认会重连5次,重连间隔将会递增 | |