应用也能聊天

简介

自二零一八年 LeanCloud 公布实时通讯(IM)服务之后,基于客户反馈和技术员对急需的消化摄取和对作业的提炼,上周专门的学问发表了「实时通信2.0 」。设计观念长久以来是「灵活、解耦、可结合、可定制」,具体能够参见《实时通讯支出指南》,驾驭LeanCloud 实时通讯的基本概念和模型。

LeanMessage 移动支付 SDK 是由 LeanCloud 提供的,专为 iOS、Android 和 WindowsPhone® 等客户端程序提供利用内聊天的 API 和劳动,并且也提供了 JavaScript API,方便开拓者打通网页和客商端应用,给最后客商提供联合的行使体验。使用 LeanMessage API,您能够非常快地以起码工作量让您的移位使用支撑实时聊天,得到一种如微信平日的牵连体验。

下载和设置

可以到 LeanCloud 官方下载点下载 LeanCloud IM SDK v2 版本。将下载到的 jar 包加入工程就可以。

千帆竞发在此以前
鉴于本文的指标,小编假诺您已经不行熟识使用 JSON、Android 和 Eclipse 进行运动应用编制程序的基本概念。在您继续阅读本文此前,请访谈 leancloud.cn 并创立您的应用程序。只需服从注册页面中的轻便指令就可以。

一定的文书聊天

咱俩先从最简易的环节入手,看看怎么用 LeanCloud IM SDK v2 兑现一对一文本推搡。

本文介绍了含蓄单聊、群聊、历史记录和选择鉴权的为主 API 类。您将学习怎么着简单进行顾客间一对一单聊,以及如何创建群组让多客商举行群聊,还会有哪些通过签订左券来对聊天通道进行调控,以爱抚选择和客户的隐衷。示例均营造于 LeanMessage SDK for Android 之上(请参阅Android开垦指南)。

初始化

和 LeanCloud 其余服务均等,实时聊天服务的伊始化也是在 Application 的 onCreate 方法中张开的:

public class MyApplication extends Application{

    public void onCreate(){
      ...
      AVOSCloud.initialize(this,"{{appId}}","{{appKey}}");
      ...
    }
}

并且在AndroidManifest.xml中间证明:

<manifest>
   ...

   <application
        android:name=".MyApplication"
        ....>
        ...

        <service android:name="com.avos.avoscloud.PushService" />

        <receiver android:name="com.avos.avoscloud.AVBroadcastReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>
        ...
   </application>

</manifest>

接下去大家须求变成客户登入。

基本概念和类

登录

假定聊天发起方名为 汤姆,为直观起见,大家使用客户名来作为 clientId 登入聊天系统(LeanCloud 云端只需要 clientId 在行使内独一就可以,具体用哪些数据由应用层决定),代码如下:

AVIMClient imClient = AVIMClient.getInstance("Tom");
imClient.open(new IMClientCallback(){
  @Override
  public void done(AVIMClient client, AVException e) {
    if (null != e) {
      // 出错了,可能是网络问题无法连接 LeanCloud 云端,请检查网络之后重试。
      // 此时聊天服务不可用。
      e.printStackTrace();
    } else {
      // 成功登录,可以开始进行聊天了(假设为 MainActivity)。
      Intent intent = new Intent(currentActivity, MainActivity.class);
      currentActivity.startActivity(intent);
    };
  }
});

闲谈参与者 Peer

在 LeanMessage 实时音信世界中,每贰个参预者(日常来说是「人」)都是三个Peer。Peer 具有三个在应用内独一标记本人的 ID(称为 PeerID,字符串类型,长度不抢先 50 字节,具体数值由运用本人明确),系统中的每一条新闻都来自于二个Peer,发送到多少个或多少个 Peer。何况,LeanMessage 的新闻服务允许贰个 Peer 在多少个例外器械上登陆,也同意一个设施上同有的时候候登陆多少个Peer,毕竟该怎么利用,由运用依据使用情状温馨挑选。

此地要小心的是,PeerID 是由使用开辟者自个儿賦值的,LeanMessage 本人并从未任何强制须要,所以:

  • 实时音信系统是足以和顾客账户类别解耦合的,应用开垦者无需把除了 PeerID 以外的别样音信告诉 LeanMessage;
  • LeanMessage 在消息转载的时候是遵照 PeerID 来独一定位的,因而只要利用本人支持同一账户的多点登陆,那么 LeanMessage 就能把新闻文告到持有终端;
  • 无名聊天/非佚名聊天那都以应用层自身主宰的,假使采纳自己能为佚名顾客钦命一个独一的 ID,那么那么些顾客参预到聊天系统里来,是完全未有毛病的。

为了避防万一侵扰,一个 Peer 供给先关切(watch)了对方才具给对方发送音信;因为 LeanMessage 提供了更加细粒度的权能决定,应用开拓者能够在关怀(watch)动作上扩大具名来担保卫安全全性。这点后边会举行详细表明。

确立对话

假若大家要跟「鲍勃」那么些客户展开聊天,我们先创设二个对话,代码如下:

List<String> clientIds = new ArrayList<String>();
clientIds.add("Tom");
clientIds.add("Bob");

// 我们给对话增加一个自定义属性 type,表示单聊还是群聊
// 常量定义:
// int ConversationType_OneOne = 0; // 两个人之间的单聊
// int ConversationType_Group = 1;  // 多人之间的群聊
Map<String, Object> attr = new HashMap<String, Object>();
attr.put("type", ConversationType_OneOne);

imClient.createConversation(clientIds, attr, new AVIMConversationCreatedCallback() {
  @Override
  public void done(AVIMConversation conversation, AVException e) {
    if (null != conversation) {
      // 成功了,这时候可以显示对话的 Activity 页面(假定为 ChatActivity)了。
      Intent intent = new Intent(this, ChatActivity.class);
      Intent.putExtra(“conversation”, conversation);
      startActivity(intent);
    }
  }
});

树立的「对话」在调整台怎么查看

如你所见,大家创制一个对话的时候,钦定了成员(汤姆 和 鲍伯)和二个相当的习性({type: 0})。那一个数量保存到云端后,你在 控制台 -> 存储 -> 数据 里面拜望到,_Conversation 表中加进了一条记下,新记录的 m 属性值为["Tom", "Bob"]attr 属性值为{"type":0}。如你所料,m 属性正是对应着成员列表,attr 属性正是客户扩展的额外属性值(以目的的款型储存)。

实时音讯 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 为持有历史信息都提供了蕴藏和询问的机能,存款和储蓄时间则依照开垦者的种类有所分化。

出殡音信

成立好对话之后,要发送新闻是很简单的:

AVIMMessage message = new AVIMMessage();
message.setContent("hello");
conversation.sendMessage(message, new AVIMConversationCallback() {
  @Override
  public void done(AVException e) {
    if (null != e) {
      // 出错了。。。
      e.printStackTrace();
    } else {
      Logger.d("发送成功,msgId=" + message.getMessageId());
    }
  }
});

好了,那样一条消息就发送过去了。但是难点来了,对于「鲍勃」来讲,他怎么技术接过旁人发给她的音信呢?

聊天会话 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

消息接收

在 鲍勃 这一端,要能接收到新闻,必要如下几步:

1,进行先河化;

2,完成和谐的 AVIMMessageHandler,响应新新闻达到通告,重尽管之类函数:

public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client);

对于 汤姆 发过来的音讯,要展示出来,我们只需兑现 onMessage 就能够,示例代码如下:

class CustomMessageHandler extends AVIMMessageHandler {
  @Override
  public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
    // 新消息到来了。在这里增加你自己的处理代码。
    String msgContent = message.getContent();
    Logger.d(conversation.getConversationid() + " 收到一条新消息:" + msgContent);
  }
}

3,进行登陆,代码也与发送端一样。

完整代码如下:

// 自定义消息响应类
class CustomMessageHandler extends AVIMMessageHandler {
  @Override
  public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
    // 新消息到来了。在这里增加你自己的处理代码。
    String msgContent = message.getContent();
    Logger.d(conversation.getConversationid() + " 收到一条新消息:" + msgContent);
  }
}

// application 的初始化部分
public void onCreate(){
  ...
  AVOSCloud.initialize(this,"{{appId}}","{{appKey}}");
  AVIMMessageManager.registerDefaultMessageHandler(new CustomMessageHandler());
  ...
}

// 用户登录部分
AVIMClient imClient = AVIMClient.getInstance("Bob");
imClient.open(new IMClientCallback(){
  @Override
  public void done(AVIMClient client, AVException e) {
    if (null != e) {
      // 出错了,可能是网络问题无法连接 LeanCloud 云端,请检查网络之后重试。
      // 此时聊天服务不可用。
      e.printStackTrace();
    } else {
      // 成功登录,可以开始进行聊天了。
    };
  }
});

注意!
AVIMMessageManager.registerDefaultMessageHandler() 绝对要在 AVIMClient.open() 以前调用,不然大概导致服务器发回去的局地新闻错过。

一定的文书聊天

接头了那多个概念之后,我们就足以初叶走入实际聊天环节了。

首先大家供给在 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 的章程。

接下去,大家伊始跟「鲍伯」这些客户进行聊天。为了给他发送音讯,大家先要关切(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 也许类似 EvoquesyncTask 的机制落到实处的,而是通过一连 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>

多少个主要的回调接口

从地点的例子中得以看来,要接纳到人家给你发送的音讯,须要重载 AVIMMessageHandler 类。从 v2 版起首,LeanCloud IM SDK 多量应用回调来报告操作结果,不过对于一些消沉的新闻布告,则照旧接纳接口来完毕的,包含:

  • 时下网络出现变化
  • 对话中有新的音信
  • 对话中有新成员走入
  • 对话中有成员离开
  • 被约请参预某对话
  • 被踢出对话

LeanCloud IM SDK 内部使用了两种接口来响应这几个事件。

支撑富媒体的谈天音信

上边的代码演示了怎么样发送文书消息,可是未来的交互形式已经尤其两种化,图片、语音、录制已然是特别布满的传媒类型。而从 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 会协助富媒体音讯,制止让各样开辟者都再度做类似的行事。

网络事件响款待口

要害用于管理互联网生成事件,接口定义在 AVIMClientEventHandler,首要函数为:

  /**
   * 实现本方法以处理网络断开事件
   */
  public abstract void onConnectionPaused(AVIMClient client);

  /**
   * 实现本方法以处理网络恢复事件
   */
  public abstract void onConnectionResume(AVIMClient client);

在互联网中断的动静下,全数的音信收发和对话操作都会产出难点。

通过 AVIMClient.setClientEventHandler(AVIMClientEventHandler handler) 能够设定全局的 ClientEventHandler。

群组聊天

在拉拉扯扯的必要里,还会有贰个十分重大的光景,就是群组聊天。从日前 AVMessage 的定义我们能够猜到,LeanMessage 应该是帮助群聊的,那实在该怎么样实现啊?上面我们一步一步来品尝一下。

对话成员变动响招待口

重要用以管理对话中成员变动的事件,接口定义在 AVIMConversationEventHandler,主要函数为:

  /**
   * 实现本方法以处理聊天对话中的参与者离开事件
   *
   * @param members 离开的参与者
   * @param kickedBy 踢人者,自愿退出的情况下踢人者就是参与者
   */
  public abstract void onMemberLeft(AVIMClient client,
      AVIMConversation conversation, List<String> members, String kickedBy);

  /**
   * 实现本方法以处理聊天对话中的参与者加入事件
   *
   * @param members 加入的参与者
   * @param invitedBy 邀请人,有可能是加入的参与者本身
   */
  public abstract void onMemberJoined(AVIMClient client,
      AVIMConversation conversation, List<String> members, String invitedBy);

  /**
   * 实现本方法来处理当前用户被踢出某个聊天对话事件
   *
   * @param kickedBy 踢出你的人
   */
  public abstract void onKicked(AVIMClient client, AVIMConversation conversation,
      String kickedBy);

  /**
   * 实现本方法来处理当前用户被邀请到某个聊天对话事件
   *
   * @param conversation 被邀请的聊天对话
   * @param operator 邀请你的人
   */
  public abstract void onInvited(AVIMClient client, AVIMConversation conversation,
      String operator);

通过 AVIMMessageManager.setConversationEventHandler(AVIMConversationEventHandler handler) 可以安装全局的 ConversationEventHandler。

基本概念

与日常的单聊相比较,群聊扩展了如下多少个基本概念:

  • 群组 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
   */
  @Override
  public abstract void onJoined(Context context, Group group);

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

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

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

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

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

  /**
   * 处理退出成功事件
   */
  @Override
  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
   */
  @Override
  public abstract void onReject(Context context, Group group, String op,
        List<String> targetIds);

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

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

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

新闻响款待口

最主要用以管理新新闻到达事件,接口定义在 MessageHandlerAVIMMessageHandler 是二个空的兑现类,大家应该经过重载 AVIMMessageHandler 的相干办法来成功音信管理。首要的点子有:

  // 收到新的消息
  @Override
  public void onMessage(AVIMMessage message, AVIMConversation conversation);

  // 自己发送的消息已经被对方接收
  @Override
  public void onMessageReceipt(AVIMMessage message, AVIMConversation conversation, AVIMClient client);

通过 AVIMMessageManager.registerDefaultMessageHandler(handler) 能够设置全局的 MessageHandler。

大家达成这三类接口,就足以管理全体的通报新闻了。示例代码如下:

class CustomNetworkHandler extends AVIMClientEventHandler {
  @Override
  public void onConnectionPaused(AVIMClient client) {
    // 请按自己需求改写
    Logger.d("connect paused");
  }

  @Override
  public void onConnectionResume(AVIMClient client) {
    // 请按自己需求改写
    Logger.d("connect resume");
  }
}

class CustomConversationHandler extends AVIMConversationEventHandler {
  public private Context gContext = null;
  private void toast(String str) {
    Toast.makeText(gContext, str, Toast.LENGTH_SHORT).show();
  }
  private void toast(Context context, String str) {
    Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
  }

  @Override
  public void onMemberLeft(AVIMClient client, AVIMConversation conversation, List<String> members, String kickedBy) {
    // 请按自己需求改写
    toast(MsgUtils.nameByUserIds(members) + " left, kicked by " + MsgUtils.nameByUserId(kickedBy));
    //注:MsgUtils 是一个辅助类,nameByUserIds 用来将 userId 转换成用户名
  }

  @Override
  public void onMemberJoined(AVIMClient client, AVIMConversation conversation, List<String> members, String invitedBy) {
    // 请按自己需求改写
    toast(MsgUtils.nameByUserIds(members) + " joined , invited by " + MsgUtils.nameByUserId(invitedBy));
    //注:MsgUtils 是一个辅助类,nameByUserIds 用来将 userId 转换成用户名
  }

  @Override
  public void onKicked(AVIMClient client, AVIMConversation conversation, String kickedBy) {
    // 请按自己需求改写
    toast("you are kicked by " + MsgUtils.nameByUserId(kickedBy));
  }

  @Override
  public void onInvited(AVIMClient client, AVIMConversation conversation, String operator) {
    // 请按自己需求改写
    toast("you are invited by " + MsgUtils.nameByUserId(operator));
  }
};

class CustomMsgHandler extends AVIMMessageHandler {
  @Override
  public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
    // 请按自己需求改写
    String msgContent = message.getContent();
    Logger.d(conversation.getConversationid() + " 收到一条新消息:" + msgContent);
  }

  @Override
  public void onMessageReceipt(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
    // 请按自己需求改写
    Logger.d("发往对话 " + conversation.getConversationid() + " 的消息 "+ message.getMessageId() +" 已被接收");
  }
}

// 设置事件响应接口
AVIMClient.setClientEventHandler(new CustomNetworkHandler());
AVIMMessageManager.setConversationEventHandler(new CustomConversationHandler());
AVIMMessageManager.registerDefaultMessageHandler(new CustomMsgHandler());

参预聊天室

由于一切实时通讯成效都以制造在 Session 的基本功上,所以大家要步入八个聊天室也急需创立在八个早已展开的 Session 上。 已经开采多少个 Session 未来,能够经过以下操作来参预三个 Group:

    Group group = SessionManager.getInstance(selfId).getGroup();//准备新建一个聊天室
    //Group group = SessionManager.getInstance(selfId).getGroup(groupId); 加入一个已经存在的聊天室
    group.join(); // LeanMessage 云端会判断 groupId 是否存在,如果不存在就新建一个 Group,否则加入已有 Group

加盟成功之后 AVGroupMessageReceiver 子类中的 onJoined 方法就能够被调用。

支持富媒体的闲谈消息

地点的代码演示了如何发送简单文本消息,可是未来的交互格局已经尤其各个化,图像、语音、摄像已然是非平时见的新闻类型。v2 版的 LeanCloud IM SDK 已经足以很好地支撑那一个富媒体音讯,具体表明如下:

往聊天室发送新闻

发送消息特轻巧,通过如下代码就能够向特定聊天室发送消息了:

    Group group = SessionManager.getInstance(selfId).getGroup(groupId);
    group.sendMessage(new AVMessage("hello world"));

发送成功之后,AVGroupMessageReceiver 子类中的 onMessageSent 方法会被调用,反之则 onMessageFailure 方法会被调用。

基类:AVIMTypedMessage

具备富媒体音讯的基类,其声称为

//SDK定义的消息类型,LeanCloud SDK 自身使用的类型是负数,所有正数留给开发者自定义扩展类型使用,0 作为「没有类型」被保留起来。
enum AVIMReservedMessageType {
  UnsupportedMessageType(0),
  TextMessageType(-1),
  ImageMessageType(-2),
  AudioMessageType(-3),
  VideoMessageType(-4),
  LocationMessageType(-5),
  FileMessageType(-6);
};

public abstract class AVIMTypedMessage extends AVIMMessage {
  public AVIMTypedMessage();

  public int getMessageType();

  @Override
  public final String getContent();

  @Override
  public final void setContent(String content);
}

收受聊天室新闻

采用八个聊天室的新闻,与接受单聊的新闻未有差距于,须要开采者完毕AVGroupMessageReceiver 接口,并在 AndroidManifest.xml 中登记就能够,如代码清单 8 所示:

public class CustomeGroupMsgReceiver extends AVGroupMessageReceiver {
    ...
    @Override
    public void onMessage(final Context context, Group group, AVMessage avMsg) {
        Logger.d("onMessage "+avMsg.getMessage());
        // 进行上层逻辑处理,譬如 UI 展示,或者消息提醒。
    }
    ...
}

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

文件音讯(AVIMTextMessage)

AVIMTypedMessage 子类,表示常常的文件音信,其宣称为

public class AVIMTextMessage extends AVIMTypedMessage {
  public String getText();
  public void setText(String text);

  public Map<String, Object> getAttrs();
  public void setAttrs(Map<String, Object> attr);
}

能够见见,对于文本新闻,首要的本性有 textattr 三个,通过简单的 getter/setter 就足以访谈到。要发送文书音信,示例代码为:

AVIMTextMessage message = new AVIMTextMessage();
message.setText("hello");
conversation.sendMessage(message, new AVIMConversationCallback() {
  @Override
  public void done(AVException e) {
    if (null != e) {
      // 出错了。。。
      e.printStackTrace();
    } else {
      Logger.d("message sent.");
    }
  }
});

查询聊天室成员

在投入二个聊天室之后,大家先是步正是走访有何样人在这几个群组里面。LeanMessage 和 LeanStorage 是构成在一块儿的,通过行使 LeanStorage 的数量存款和储蓄功用,来保存八个聊天室的中坚消息(表名:AVOSRealtimeGroups),在 LeanStorage 应用管理平台的数码基本,大家得以见到 AVOSRealtimeGroups 的有着字段。

LeanStorage 的数量大旨
LeanStorage 也是 LeanCloud 平台的着力服务之一,提供了使用内数据和文件数量的积存功用。对于利用内数据,LeanStorage 协助 schema free 的积累,开垦者无需事先定义数据的格局,只要顺应 JSON 格式的 Object 都足以随便存储到 LeanStorage 云端。同期,LeanStorage 也提供八个 Web 版的多少管理分界面,能够十分便于地增、删、改、查任何数据。

理所当然,在大家了解一个聊天室的 groupId 的时候,也能够在代码中,通过 AVObject 的 fetch 接口来查阅这几个聊天室的组员情状,代码如清单 9 所示:

    AVObject groupObject = AVObject.createWithoutData("AVOSRealtimeGroups",groupId);
    groupObject.fetch();//如果您在UI进程中,请使用异步方法调用
    List groupMembers= groupObject.getList("m");

严防系统线程阻塞!
追思一下,在运动应用程序中,长日子的操作(如互连网、文件或长的计量)不应有在主系统线程上到位。相反,应在一个独自的办事线程中实行它们。阻塞系统线程会对应用程序的客户分界面包车型的士响应技术产生负面影响,有相当的大可能率形成强行关闭您的应用程序。

文本音讯(AVIMFileMessage)

AVIMTypedMessage 子类,用来发送带附属类小部件的音讯,开辟者能够用它来发送「离线文件」。对于此类音讯,LeanCloud IM SDK 内部会先把公文上传到 LeanCloud 文件存储服务器(自带 CDN 作用),然后把文件元数据(url,文件大小等等)放在新闻包内发送到 LeanCloud 实时通讯服务端。其构造函数注脚为:

// 传入本地文件路径,构造消息对象
public AVIMFileessage(String localPath) throws FileNotFoundException, IOException;
// 传入本地文件,构造消息对象
public AVIMFileMessage(File localFile) throws FileNotFoundException, IOException;
// 传入 AVFile 实例,构造消息对象
public AVIMFileMessage(AVFile file);

与公事音信看似,文件音讯也支撑附带文本和别的自定义属性,能够通过如下方法增加/ 获取更加的多音信:

  • String getText() / void setText(String text)
  • Map<String, Object> getAttrs() / void setAttrs(Map<String, Object> attr);

出殡文书音信的示范代码为:

String localZipfilePath;
try {
  AVIMFileMessage message = new AVIMFileMessage(localZipfilePath);
  message.setText("这是你要的文档");
  conversation.sendMessage(message, new AVIMConversationCallback() {
    @Override
    public void done(AVException e) {
      if (null != e) {
        // 出错了。。。
        e.printStackTrace();
      } else {
        Logger.d("message sent");
      }
    }
  });
} catch (Exception ex) {
}

收下到那样音讯之后,开拓者能够通过以下办法,获取到文件元数据(size 等)和贰个蕴涵二进制数据的 AVFile 对象:

  • AVFile getAVFile() 方法会重临一个二进制文件的 AVFile 实例,之后方可由此 AVFile 来产生数据下载或许其余操作,具体能够参见 AVFile 说明
  • String getFileUrl() 方法会重回二进制文件的 url
  • long getSize() 方法会重回二进制文件的莫过于尺寸(单位:byte)
  • Map<String, Object> getFileMetaData() 可以赢得二进制文件的其他元数据消息。

成员管理

在查询到聊天室成员之后,能够让客商特邀一些友好的爱侣加入,作为管理员也得以去除一些「可怕」的分子。代码如清单 10 所示:

    Group group = SessionManager.getInstance(selfId).getGroup(groupId);
    List<String> toInvite = Arrays.asList("peerId1","peerId2","peerId3");
    group.inviteMember(toInvite);
    List<String> toKickOff = Arrays.asList("badBoy1","badBoy2");
    group.kickMembers(toKickOff);

特邀成功今后,公告的流水生产线是那样的:

    操作者(管理员)                           被邀请者                        其他人
1,发出请求 inviteMember
2,AVGroupMessageReceiver.onInvited
3,                                      AVGroupMessageReceiver.onJoined
4,AVGroupMessageReceiver.onMemberJoin                          AVGroupMessageReceiver.onMemberJoin

对应地,踢人的流程如下:

    操作者(管理员)                           被踢者                         其他人
1,发出请求 kickMember
2,AVGroupMessageReceiver.onKicked
3,                                    AVGroupMessageReceiver.onQuit
4,AVGroupMessageReceiver.onMemberLeft                         AVGroupMessageReceiver.onMemberLeft

图像音信(AVIMImageMessage)

AVIMFileMessage 子类,特意用来发送图像和附带文本的掺和音信,其构造函数注明为:

// 传入本地文件路径,构造消息对象
public AVIMImageMessage(String localPath) throws FileNotFoundException, IOException;
// 传入本地文件,构造消息对象
public AVIMImageMessage(File localFile) throws FileNotFoundException, IOException;
// 传入 AVFile 实例,构造消息对象
public AVIMImageMessage(AVFile file);

发送图像音信的示范代码为:

String localImagePath;
try {
  AVIMImageMessage message = new AVIMImageMessage(localImagePath);
  message.setText("你说我好看不?");
  conversation.sendMessage(message, new AVIMConversationCallback() {
    @Override
    public void done(AVException e) {
      if (null != e) {
        // 出错了。。。
        e.printStackTrace();
      } else {
        Logger.d("message sent");
      }
    }
  });
} catch (Exception ex) {
}

收取到那般音信随后,开采者可以通过如下方法,获取到多少图像元数据(width,height,图像 size)和多少个带有图像数据的 AVFile 对象:

  • int getWidth() 方法会重临图像的增幅(单位:pixel)
  • int getHeight() 方法会重临图像的莫大(单位:pixel)
  • AVFile getAVFile() (承接自 AVIMFileMessage)方法会再次回到贰个图像文件的 AVFile 实例
  • String getFileUrl() (承接自 AVIMFileMessage)方法会重临图像文件的 url
  • long getSize() (承继自 AVIMFileMessage)方法会再次回到图像文件的莫过于尺寸(单位:byte)
  • String getText() (承袭自 AVIMFileMessage)方法会重回随图像一齐发送的文件音信。
  • Map<String, Object> getFileMetaData() (承袭自 AVIMFileMessage)能够拿走图像的别的元数据新闻。

查询历史音信

LeanMessage 会将非暂态音讯自动保存在云端,之后开荒者能够透过 AVHistoryMessageQuery 这一个指标来进行询问。AVHistoryMessageQuery 定义如清单 11 所示:

public class AVHistoryMessageQuery {
    int limit;
    String convid;
    String from;
    long timestamp;

    /**
     * 设置查询返回集合的大小
     * 默认100,最大1000
     */
    public void setLimit(int limit);

    /**
     * 设定聊天的发起人是谁
     */
    public void setFrom(String from);

    /**
     * 设置查询从哪个时间开始的聊天记录
     */
    public void setTimestamp(long timestamp);

    /**
     * 指定聊天记录查询条件中,聊天发送的对象条件
     */
    public void setPeerIds(List<String> peerIds);

    /**
     * 同步方法查询聊天记录
     * 请确保在一个异步方法中调用此方法,否则会出现UI线程中的网络请求而导致的UI卡死
     */
    public List<AVHistoryMessage> find() throws AVException;

    /**
     * 异步方法查询聊天记录
     */
    public void findInBackground(HistoryMessageCallback callback);

    /**
     * 此接口为异步查询聊天记录的回调类
     */
    public static interface HistoryMessageCallback;
}

透过 AVHistoryMessageQuery 查询获得的结果是 AVHistoryMessage,该类的概念如清单 12 所示:

public class AVHistoryMessage extends AVMessage {
    /**
     * 查看是否属于聊天室聊天记录
     */
    public boolean isRoom();

    /**
     * 查看聊天记录所在的conversation Id,对于 Group 来说等于 GroupID,对于单聊来说,是内部生成的一个值。
     */
    public String getConvid();
}

聊天记录的询问的中坚方法跟 AVQuery 类似然而略有不一样。 针对 Session 的聊天记录和聊天室 Group 的聊天记录查询略有分歧,可是基本流程是同等(代码清单 12):

       String selfId = "Tom";
       SessionManager sm = SessionManager.getInstance(selfId);
       List<String> peers = new ArrayList<String>();
       peers.add(selfId);
       peers.add("Bob");
       AVHistroyMessageQuery sessionHistoryQuery = sm.getHistroyMessageQuery();
       sessionHistoryQuery.setLimit(100);  //设置查询结果大小
       sessionHistoryQuery.setPeerIds(peers); // 设置单聊的参与方,多个参与者之间是「与」的关系
       sessionHistoryQuery.setTimestamp(1413184345686);  //查询时间片1413184345686以前的聊天记录
       sessionHistoryQuery.findInBackground(new HistoryMessageCallback() {
             @Override
             public void done(List<AVHistoryMessage> messages, AVException error) {
                  System.out.println(messages.size());
             }
       });//查询session里的聊天记录

       Group group = sm.getGroup("140a534fd092809500e6d651e73400c7");
       AVHistroyMessageQuery groupHistoryQuery = group.getHistoryMessageQuery();//获取AVHistoryMessageQuery对象来查询聊天室的聊天记录
       groupHistoryQuery.findInBackground(new HistoryMessageCallback(){
            @Override
            public void done(List<AVHistoryMessage> messages,AVException error){
              for(AVHistoryMessage msg:messages){
                 System.out.println(msg.getMessage());
              }
            }
       })

地点第二个查询会获得「汤姆」和「Bob」在特按期间点在此以前的 100 条聊天记录;第2个查询会得到一定聊天室的保有聊天记录(就算总量不超过一千 条的话)。

旋律音信(AVIM奥迪(Audi)oMessage)

AVIMFileMessage 子类,特意用来发送语音和附带文本的掺和音讯,其构造函数申明为:

// 传入本地文件路径,构造消息对象
public AVIMAudioMessage(String localPath) throws FileNotFoundException, IOException;
// 传入本地文件,构造消息对象
public AVIMAudioMessage(File localFile) throws FileNotFoundException, IOException;   
// 传入 AVFile 实例,构造消息对象
public AVIMAudioMessage(AVFile file);

发送音频音讯的示范代码为:

String localAudioPath;
try {
  AVIMAudioMessage message = new AVIMAudioMessage(localAudioPath);
  message.setText("听听我唱的小苹果:)");
  conversation.sendMessage(message, new AVIMConversationCallback() {
    @Override
    public void done(AVException e) {
      if (null != e) {
        // 出错了。。。
        e.printStackTrace();
      } else {
        Logger.d("message sent");
      }
    }
  });
} catch (Exception ex) {
}

接收到那般音信随后,开荒者能够通过如下方法,获取到多少音频元数据(时间长度duration、音频 size)和多少个含有音频数据的 AVFile 对象:

  • double getDuration() 方法会重临音频的长度(单位:秒)
  • AVFile getAVFile() (承接自 AVIMFileMessage)方法会再次来到多个音频文件的 AVFile 实例
  • String getFileUrl() (传承自 AVIMFileMessage)方法会重返音频文件的 url
  • long getSize() (传承自 AVIMFileMessage)方法会重回音频文件的其实尺寸(单位:byte)
  • String getText() (承袭自 AVIMFileMessage)方法会重返随音频一同发送的文书消息。
  • Map<String, Object> getFileMetaData() (承袭自 AVIMFileMessage)能够赢得音频的别的元数据音信。

放眼查看全体聊天室

翻开全数聊天室的法子和查看单个聊天室成员的法子类似,都是直接通过 AVQuery 只怕 AVObject 来遍历 AVOSRealtimeGroups 表完成的,这里不再赘述。

录像新闻(AVIMVideoMessage)

AVIMFileMessage 子类,特地用来发送录像和附带文本的交集音讯,其构造函数评释为:

// 传入本地文件路径,构造消息对象
public AVIMVideoMessage(String localPath) throws FileNotFoundException, IOException;
// 传入本地文件,构造消息对象
public AVIMVideoMessage(File localFile) throws FileNotFoundException, IOException;
// 传入 AVFile 文件,构造消息对象
public AVIMVideoMessage(AVFile file);

出殡录制音信的亲自过问代码为:

String localVideoPath;
try {
  AVIMVideoMessage message = new AVIMVideoMessage(localVideoPath);
  message.setText("敢不敢跟我比一比");
  conversation.sendMessage(message, new AVIMConversationCallback() {
    @Override
    public void done(AVException e) {
      if (null != e) {
        // 出错了。。。
        e.printStackTrace();
      } else {
        Logger.d("message sent");
      }
    }
  });
} catch (Exception ex) {
}

收纳到如此新闻之后,开荒者能够能够透过如下方法,获取到几何摄像元数据(时间长度duration、摄像 size)和多少个包罗录制数据的 AVFile 对象:

  • double getDuration() 方法会再次回到录像的尺寸(单位:秒)
  • AVFile getAVFile() (承接自 AVIMFileMessage)方法会重返一个录制文件的 AVFile 实例
  • String getFileUrl() (承继自 AVIMFileMessage)方法会再次来到摄像文件的 url
  • long getSize() (承袭自 AVIMFileMessage)方法会重返录制文件的实在尺寸(单位:byte)
  • String getText() (承继自 AVIMFileMessage)方法会再次回到随录制一齐发送的文件音信。
  • Map<String, Object> getFileMetaData() (继承自 AVIMFileMessage)能够拿走录制的别的元数据音信。

聊天记录和平安

前面达成了单聊、群聊、富媒体聊天比很多职能,然则开荒者也许已经开掘了,那都以平昔调用 LeanMessage SDK 来贯彻的,对于大家开辟者来讲,能操纵的事物非常少,在安全性上会存在有的揪心。举个例子:万一不怀好意的人破解了本人的 appId 和 appKey,是还是不是就足以在自己的闲谈社区里边扬威耀武?

为了满意开辟者对权力和验证的渴求,LeanMessage 还设计了操作具名的编写制定。咱们能够在 LeanCloud 应用调节台、设置、应用选项中恐吓启用签字(刚毅推荐那样做)。启用后,全体的 Session open 和 watch 行为都亟需证实具名,那样开采者即可对客户登入以及她能够关注如何人,进而能够给哪些人发音讯实行丰富的决定。

签名选拔 Hmac-sha1 算法,输出字节流的十六进制字符串 (hex dump),签名的音讯格式如下:

app_id:peer_id:watch_peer_ids:timestamp:nonce

其中:

  • app_id 是你的施用 ID
  • peer_id 是开垦此 Session 的 Peer ID
  • watch_peer_ids 是 open 或 watch 诉求中关切的 peer ids,升序排序后以: 分隔
  • timestamp 是现阶段的 UTC 时间距离 unix epoch 的秒数
  • nonce 为随机字符串

在群组操作中,LeanMessage 对加群、邀约和踢出群这五个动作也允许进入签字,它的签订公约格式是:

app_id:peer_id:group_id:group_peer_ids:timestamp:nonce:action

其中:

  • app_id, peer_id, timestamp 和 nonce 同上
  • group_id 是本次行为涉及的群组 ID,对于开创群尚未有 id 的情事,group_id 是空字符串
  • group_peer_ids 是: 分隔的升序排序的 peer id,即特邀和踢出的 peer_id;对参与群的动静,这里是空字符串
  • action 是本次作为的动作,三种行为分别对应常量 join, invite 和 kick

签订合同的 key 是行使的 master key。开拓者能够实现协调的 SignatureFactory,调用远程的服务器的签字接口得到签字。若无团结的服务器,能够直接在 LeanCloud 的云代码上经过 Web Hosting 动态接口达成自身的签订左券接口。在移动使用中中央银行政机关接做签名是丰裕危急的,它恐怕引致你的 master key 泄漏。

LeanCloud 的 appKey 分类
在 LeanCloud 平台上申请了动用之后,LeanCloud 会分配给我们多个key:二个 appKey,二个 master Key。个中 appKey 能够实行一些常见的操作,况兼受到 LeanCloud 平台安全设置的限量,类似于操作系统中的普通 User 账号,所以能够一向用在客商端;master Key 则持有具有权力,类似于操作系统中的 Root/Administrator 账号,所以请稳当保管。

好,有了具名机制之后,我们到底该怎样利用呢?大家只必要达成团结的 SignatureFactory,然后在开启 session 的时候,把这么些 signatureFactory 传进去就可以。示例代码如清单 13 所示:

// Signature 定义如下,由 LeanMessage 提供
public class Signature {
    private String signature;
    private long timestamp;
    private String nonce;
    private List<String> signedPeerIds;

    // getter / setter for properties
    ......
}
// customise signature factory,由开发者实现
public MySignatureFactory implements SignatureFactory {
    @override
    public Signature createSignature(String selfId, List<String> watchIds) {
        // call remote server for correct signature.
    }
   @override
    public Signature createGroupSignature(String groupId, String selfId, List<String> targetPeerIds,
  String action) {
        // call remote server for correct group signature.
    }
}

// open session with signature factory.
SignatureFactory signatureFacatory = new MySignatureFactory();
SessionManager sm = SessionManager.getInstance(selfId);
sm.setSignatureFactory(signatureFactory);
sm.open(selfId);

设定了 SignatureFactory 之后,对于急需鉴权的操作,LeanMessage SDK 与劳动器端通信的时候都会带上应用本身生成的 Signature 音信,那样 LeanMessage 服务器端就能够利用 app 的 masterKey 来评释消息的可行,保证聊天渠道的安全。

地理地方消息(AVIMLocationMessage)

AVIMTypedMessage 子类,协理发送地理地点信息和附带文本的混合音讯,其宣称为:

public class AVIMLocationMessage extends AVIMTypedMessage {
  public String getText();
  public void setText(String text);

  public Map<String, Object> getAttrs();
  public void setAttrs(Map<String, Object> attr);

  public AVGeoPoint getLocation();
  public void setLocation(AVGeoPoint location);
}

与公事音信看似,地理地点消息只是增添了三个 AV吉优Point 的 Location 属性。要发送地点音讯的身体力行代码为:

AVIMLocationMessage message = new AVIMLocationMessage();
message.setText("快点过来!");
message.setLocation(new AVGeoPoint(15.9, 56.4));
conversation.sendMessage(message, new AVIMConversationCallback() {
  @Override
  public void done(AVException e) {
    if (null != e) {
      // 出错了。。。
      e.printStackTrace();
    } else {
      Logger.d("message sent");
    }
  }
});

收到到这么的音信随后,开辟者能够收获到具体的地理地点数据。

结束语

LeanMessage 是一个分外稳固可信赖的闲谈服务平台,提供的效果与利益也能够满足大家选择开荒者的须求。这里自个儿经过介绍 LeanMessage API 来贯彻利用内的单聊、群聊、富媒体音讯等基本功用,可是LeanMessage 还帮助更加的多高阶功用,比如 Super Peer、敏感词过滤、音讯实时监听等等,有意思味的相爱的人能够一而再深究。

何以吸取富媒体音讯

新版 LeanCloud IM SDK 内部封装了对富媒体音讯的帮助,全数富媒体新闻都是从 AVIMTypedMessage 派生出来的。发送的时候能够一贯调用 conversation.sendMessage() 函数。在接收端,我们也专程扩大了一类回调接口 AVIMTypedMessageHandler,其定义为:

public class AVIMTypedMessageHandler<T extends AVIMTypedMessage> extends MessageHandler<T> {

  @Override
  public void onMessage(T message, AVIMConversation conversation, AVIMClient client);

  @Override
  public void onMessageReceipt(T message, AVIMConversation conversation, AVIMClient client);
}

开采者能够编写本身的音信管理 handler,然后调用 AVIMMessageManager.registerMessageHandler(Class<? extends AVIMMessage> clazz, MessageHandler<?> handler) 函数来注册目的 handler。

接收端对于富媒体音信的通报管理的示范代码如下:

class MsgHandler extends AVIMTypedMessageHandler<AVIMTypedMessage> {

  @Override
  public void onMessage(AVIMTypedMessage message, AVIMConversation conversation, AVIMClient client) {
    // 请按自己需求改写
    switch(message.getMessageType()) {
    case AVIMReservedMessageType.TextMessageType:
      AVIMTextMessage textMsg = (AVIMTextMessage)message;
      Logger.d("收到文本消息:" + textMsg.getText() + ", msgId:" + textMsg.getMessageId());
      break;
    case AVIMReservedMessageType.FileMessageType:
      AVIMFileMessage fileMsg = (AVIMFileMessage)message;
      Logger.id("收到文件消息。msgId=" + fileMsg.getMessageId() + ", url=" + fileMsg.getFileUrl() + ", size=" + fileMsg.getSize());
      break;
    case AVIMReservedMessageType.ImageMessageType:
      AVIMImageMessage imageMsg = (AVIMImageMessage)message;
      Logger.id("收到图片消息。msgId=" + imageMsg.getMessageId() + ", url=" + imageMsg.getFileUrl() + ", width=" + imageMsg.getWidth() + ", height=" + imageMsg.getHeight());
      break;
    case AVIMReservedMessageType.AudioMessageType:
      AVIMAudioMessage audioMsg = (AVIMAudioMessage)message;
      Logger.id("收到音频消息。msgId=" + audioMsg.getMessageId() + ", url=" + audioMsg.getFileUrl() + ", duration=" + audioMsg.getDuration());
      break;
    case AVIMReservedMessageType.VideoMessageType:
      AVIMVideoMessage videoMsg = (AVIMAudioMessage)message;
      Logger.id("收到视频消息。msgId=" + videoMsg.getMessageId() + ", url=" + videoMsg.getFileUrl() + ", duration=" + videoMsg.getDuration());
      break;
    case AVIMReservedMessageType.LocationMessageType:
      AVIMLocationMessage locMsg = (AVIMLocationMessage)message;
      Logger.id("收到位置消息。msgId=" + locMsg.getMessageId() + ", latitude=" + locMsg.getLocation().getLatitude() + ", longitude=" + locMsg.getLocation().getLongitude());
      break;
    }
  }

  @Override
  public void onMessageReceipt(AVIMTypedMessage message, AVIMConversation conversation, AVIMClient client) {
  }
}
MsgHandler msgHandler = new MsgHandler();
AVIMMessageManager.registerMessageHandler(AVIMTypedMessage.class, msgHandler);

LeanCloud IM SDK 内部新闻分发的逻辑是这么的:对于收受的任一新音信,SDK 内部都会先剖判音信的花色,依照项目找到开拓者为这一档案的次序注册的拍卖 handler,然后所有人家调用这么些 handler 的 onMessage 函数。若无找到特意管理这一品类消息的 handler,就能传递给 defaultHandler 管理。

那样一来,在开采者为 TypedMessage(及其子类) 钦赐了极其的 handler,也钦赐了大局的 defaultHandler 了的时候,如若发送端发送的是通用的 AVIMMessage 新闻,那么接受端即是 AVIMMessageManager.registerDefaultMessageHandler()中钦定的 handler 被调用;倘若发送的是 AVIMTypedMessage(及其子类)的音信,那么接受端就是 AVIMMessageManager.registerMessageHandler()中内定的 handler 被调用。

哪些扩充自个儿的富媒体音讯

延续于 AVIMTypedMessage,开拓者也得以扩充本人的富媒体新闻。其须要和手续是:

  • 兑现新的新闻类型,继承自 AVIMTypedMessage。这里需求静心两点:
    • 在 class 上加码二个 @AVIMMessageType(type=123) 的 Annotation,具体音讯类型的值(123)由开垦者本人支配(LeanCloud 内建的音讯类型应用负数,全体正数都留下给开拓者扩张使用)。
    • 在信息内部属性上要加进 @AVIMMessageField(name="") 的 Annotation,name 为可选字段在宣称字段属性,同不平时间自定义的字段要有照管的 getter/setter 方法。
  • 调用 AVIMMessageManager.registerAVIMMessageType(Class<? extends AVIMTypedMessage> messageType) 函数实行项目注册
  • 调用 AVIMMessageManager.registerMessageHandler(Class<? extends AVIMMessage> clazz, MessageHandler<?> handler) 函数举行消息管理 handler 注册。

AVIMTextMessage 的源码如下,可供参谋:

@AVIMMessageType(type = -1)
public class AVIMTextMessage extends AVIMTypedMessage {

  @AVIMMessageField(name = "_lctext")
  String text;
  @AVIMMessageField(name = "_lcattrs")
  Map<String, Object> attrs;

  public String getText() {
    return this.text;
  }

  public void setText(String text) {
    this.text = text;
  }

  public Map<String, Object> getAttrs() {
    return this.attrs;
  }

  public void setAttrs(Map<String, Object> attr) {
    this.attrs = attr;
  }
}

群组聊天

与日前的单聊类似,群组聊天也急需先成立三个对话(AVIMConversation),然后发送、接收新的消息。

创建群组

和单聊类似,建构二个四人聊天的群组也是很简短的。举例:

Map<String, Object> attr = new HashMap<String, Object>();
attr.put("type", ConversationType_Group);
imClient.createConversation(clientIds, attr, new AVIMConversationCreatedCallback() {
  @Override
  public void done(AVIMConversation conversation, AVException e) {
    if (null != conversation) {
      // 成功了!
      Intent intent = new Intent(currentActivity, ChatActivity.class);
      Intent.putExtra(“conversation”, conversation);
      currentActivity.startActivity(intent);
    }
  }
});

成功现在,大家就足以进来聊天分界面了。

往群组发送音信

发送消息特别轻松,与前面单聊的处境同样。

作者们会注意到,AVIMConversation 还也是有一个发送消息的方法:

public void sendMessage(final AVIMMessage message, final int messageFlag,
      final AVIMConversationCallback callback)

而这里 flag 的定义有如下二种档案的次序:

  • 暂态音讯(AVIMConversation.TRANSIENT_MESSAGE_FLAG)。这种新闻不会被机关保存(现在在历史新闻中不能找到它),也不扶助延迟接收,离线顾客更不会收到推送布告,所以相符用来做调控左券。譬喻聊天进度中「某某正在输入中...」那样的情状新闻,就切合通过暂态音讯来发送。
  • 平日音信(AVIMConversation.NONTRANSIENT_MESSAGE_FLAG)。这种音讯正是大家最常用的音信类型,在 LeanCloud 云端会活动保存起来,帮助延迟接收和离线推送,以往在历史音信中得以找到它。
  • 待回执新闻(AVIMConversation.RECEIPT_MESSAGE_FLAG)。这也是一种日常新闻,只是消息被对方接到之后 LeanCloud 服务端会发送两个回执布告给发送方(这就是AVIMMessageHandler 中 public void onMessageReceipt(AVIMMessage message, AVIMConversation conversation, AVIMClient client) 函数被调用的空子)。

抽取群组音信

收纳四个群组的新闻,与吸收接纳单聊的音讯也是完全一样的。

分子管理

在询问到聊天室成员之后,能够让顾客邀约部分要好的爱人步向,作为管理员也足以去除一些「可怕」的成员。
投入新成员的 API 如下:

// 假设需要邀请 Alex,Ben,Chad 三人加入对话
List<String> userIds = new ArrayList<String>();
userIds.add("Alex");
userIds.add("Ben");
userIds.add("Chad");
conversation.addMembers(userIds, new AVIMConversationCallback() {
  @Override
  public void done(AVException error) {
    if (null != error) {
      // 加入失败,报错.
      error.printStackTrace();
    } else {
      // 发出邀请,此后新成员就可以看到这个对话中的所有消息了。
      Logger.d("invited.");
    }
  }
});

特邀成功之后,相关方收到布告的时序是这么的:

    操作者(管理员)                    被邀请者                        其他人
1, 发出请求 addMembers
2,                               收到 onInvited 通知
3, 收到 onMemberJoined 通知      收到 onMemberJoined 通知      收到 onMemberJoined 通知

对应地,踢人时的调用 API 是:

List<String> userIds = new ArrayList<String>();
userIds.add("Alex");
conversation.kickMembers(userIds, new AVIMConversationCallback() {
  @Override
  public void done(AVException error) {
    if (null != error) {
      // 失败,报错.
      error.printStackTrace();
    } else {
      // 成功。
      Logger.d("kicked.");
    }
  }
});

踢人时,相关方收到通告的时序如下:

    操作者(管理员)                被踢者                       其他人
1, 发出请求 kickMembers
2,                          收到 onKicked 通知
3, 收到 onMemberLeft 通知                             收到 onMemberLeft 通知

注意!
假设约请、踢人操作发生的时候,被邀约者/被踢者当前不在线,那么通告音信并不会被离线缓存,所以他们再上线的时候将不会摄取布告。

赢得历史音信

LeanMessage 会将非暂态音讯自动保存在云端,之后开辟者能够由此AVIMConversation 来博取该对话的有所历史新闻。获取历史新闻的 API 如下:

String oldestMsgId;
long oldestMsgTimestamp;
conversation.queryMessages(oldestMsgId,oldestMsgTimestamp, limit, new AVIMHistoryMessageCallback(){
  @Override
  public void done(List<AVIMMessage> messages, AVException e) {
    if (null != e) {
      // 出错了:(
    } else {
      // 成功,可以将消息加入缓存,同时更新 UI
    }
  }
});

注意:
获得历史音讯的时候,LeanCloud 云端是从某条音讯起先,往前找寻开拓者钦命的 N 条音信,再次回到给顾客端。为此,获取历史新闻需求传入八个参数:起先音讯的 msgId,最早音讯的发送时间戳,必要取得的新闻条数。

透过这一 API 得到的新闻正是 AVIMMessage 还是 AVIMTypedMessage 实例数组,开垦者能够像在此以前接受新新闻文告一致管理。

启用离线音讯推送(仅对 iOS 平台客商有效)

无论是是单聊仍旧群聊,当顾客 A 发出音信后,借使目的对话中有一对顾客日前不在线,LeanCloud 云端能够提供离线推送的主意来提醒客户。这一成效默许是停业的,你能够在 LeanCloud 应用调整台南拉开它。开启方法如下:

  • 登陆 LeanCloud 应用调节台,选拔精确的行使步入;
  • 选用最上端的「音信」服务,依次点击左侧菜单「实时音信」->「设置」;
  • 在左手「iOS 客商离线时的推送内容」下填好你要推送出去的音讯内容,保存;

这么 iOS 平台上的客商就能够收到 Push Notification 了(当然,前提是选取本人申请到了 RemoteNotification 权限,也将正确的推送证书上传到了 LeanCloud 调控台)。

群组信息免打扰(仅对 iOS 平台顾客有效)

甭管是单聊依旧群聊,对于发往普通的 Conversation 的常备音信,假使接收方当前不在线,LeanCloud 云端支持通过 Push Notification 的秘籍进行提示。日常情状下那都以很好的,但是只要某些群组特别活跃,那离线客商就能够吸收接纳过多的推送,会形成十分的大的打扰。

对此 LeanCloud IM 服务也允许单个客商来关闭/展开有些对话的离线推送功用。

搜寻群组

随意是单聊,还是群聊,在 LeanCloud IM SDK 里面都是对话(Conversation)。大家给对话设置了如下三种属性:

  • conversationId,字符串,对话 id,只读,对话成立之后由 LeanCloud 云端赋予一个大局独一的 id。
  • creator,字符串,对话创立者 id,只读,标志对话创设者消息
  • members,数组,对话加入者,这里记录了拥有的参加者
  • name,字符串,对话的名字,optional,可用来对于群组命名
  • attributes,Map/Dict,自定义属性,optional,供开垦者本人扩展用。

大家提供了特意的类,来寻找一定的群组:通过 imClient.getQuery() 获得贰个 AVIMConversationQuery 实例,然后调用 AVIMConversationQuery.wherexxx 多级措施来充实约束原则。比如要物色当前报到客户出席的具有群聊对话,其代码为

// 搜索 Tom 参与的所有群组对话
List<String> clients = new ArrayList<String>();
clients.add("Tom");
AVIMConversationQuery conversationQuery = imClient.getQuery();
conversationQuery.containsMember(clients);

// 之前有常量定义:
// const int ConversationType_OneOne = 0;
// const int ConversationType_Group = 1;
conversationQuery.whereEqualTo("attr.type", ConversationType_Group);

conversationQuery.findInBackground(new AVIMConversationQueryCallback(){
  @Override
  public void done(List<AVIMConversation> conversations, AVException e) {
    if (null != e) {
      // 出错了。。。
      e.printStackTrace();
    } else {
      if (null != conversation) {
        Logger.d("找到了符合条件的 " + conversations.size() + " 个对话");
      } else {
        Logger.d("没有找到符合条件的对话");
      }
    }
  }
});

AVIMConversationQuery 中设置规范的不二秘诀与 AVQuery 类似。这里 conversationQuery.containsMember() 表示对话的成员中最少含有那么些人口,可用来依照局地成员查找对话;与此类似的还恐怕有一个 conversationQuery.withMembers() 则表示有且独有那一个分子,用来依照全体成员查找目的对话;conversationQuery.whereXXX() 文山会海措施可用来限制对话名称和自定义属性,这里要强调的有些是,对于自定义属性的约束标准,属性名绝对要以 attr 最初,如上例所示,限定额外的 type 条件的时候须求内定的属性名是 attr.type。具体能够参照他事他说加以考察其头文件。

绽开聊天室

盛开聊天室(也叫暂态对话)能够用来相当多地方,譬喻弹幕、直播等等。在 LeanCloud IM SDK 中,开放聊天室是一类特殊的群组,它也帮助创设、出席/踢出成员等操作,音讯记录会被封存并可供获取;与普通群组不等同的地点实际映现为:

  • 不援协助调查询成员列表,你能够经过有关 API 查询在线人数;
  • 不协理离线新闻、离线推送布告等效果;
  • 未有成员加入、离开的通报;
  • 三个顾客二遍登入只可以踏向四个开放聊天室,插足新的开放聊天室后会自动离开原先的聊天室;
  • 参预后三时辰内断网重连会活动步入原聊天室,超越那几个时刻则必要重新加入;

创制开放聊天室

和常见的群组类似,营造三个吐放聊天室也是很简单的,只是在 AVIMClient.createConversation(conversationMembers, name, attributes, isTransient, callback) 中大家供给传入 isTransient=true 选项。例如:

Map<String, Object> attr = new HashMap<String, Object>();
attr.put("type", ConversationType_Group);
imClient.createConversation(clientIds, name, attr, true, new AVIMConversationCreatedCallback() {
  @Override
  public void done(AVIMConversation conversation, AVException e) {
    if (null != conversation) {
      // 成功了,进入聊天室
      Intent intent = new Intent(currentActivity, ChatActivity.class);
      Intent.putExtra(“conversation”, conversation);
      currentActivity.startActivity(intent);
    }
  }
});

创设成功以往,大家就可以进来聊天分界面了。开放聊天室的其余操作,都与平时群组操作同样。

加盟开放聊天室

比如任何极端客商都足以步向开放聊天室。作为开采者,大家得以因此通过特定条件检索到具有开放聊天室,然后允许客户自由步入,其演示代码为:

conversation.join(new AVIMConversationCallback(){
  @Override
  public void done(AVException e) {
    if (null != e) {
      // 出错了:(
    } else {
      // 成功,此时可以进入聊天界面了。。。
      Intent intent = new Intent(currentActivity, ChatActivity.class);
      Intent.putExtra(“conversation”, conversation);
      currentActivity.startActivity(intent);
    }
  }
});

询问在间谍数

通过 AVIMConversation.getMemberCount() 方法能够实时查询开放聊天室的在眼线数。示例代码如下:

conversation.getMemberCount(new AVIMConversationMemberCountCallback(){
  @Override
  public void done(Integer memberCount, AVException e) {
    if (null != e) {
      // 出错了:(
    } else {
      // 成功,此时 memberCount 的数值就是实时在线人数
    }
  }
});

签订合同和平安

为了知足开垦者对权力和认证的须求,LeanCloud 还设计了操作签字的建制。大家得以在 LeanCloud 应用调控台中的「设置」->「应用选项」->「聊天推送」上边勾选「聊天服务签订公约认证」来启用签名(刚强推荐这样做)。启用后,全体的客商登入、对话创制/参加、诚邀成员、踢出成员等操作都急需验证签字,那样开采者就足以对音信实行充裕的垄断。

顾客端那边毕竟该怎样选取啊?我们只必要完毕 SignatureFactory 接口,然后在顾客登入此前,把那些接口的实例赋值给 AVIMClient 就可以(AVIMClient.setSignatureFactory(factory))。

设定了 signatureFactory 之后,对于急需鉴权的操作,LeanCloud IM SDK 与劳动器端通信的时候都会带上应用本身生成的 Signature 音讯,LeanCloud 云端会接纳 app 的 masterKey 来表明音信的一蹴而就,保险聊天路子的安全。

对于 SignatureFactory 接口,我们只必要贯彻那八个函数就能够:

  /**
   * 实现一个基础签名方法 其中的签名算法会在SessionManager和AVIMClient(V2)中被使用
   */
  public Signature createSignature(String peerId, List<String> watchIds) throws SignatureException;

  /**
   * 实现AVIMConversation相关的签名计算
   */
  public Signature createConversationSignature(String conversationId, String clientId,
      List<String> targetIds, String action) throws SignatureException;

createSignature 函数会在客户登陆的时候被调用,createConversationSignature 会在对话创设/插手、诚邀成员、踢出成员等操作时被调用。

您须求做的正是安分守己前文所述的签字算法完成签订公约,当中 Signature 评释如下:

public class Signature {
  public List<String> getSignedPeerIds();
  public void setSignedPeerIds(List<String> signedPeerIds);

  public String getSignature();
  public void setSignature(String signature);

  public long getTimestamp();
  public void setTimestamp(long timestamp);

  public String getNonce();
  public void setNonce(String nonce);
}

其间多少个属性分别是:

  • signature 签名
  • timestamp 时间戳,单位秒
  • nonce 随机字符串 nonce
  • signedPeerIds 放行的 clientId 列表,v2 中已经抛开不用

上面包车型大巴代码显示了基于 LeanCloud 云代码实行签字时,客商端的达成部分,你能够参照它来成功自个儿的逻辑达成:

public class KeepAliveSignatureFactory implements SignatureFactory {
 @Override
 public Signature createSignature(String peerId, List<String> watchIds) {
   Map<String,Object> params = new HashMap<String,Object>();
   params.put("self_id",peerId);
   params.put("watch_ids",watchIds);

   try{
     Object result =  AVCloud.callFunction("sign",params);
     if(result instanceof Map){
       Map<String,Object> serverSignature = (Map<String,Object>) result;
       Signature signature = new Signature();
       signature.setSignature((String)serverSignature.get("signature"));
       signature.setTimestamp((Long)serverSignature.get("timestamp"));
       signature.setNonce((String)serverSignature.get("nonce"));
       return signature;
     }
   }catch(AVException e){
     throw (SignatureFactory.SignatureException) e;
   }
   return null;
 }

  @Override
  public Signature createConversationSignature(String convId, String peerId, List<String> targetPeerIds,String action){
   Map<String,Object> params = new HashMap<String,Object>();
   params.put("self_id",peerId);
   params.put("group_id",convId);
   params.put("group_peer_ids",targetPeerIds);
   params.put("action",action);

   try{
     Object result = AVCloud.callFunction("group_sign",params);
     if(result instanceof Map){
        Map<String,Object> serverSignature = (Map<String,Object>) result;
        Signature signature = new Signature();
        signature.setSignature((String)serverSignature.get("signature"));
        signature.setTimestamp((Long)serverSignature.get("timestamp"));
        signature.setNonce((String)serverSignature.get("nonce"));
        return signature;
     }
   }catch(AVException e){
     throw (SignatureFactory.SignatureException) e;
   }
   return null;
  }
}

LeanCloud IM SDK 静心做好底层的通信服务,有越来越多能够定制化的地方,比方说:

  • 账户连串和 IM 系统是分离的;
  • 音讯成为离线推送的时候,推送内容开垦者是能够定制的;
  • 透过 web hook,开荒者能够对消息举行更加多管理;
  • 闲话进程中通过音讯鉴权机制,开垦者能够有越来越多调控;

因为非常不足 UI 组件,一步一个鞋印地讲在新顾客接入开支大概稍高,可是在作业规模庞大、产品供给变多过后,相信我们会进一步喜欢 LeanCloud 这种自由灵活的使用体验,以及牢固急忙的服务品质。

本文由明仕msyz手机版发布于宠物-蜂鸟摄影论坛,转载请注明出处:应用也能聊天

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。