让你的 Android 应用拥有微信一样的实时沟通体验

killme2008 2014-11-19 02:43:12
LeanMessage 移动开发 SDK 是由 LeanCloud 提供的,专为 iOS、Android 和 WindowsPhone 等客户端程序提供应用内聊天的 API 和服务,并且也提供了 JavaScript API,方便开发者打通网页和客户端应用,给最终用户提供统一的使用体验。使用 LeanMessage API,您可以极快地以最少工作量让您的移动应用支持实时聊天,得到一种如微信一般的沟通体验。

开始之前 出于本文的目的,我假设您已经非常熟悉使用 JSON、Android 和 Eclipse 进行移动应用编程的基本概念。在您继续阅读本文之前,请访问 leancloud.cn 并创建您的应用程序。只需遵循注册页面中的简单指令即可。
本文介绍了包含单聊、群聊、历史记录和应用鉴权的核心 API 类。您将学习如何简单进行用户间一对一单聊,以及如何创建群组让多用户进行群聊,还有如何通过签名来对聊天通道进行控制,以保护应用和用户的隐私。示例均构建于 LeanMessage SDK for Android 之上(请参阅Android开发指南)。

基本概念和类

聊天参与者 Peer

在 LeanMessage 实时消息世界中,每一个参与者(一般而言是「人」)都是一个 Peer。Peer 拥有一个在应用内唯一标识自己的 ID(称为 PeerID,字符串类型,长度不超过 50 字节,具体数值由应用自身确定),系统中的每一条消息都来自于一个 Peer,发送到一个或多个 Peer。并且,LeanMessage 的消息服务允许一个 Peer 在多个不同设备上登录,也允许一个设备上同时登录多个 Peer,究竟该如何使用,由应用根据使用场景自己选择。

这里要注意的是,PeerID 是由应用开发者自己賦值的,LeanMessage 本身并没有任何强制要求,所以:

*实时消息系统是可以和用户账户系统解耦合的,应用开发者不需要把除了 PeerID 以外的任何信息告诉 LeanMessage;
*LeanMessage 在消息转发的时候是按照 PeerID 来唯一定位的,因此如果应用自身支持同一账户的多点登录,那么 LeanMessage 就会把消息通知到所有终端;
*匿名聊天/非匿名聊天这都是应用层自己决定的,如果应用自身能为匿名用户指定一个唯一的 ID,那么这个用户参与到聊天系统里来,是完全没有问题的。

为了防止骚扰,一个 Peer 需要先关注(watch)了对方才能给对方发送消息;因为 LeanMessage 提供了更细粒度的权限控制,应用开发者可以在关注(watch)动作上增加签名来保证安全性。这一点后面会进行详细说明。

实时消息 AVMessage
在 LeanMessage 中所有的消息都是 AVMessage 的实例,AVMessage 只支持文本,并且长度不能超过 5KB。消息分为暂态(transient)和持久消息两种类型。所有持久消息都会在 LeanMessage 云端保存,所以用户离线之后也可以得到通知和接收,而暂态消息并不会离线保存,适合开发者用来进行协议控制。

AVMessage 的定义如清单 1 所示:
public class AVMessage implements Parcelable {
private List<String> toPeerIds; // 消息接收方的 PeerID,支持一个或多个

String groupId; // 消息所属群组的ID,对于普通一对一聊天消息而言,此值为空
String message; // 消息体
long timestamp; // 消息发送时间戳
boolean isTransient; // 是否是暂态消息
String fromPeerId; // 消息发送方的 PeerID

public AVMessage();
public AVMessage(String message);
public AVMessage(String message, List<String> toPeerIds, boolean isTransient);
public AVMessage(String message, boolean isTransient);
}

LeanMessage 为所有历史消息都提供了存储和查询的功能,存储时间则根据开发者的类型有所不同。

聊天会话 Session
每一个 Peer 通过开启(open)一个会话(Session)而加入实时消息服务,Peer 可以在一个会话中关注(watch)一个或多个 Peer,当被关注者上下线时,会收到通知。Peer 在开启会话后只能向自己关注的其他 Peers 发送消息,但可以收到任何 Peer 发来的消息,也就是说单向关注时,消息可以顺利地由关注者发往被关注者。

Session 有如下几种状态:

*opened。 Session 被正常打开,此时可以进行正常的通信;
*pause。 网络异常(譬如 wifi 断开,3G/2G 切换,iOS 应用进入后台,等),Session 进入暂停状态,当网络恢复时,Session 会自动重连;
*resume。 应用转入前台,会话重新建立起来(此状态只在 iOS 设备上有效)
*closed。 Session 结束,仅在显示调用了 Session.close 方法时才会发生。用户注销实时通信服务,不再能够接收到消息或推送通知;

Session 上可以进行的操作有:

*open 以一个 Peer ID 打开 Session
*watch 关注一组 Peer ID,关注后可以收到这个 Peer 的上下线通知,发送消息
*unwatch 取消对一组 Peer ID 的关注
*sendMessage 给一组 Peer ID 发送消息
*queryOnlinePeer 查找当前在线的 Peers
*getHistoryMessageQuery 查找历史消息
*setSignatureFactory 设置签名类(为了保证安全性,后面会讲述)
*close 注销服务,关闭 Session

一对一的文本聊天
明白了这三个概念之后,我们就可以开始进入实际聊天环节了。

首先我们需要在 application 的 onCreate 函数中进行 LeanCloud 最基本的初始化:
@Override
public void onCreate() {
super.onCreate();
AVOSCloud.initialize(this, "pleaseReplaceWithYourAppId", "pleaseReplaceWithYourAppKey");
}

接下来我们来看一下怎么样进行一对一的基本聊天。首先,我们需要开启一个会话(Session),示例代码如清单 2 所示:
SessionManager session = SessionManager.getInstance(selfId);//获取SessionManager实例,以便于后续的操作。这里的 selfId 可以是用户的实际 id,也可以是其他唯一的字符串,譬如「Tom」。
List<String> watchedIds = new LinkedList<String>();
session.open(watchedIds); //打开Session,同时关注一些 PeerID。此时没有关注对象

注意! 一般而言,会话的开启是在用户登录之后的 RootActivity 中进行的。对于支持匿名聊天的应用,也可以在 Application 启动的时候进行。千万不要在一个临时或短命的 Activity 中开启聊天会话。上面代码中 SessionManager 也是 Session 的子类,所以可以直接调用 Session 的方法。

接下来,我们开始跟「Bob」这个用户进行聊天。为了给他发送消息,我们先要关注(watch)他,代码如下:
List<String> peerIds = new LinkedList<String>();
peerIds.add("Bob");
session.watchPeers(peerIds);

之后我们给「Bob」发送一条消息:
List<String> peerIds = new LinkedList<String>();
peerIds.add("Bob");
session.sendMessage(new AVMessage("嗨,你好,我是 Tom", peers, false));

好了,这样一条消息就发送过去了。但是问题来了,对于「Bob」而言,他怎么才能收到别人发给他的消息呢?

上面对于 Session 的所有操作都是异步的。与一般 Android 异步方法调用不同,LeanMessage SDK 的异步并不是通过 Callback 或者类似 RsyncTask 的机制实现的,而是通过继承 AVMessageReceiver 这一 BoardcastReceiver,实现以下方法来处理来自服务器端的响应的。AVMessageReceiver 接口定义如清单 3 所示:
/**
* 当服务器成功与客户端打开session时产生本次回调
*/
public abstract void onSessionOpen(Context context, Session session);

/**
* 在 session 暂停时调用,一般都是由网络连接丢失导致的隐性调用
*/
public abstract void onSessionPaused(Context context, Session session);

/**
* Session 恢复时,一般都是网络连接恢复以后的
* 这个时候你可以处理一些由于网络异常导致的失败消息
*/
public abstract void onSessionResumed(Context context, Session session);

/**
* 从某个Peer接收到消息时,会收到一次调用
*/
public abstract void onMessage(Context context, Session session,
AVMessage msg);

/**
* 服务器反馈消息已经成功发送时,会收到调用
*/
public abstract void onMessageSent(Context context, Session session,
AVMessage msg);

/**
* 在消息发送失败时,产生的调用 在这里你可以保存一下发送失败的消息以便未来重发
*/
public abstract void onMessageFailure(Context context, Session session,
AVMessage msg);

/**
* 当关注的一些peers上线时,产生的调用
*/
public abstract void onStatusOnline(Context context, Session session,
List<String> peerIds);

/**
* 当关注的一些peers下线时,产生的调用
*/
public abstract void onStatusOffline(Context context, Session session,
List<String> peerIds);

/**
* 当与服务器发生交互的过程中的任何错误,都在这里被返回
*/
public abstract void onError(Context context, Session session, Throwable e);

从上面接口的定义中,我们可以看到,要接收到别人发过来的消息,只需要响应 onMessage() 方法即可。代码示例如清单 4 所示:
public class CustomeMsgReceiver extends AVMessageReceiver {
@Override
public void onMessage(final Context context, Session session, AVMessage avMsg) {
Logger.d("onMessage "+avMsg.getMessage());
// 进行上层逻辑处理,譬如 UI 展示,或者消息提醒。
}
}

// 在 AndroidManifest.xml 文件中声明这一 BoardcastReceiver。
<receiver android:name=".receiver.CustomeMsgReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="com.avoscloud.session.action" />
</intent-filter>
</receiver>

支持富媒体的聊天消息
上面的代码演示了如何发送文本信息,但是现在的交互方式已经越来越多样化,图片、语音、视频已是非常普遍的媒体类型。而从 AVMessage 的定义来看,只支持不超过 5KB 大小的文本,那么 LeanMessage 又如何能支持富媒体的聊天消息呢?

记得 LeanStorage 中的 AVFile 吗? AVFile 是 LeanStorage 提供的非结构化数据存储解决方案,可以让你的应用程序将二进制文件存储到云端服务器中,并且自动提供 CDN 加速服务,能带给用户更迅捷的下载体验。比如常见的文件类型图像文件、影像文件、音乐文件和任何其他二进制数据都可以使用。具体说明可以参见Android 开发文档

对于图片、语音、视频这类较大的非结构化数据,存储到云端文件系统之后,在消息中发送 url 已是业界惯例,并且 LeanMessage 中 AVMessage 类的定义并没有规定消息体是什么类型,所以我们可以充分利用这一扩展空间,结合 AVFile 来发送、接收富媒体的聊天消息。实现方法如清单 5 所示:

AVFile file = AVFile.withAbsoluteLocalPath("test.jpg", Environment.getExternalStorageDirectory() + "/test.jpg");
file.saveInBackground(new SaveCallback() {
// override
public void done(AVException ex) {
if (null != ex) {
// error
} else {
// construct message body under json format.
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("type", "image");
params.put("context", "嗨,你好,我是 Tom");
params.put("attachment", file.getUrl());

List<String> peerIds = new LinkedList<String>();
peerIds.add("Bob");
session.sendMessage(new AVMessage(JSON.toJSONString(params), peers, false));
}
}
});

新版本的 LeanMessage SDK 会支持富媒体消息,避免让每个开发者都重复做类似的工作。

群组聊天
在聊天的需求里,还有一个很重要的场景,就是群组聊天。从前面 AVMessage 的定义我们可以猜到,LeanMessage 应该是支持群聊的,那实际上该如何实现呢?下面我们一步一步来尝试一下。

基本概念
与普通的单聊相比,群聊增加了如下两个基本概念:

*群组 AVGroup

AVGroup 代表一个聊天群组,可以对应到实际的多人聊天、聊天群、聊天室等,每个 AVGroup 有一个唯一的 ID(groupID,由 LeanMessage 云端分配),其定义如清单 6 所示:

  public class AVGroup implements Group {
String roomId;
String selfId;
Session session;
}

一个 Peer 加入群后向群发送的消息可以被所有群成员收到。当有新成员加入或者既有成员退出时,所有群成员都会得到通知。AVGroup 上可以进行的操作有:

public interface Group{
public void join();
public void sendMessage(AVMessage msg);
public void kickMember(List<String> peerIds);
public void inviteMember(List<String> peerIds);
public void quit();
public String getGroupId();
public String getSelfId();
public AVHistoryMessageQuery getHistoryMessageQuery();
}

*群组消息接受器 AVGroupMessageReceiver

与 AVMessageReceiver 类似,AVGroupMessageReceiver 主要用来处理群组操作的结果。其详细定义如清单 7 所示:

// 在加入聊天室成功后被调用 如果join时,没有带groupId,您可以在返回的group中间获取groupId
public abstract void onJoined(Context context, Group group);

//当你被别人邀请进入某个聊天室以后
// @param group
// @param byPeerId 这个人邀请了你
public abstract void onInvited(Context context, Group group, String byPeerId);

// 当你被别人踢出聊天室以后
// @param group
// @param byPeerId 是他踢了你
public abstract void onKicked(Context context, Group group, String byPeerId);

// 处理消息发送成功事件
public abstract void onMessageSent(Context context, Group group, AVMessage message);

// 用来处理消息发送失败事件.可以缓存起来,事后重发
public abstract void onMessageFailure(Context context, Group group, AVMessage message);

// 收到消息以后被调用,一般通过这个接口来处理和接受来自Group的消息
// @param context
// @param group
// @param message
// @param fromPeerId 发消息者
public abstract void onMessage(Context context, Group group, AVMessage message);

// 处理退出成功事件
public abstract void onQuit(Context context, Group group);

// 处理Group操作被拒绝的时间
// @param context
// @param group
// @param op 这里可能存在的操作有 "join","invite","kick"
// @param targetIds
// 一般来说是指被操作的对象,在join操作中间就是指groupId本身,
// invite和kick中则指被邀请或者被踢除的peerIds
public abstract void onReject(Context context, Group group, String op, List<String> targetIds);

// 处理新用户加入事件
public abstract void onMemberJoin(Context context, Group group, List<String> joinedPeerIds);

// 处理用户退出事件
public abstract void onMemberLeft(Context context, Group group, List<String> leftPeerIds);

// 处理所有Group相关的异常
public abstract void onError(Context context, Group group, Throwable e);


阅读原文
...全文
167 点赞 收藏 3
写回复
3 条回复
LeanCloud 通讯 2014年11月21日
谢谢楼主!!好东西~ 收藏~
回复 点赞
浮生若梦_平淡为真 2014年11月19日
不错的软文,
回复 点赞
开发者_android 2014年11月19日
你这是在提问中发blog啊,人才.
回复 点赞
发动态
发帖子
Android
创建于2009-10-09

4.6w+

社区成员

9.0w+

社区内容

移动平台 Android
社区公告
暂无公告