为您的 Android 应用也能够拉(第二版本)

简介

自打去年 LeanCloud
发布实时通信(IM)服务之后,基于用户反映及工程师对需求的克及针对性业务的提炼,上周专业颁布了「实时通信
2.0
」。设计意见还是是「灵活、解耦、可组合、可定制」,具体可以参考《实时通信支出指南》,了解
LeanCloud 实时通信的基本概念和模型。

村庄达到春树,这是一个豪门都好熟稔的名。

下载和设置

可以到 LeanCloud
官方下载点下载
LeanCloud IM SDK v2 本子。将下充斥至的 jar 包加入工程即可。

记我第一不善听说村上春树这个名字或者读初中的时段,那个时候读村上春树的修如是如出一辙栽潮流,好像判定是否是文学青年发出同样修就算是得使读了村达到春树的小说。

相当的文书聊天

咱先行由极度简单易行的环入手,看看怎么用 LeanCloud IM SDK v2
实现一对一文本聊天。

倘那时候的我或者韩寒的粉,自然是没有读了村达到的修了。

初始化

及 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>

接下我们得做到用户登录。

第一坏吃我对此村及春树的小说有兴趣或如穷根究底至高中时每月必读之萌芽,当时八月长安的《那么多年》正在萌芽上连载,记得来一个部分是楚天阔问陈见夏有没发读了《挪威之林海》,陈见夏的反馈是:

登录

要聊天发起方名叫 Tom,为直观起见,我们下用户名来作为 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);
    };
  }
});

“是那以颇受挫的力作吗?”

建对话

如果我们而与「Bob」这个用户展开聊天,我们事先创造一个会话,代码如下:

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);
    }
  }
});

成立之「对话」在控制台怎么查

假设你所展现,我们创建一个对话之时段,指定了成员(Tom 和
Bob)和一个格外的习性({type: 0})。这些数据保存及云端后,你于
控制台 -> 存储 -> 数据 里面会盼,_Conversation
表中益了扳平漫漫记下,新记录的 m 属性值为["Tom", "Bob"]attr
属性值为{"type":0}。如你所预期,m 属性就是本着许在成员列表,attr
属性就是用户多的前额外属性值(以目标的花样储存)。

立刻之我乐弯了腰,一本书中的角色对于外物的评说大老程度达呢体现了作者对于当下件东西之褒贬,而八月长安当作自身好酷爱的大手笔,能够对同一本书做出如此有趣的评头品足,倒是引起了自身极大的好奇心,由此我对于《挪威底老林》这按照开有了兴趣。

出殡信息

确立好对话之后,要发送信息是颇简短的:

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());
    }
  }
});

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

说实话我先是不行读《挪威的丛林》确实是获得在猎奇之心绪去念的,我先是蹩脚看到小说可以这样形容,我首先涂鸦知道人性中原来出这么多未也人知的地方。

消息接收

每当 Bob 这同样端,要能够接受及消息,需要如下几步:

1,进行初始化;

2,实现团结之 AVIMMessageHandler,响应新消息到达通知,主要是之类函数:

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

于 Tom 发过来的信息,要展示出来,我们特需要兑现 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() 之前调用,否则可能致服务器发回的一些信息丢失。

得到在猎奇的心境读毕了及时按照开,我不得不承认自身连没读懂,但自或觉得这是平等照很吸引人口之小说,村及呢真的好有才华。

几只根本的回调接口

打上面的事例中可见见,要接过至人家被你发送的消息,需要重载
AVIMMessageHandler 类。从 v2 版开始,LeanCloud IM SDK
大量运回调来报告操作结果,但是于片低落之音信通知,则还是使用接口来实现之,包括:

  • 时网出现变化
  • 对话中生出新的信息
  • 对话中生新成员在
  • 对话中有成员离开
  • 给邀加入某对话
  • 受蹬来对话

LeanCloud IM SDK 内部以了三种接口来响应这些事件。

如果就这样和村及春树告别,我恐怕自身下就是再也不会拿起他的题又来读了。

网络事件响应接口

一言九鼎用以处理网络转移事件,接口定义在
AVIMClientEventHandler,主要函数为:

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

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

每当网中断的状下,所有的信收发和对话操作都见面现出问题。

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

亚破以起《挪威的林》是当高三的时刻,高三时的自头脑交瘁,有一段时间真的认为好坚持不下去了。不知怎么回事,在那段压力太要命的日子里,我忽然想起了永泽送给渡边的一模一样句话:

对话成员变动响应接口

最主要用于处理对话中成员变动之轩然大波,接口定义在
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。

毫不同情自己,同情自己是见不得人懦夫干的坏事。

消息应接口

重大用来处理新消息到达事件,接口定义在
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());

自身再将起了《挪威的林海》,在每天晚上睡前本人还见面念上等同粗片段,这样才来勇气继续对次龙之存。

支持富媒体的谈天消息

点的代码演示了怎样发送简单文本信息,但是今之交互方式已经越来越多样化,图像、语音、视频已经是十分普遍的信息类型。v2
版的 LeanCloud IM SDK 已经可以生好地支撑这些富媒体消息,具体说明如下:

说来实在要命稀奇,我还会起这样的均等以小说中汲取力量。然而事实就是是本人确实于村及之小说被汲取到了力。

基类: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);
}

是力了,也恐怕是安慰罢。

文本消息(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.");
    }
  }
});

高考了晚底及时同一年里我而相继读了《没有色彩的多崎作和他的巡礼的年》、《国境以南
烨以西》、《遇到任何的女孩》、《且听风吟》和《斯普特尼恋人》。

文件消息(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()
    可以获得二进制文件的其他元数据信息。

当当时同样年里本身竟读懂了村上春树,并容易上了村上春树这号女作家。

图像信息(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)可以博得图像的任何元数据信息。

我们为何爱村及春树?

旋律消息(AVIMAudioMessage)

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)可以取得音频的别样元数据信息。

自家以为一个大要紧的故是:

视频信息(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)可以获视频的旁元数据信息。

盖他会真诚的当人生的庸俗与虚无。

地理位置信息(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);
}

暨公事消息看似,地理位置信息才是加了一个 AVGeoPoint 的 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");
    }
  }
});

收到至这么的音随后,开发者可以获到实际的地理位置数据。

正前把日子我在《萌芽》微信公众号及读到了千篇一律首关于村及春树的推文。

什么样吸收富媒体消息

新版 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 这种自由灵活的使体验,以及稳定快速的劳务品质。

自己不确定村及想要达的是否是这个意思,或许是因自还太年轻气盛吧。

推心置腹之照人生之虚无和世俗是自己看村达到所兼有的极端弥足珍贵的一致起品质。

当互联网时代,我们无聊之早晚总是会失去网络达到搜索刺激,打游戏、看综艺,反正就是说话啊不要为自己的心头停下来,因为要停止下来就是亟须要面对人生的心虚无和世俗。

唯独网络虽然能少缓解人生之虚无和世俗,但心的挺空洞并不曾因此而弥及。

我们且在逃避,逃避倾听我们内心真正的声。我们将全副底流年还花费在倾听别人的想法及,却甚少静下心来去倾听自己心灵的声响。

咱们不歇地美名化生活、娱乐化社会,我们整天忙忙碌碌,却始终不愿意给生的真相。

全总下午睡在铺上通往在上花板,这种毫无意义的政工也许很少有人会再次开了,真诚的面人生的心虚无和世俗啊杀少有人在举行了。

只是生活之困境无法通过别人的活来解决,有时候我们务必承认我们无能为力凭任何人,人生有下便是孤零零而虚无的,每一样步都必须由咱们切身走来,哪怕是只身的一个人也亟须移动下去,哪怕前方是荆棘丛生也务必动下来,只有这么才能确实走来人生之困境。


苟您以人生一样垮糊涂的下将在一本村上之小说,读着读着笑来了声、笑出了泪,那我怀念你大概是爱上村上春树了。