<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>beatles (beatles)</title>
    <link>https://ruby-china.org/beatles</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>创业还需要开发 App 么？ </title>
      <description>&lt;p&gt;微信连续两周发布重量级接口。首先是让网页开发者欢欣鼓舞的 JS SDK，使微信内的 WebView 几乎可以得到微信 Native App 的所有能力。此外，公众平台数据接口，允许开发者获取详细、灵活的运营数据。这两个发布，使这个本来就是入口级的 App 变得更加开放和强大，毫无疑问这是一个微信的时代。在这个时代，该怎样开发互联网产品呢？&lt;/p&gt;
&lt;h2 id="更高效构建 MVP"&gt;更高效构建 MVP&lt;/h2&gt;
&lt;p&gt;听说过「精益创业」的人都知道最小化可行性产品（MVP）的理念 —— 即通过一个最小化、却可以满足核心需求的产品来测试市场的反应，MVP 背后的核心原则就是减少成本来测试想法是否满足用户需求。形象点的比喻就是，在你决定开发一个自动贩卖机在地铁里卖饮料前，请先站在地铁里卖一个星期的饮料，看看是否有人买单。&lt;/p&gt;

&lt;p&gt;产品经理都懂得，用 MVP 的思路来开发产品是非常见效的，产品可以在这个过程中不断收集反馈，持续改进迭代下去。那现在可以怎样构建一个 MVP 产品呢？答案就是开发一个 Web App。&lt;/p&gt;
&lt;h3 id="Web 的涅槃重生"&gt;Web 的涅槃重生&lt;/h3&gt;
&lt;p&gt;曾经的 Web App 可能会被各种吐槽，比如性能差、本地能力弱等，但最大的一个问题是没有入口。性能随着换机不断提高，本地能力随着对 HTML5 的支持和各种 App 的 JavaScript SDK 开放接口也变得愈发强大，而最大的入口问题如今被微信解决。换句话说，微信其实是两个 App，一个是非常强大的聊天软件，另一个是当今用户量最大的浏览器。&lt;/p&gt;

&lt;p&gt;微信自身定位非常好，张小龙给的底线很清楚 ——「非核心业务不要增加客户端成本」，所以类似购物、彩票、打车、电影票等等业务都是通过 Web 来实现。这样既利用 Web 开发成本低的特点，也证明 Web 的能力，最重要的是这利用 Web 最大的优势，就是非常易于分享。从红包类的应用就能够看出来这个优势。&lt;/p&gt;
&lt;h3 id="一个公众号就是一个 App"&gt;一个公众号就是一个 App&lt;/h3&gt;
&lt;p&gt;扫一扫配合微信公众号，则又使用户可以通过二维码、朋友圈分享、好友推荐等入口关注公众号，这可比安装一个 App 成本低的多。而公众号又可以通过自定义菜单、消息推送等方式，以同样低的成本让用户打开自己的 Web App，所以关注一个公众号就相当于安装一个 App。&lt;/p&gt;

&lt;p&gt;比如：微信中的 Web App 可以获取用户基本信息、地理位置、短信验证等，可以通过这些做匹配告诉用户附近都有哪些星巴克店或者其他本地服务，用户去过之后还可以分享给好友，好友加入后双方都可以有奖励。类似公众号中的 Web App 越来越多，相信其他比较大的类微信 App 也会很快加入这场变革中。App 将会越来越不重要，因为所有的功能一个 Web App 都能搞定，并可以集成在类似微信这样强大的入口内。&lt;/p&gt;
&lt;h2 id="BAAS 颠覆原有开发方式"&gt;BAAS 颠覆原有开发方式&lt;/h2&gt;
&lt;p&gt;随着去 App 化时代的到来，开发方式也开始变化。越来越多的 BaaS（后端即服务）也开始兴起，将已经成型的后端业务做成服务提供给开发者使用，大大提升开发效率降低成本，相当于云服务的第二代。之前，因为有 AWS（亚马逊的云服务）类的云服务，开发一个产品不需要运维服务器。现在，使用国内比较主流的 LeanCloud 类的 BaaS 服务之后，则基本上不再需要后端开发。&lt;/p&gt;

&lt;p&gt;如果想做一个产品，基本上一定会有用户账号、数据存储、短信验证等功能，之前都是后端开发工程师给出接口，前端（包括 iOS、Android 和 Web 端）调用。可为什么每次都要大家自己做一套呢，这类业务模块不可以做成服务么？这样后端的数据存储、服务运维和通用类的业务都可以做成服务提供给用户，通过控制台控制，就可以做到无后端开发。&lt;/p&gt;

&lt;p&gt;很明显无后端开发方式最大的受益者，就是 Web App 的开发者。通过相关平台的 JS SDK 就可以轻松使用平台的服务，比如在 LeanCloud 上面就有数据存储、实时通信、发送短信等多种接口。开发者可以快速构建一个产品出来，很快就可以在微信上线，通过社交病毒式的传播开。&lt;/p&gt;
&lt;h2 id="结语"&gt;结语&lt;/h2&gt;
&lt;p&gt;这个时代无疑是一个创业者的时代，Web App 与 BaaS 的出现与结合，使开发方式变得越来越简单、迅速和高效。创业环境、融资环境越来越成熟，很快会有新一代的产品诞生。生产资料的改变，必将带来新的生产力提升。让我们拭目以待，接下来无数改变人们生活的公司将会不断地涌现。&lt;/p&gt;

&lt;p&gt;本文转自：&lt;a href="http://www.36kr.com/p/219195.html" rel="nofollow" target="_blank" title=""&gt;36 氪&lt;/a&gt;&lt;/p&gt;</description>
      <author>beatles</author>
      <pubDate>Mon, 02 Feb 2015 16:45:58 +0800</pubDate>
      <link>https://ruby-china.org/topics/24070</link>
      <guid>https://ruby-china.org/topics/24070</guid>
    </item>
    <item>
      <title>「轻松支付，只需几步」使用 LeanCloud 云代码接入支付宝示例</title>
      <description>&lt;p&gt;如果你的应用想接入支付宝，让用户可以在应用内部直接支付，你可以看下这篇文档和开源项目，也许会给你带来一些帮助。&lt;/p&gt;

&lt;p&gt;项目：&lt;a href="https://github.com/leancloud/cloud-code-alipay" rel="nofollow" target="_blank"&gt;https://github.com/leancloud/cloud-code-alipay&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="了解支付宝「即时到账收款」"&gt;了解支付宝「即时到账收款」&lt;/h3&gt;
&lt;p&gt;在尝试该项目之前，你需要了解支付宝「即时到账收款」这个功能：&lt;/p&gt;

&lt;p&gt;相关的介绍在这里：即时到帐收款 (&lt;a href="https://b.alipay.com/order/productDetail.htm?productId=2012111200373124" rel="nofollow" target="_blank"&gt;https://b.alipay.com/order/productDetail.htm?productId=2012111200373124&lt;/a&gt;)
确认自己有「企业支付宝账号（不含个体工商户）」
了解整个流程是什么样子的
然后就可以继续了。&lt;/p&gt;
&lt;h3 id="安装"&gt;安装&lt;/h3&gt;&lt;h3 id="下载代码："&gt;下载代码：&lt;/h3&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="nd"&gt;@github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;com&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;leancloud&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alipay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改支付宝相关的配置 cloud/config/alipay.js&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;sign_type:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;alipay_gateway:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nl"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//mapi.alipay.com/gateway.do?',&lt;/span&gt;
  &lt;span class="nl"&gt;https_verify_url:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nl"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//mapi.alipay.com/gateway.do?service=notify_verify&amp;amp;',&lt;/span&gt;
  &lt;span class="nl"&gt;partner:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="mi"&gt;2088000000000000&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;notify_url:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="c1"&gt;//xxx.avosapps.com/pay/notify',&lt;/span&gt;
  &lt;span class="nl"&gt;return_url:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="c1"&gt;//xxx.avosapps.com/pay/return'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;alipay_gateway: 支付宝网关，一般情况默认即可。&lt;/li&gt;
&lt;li&gt;https_verify_url: 支付宝回调验证 url，一般情况默认即可。&lt;/li&gt;
&lt;li&gt;partner: 合作者身份（PID），2088 开头的 16 位数字，可以在支付宝网站查找：&lt;a href="https://b.alipay.com/order/pidAndKey.htm" rel="nofollow" target="_blank"&gt;https://b.alipay.com/order/pidAndKey.htm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;key: 安全校验码，数字加字幕的字符串，可以在支付宝网站查找：&lt;a href="https://b.alipay.com/order/pidAndKey.htm" rel="nofollow" target="_blank"&gt;https://b.alipay.com/order/pidAndKey.htm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;notify_url: 支付宝异步通知 url，二级域名根据 LeanCloud 云代码配置而定。&lt;/li&gt;
&lt;li&gt;return_url: 支付宝同步通知 url，二级域名根据 LeanCloud 云代码配置而定。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="部署"&gt;部署&lt;/h3&gt;
&lt;p&gt;配置 LeanCloud appId 和 appKey&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;avoscloud&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;appId&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;切换目标应用&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;avoscloud&lt;/span&gt; &lt;span class="n"&gt;checkou&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;部署应用到测试环境和生产环境&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;avoscloud&lt;/span&gt; &lt;span class="n"&gt;deploy&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;avoslcoud&lt;/span&gt; &lt;span class="n"&gt;publish&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提示 : 过程中可能会提示输入 masterKey。&lt;/p&gt;

&lt;p&gt;如果没有错误，请打开浏览器，根据自己的二级域名键入网址：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="c1"&gt;//&amp;lt;yourPath&amp;gt;.avosapps.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果看到「支付宝即时到账交易接口」的页面，恭喜你，部署成功！&lt;/p&gt;
&lt;h3 id="感受一下"&gt;感受一下&lt;/h3&gt;
&lt;p&gt;1.在「支付宝即时到账交易接口」的测试页面，输入相关信息。注意：「卖家支付宝账户」需要和「partner」对应；金额可以输入 0.01 (表示支付 1 分钱) 来进行尝试。输入完成后点击确认。
2.你将看到跳转到支付宝页面，输入自己的支付宝账号和支付密码等完成支付。
3.支付完成后会跳转回我们自己的应用页面，并显示 验证结果：true。支付流程结束。当然，你的 1 分钱也转到了对应的卖家账户 ;)&lt;/p&gt;
&lt;h3 id="开发相关"&gt;开发相关&lt;/h3&gt;&lt;h3 id="文件说明"&gt;文件说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;cloud/app.js: 支付宝相关请求路由。&lt;/li&gt;
&lt;li&gt;cloud/alipay.js: 支付宝相关签名验证，生成跳转等逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="路由信息"&gt;路由信息&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;GET /: 静态首页 public/index.html。&lt;/li&gt;
&lt;li&gt;POST /pay: 接受表单信息、签名，并准备跳转到支付宝。&lt;/li&gt;
&lt;li&gt;GET /pay/return: 等待支付宝同步回调，并验证调用方是否真正来自支付宝。&lt;/li&gt;
&lt;li&gt;POST /pay/notify: 等待支付宝异步回调，并验证调用方是否真正来自支付宝。&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>beatles</author>
      <pubDate>Wed, 07 Jan 2015 16:33:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/23594</link>
      <guid>https://ruby-china.org/topics/23594</guid>
    </item>
    <item>
      <title>让你的 Android 应用拥有微信一样的实时沟通体验</title>
      <description>&lt;p&gt;[LeanMessage][2] 移动开发 SDK 是由 [LeanCloud][2] 提供的，专为 iOS、Android 和 WindowsPhone® 等客户端程序提供应用内聊天的 API 和服务，并且也提供了 JavaScript API，方便开发者打通网页和客户端应用，给最终用户提供统一的使用体验。使用 LeanMessage API，您可以极快地以最少工作量让您的移动应用支持实时聊天，得到一种如微信一般的沟通体验。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;开始之前
出于本文的目的，我假设您已经非常熟悉使用 JSON、Android 和 Eclipse 进行移动应用编程的基本概念。在您继续阅读本文之前，请访问 leancloud.cn 并创建您的应用程序。只需遵循注册页面中的简单指令即可。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本文介绍了包含单聊、群聊、历史记录和应用鉴权的核心 API 类。您将学习如何简单进行用户间一对一单聊，以及如何创建群组让多用户进行群聊，还有如何通过签名来对聊天通道进行控制，以保护应用和用户的隐私。示例均构建于 LeanMessage SDK for Android 之上（请参阅 [Android 开发指南][4]）。&lt;/p&gt;
&lt;h2 id="基本概念和类"&gt;基本概念和类&lt;/h2&gt;&lt;h3 id="聊天参与者 Peer"&gt;聊天参与者 Peer&lt;/h3&gt;
&lt;p&gt;在 LeanMessage 实时消息世界中，每一个参与者（一般而言是「人」）都是一个 Peer。Peer 拥有一个在应用内唯一标识自己的 ID（称为 PeerID，字符串类型，长度不超过 50 字节，具体数值由应用自身确定），系统中的每一条消息都来自于一个 Peer，发送到一个或多个 Peer。并且，LeanMessage 的消息服务允许一个 Peer 在多个不同设备上登录，也允许一个设备上同时登录多个 Peer，究竟该如何使用，由应用根据使用场景自己选择。&lt;/p&gt;

&lt;p&gt;这里要注意的是，PeerID 是由应用开发者自己賦值的，LeanMessage 本身并没有任何强制要求，所以：&lt;/p&gt;

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

&lt;p&gt;为了防止骚扰，一个 Peer 需要先关注（watch）了对方才能给对方发送消息；因为 LeanMessage 提供了更细粒度的权限控制，应用开发者可以在关注（watch）动作上增加签名来保证安全性。这一点后面会进行详细说明。&lt;/p&gt;
&lt;h3 id="实时消息 AVMessage"&gt;实时消息 AVMessage&lt;/h3&gt;
&lt;p&gt;在 LeanMessage 中所有的消息都是 AVMessage 的实例，AVMessage 只支持文本，并且长度不能超过 5KB。消息分为暂态（transient）和持久消息两种类型。所有持久消息都会在 LeanMessage 云端保存，所以用户离线之后也可以得到通知和接收，而暂态消息并不会离线保存，适合开发者用来进行协议控制。&lt;/p&gt;

&lt;p&gt;AVMessage 的定义如清单 1 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Parcelable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toPeerIds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 消息接收方的 PeerID，支持一个或多个&lt;/span&gt;

     &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 消息所属群组的ID，对于普通一对一聊天消息而言，此值为空&lt;/span&gt;
     &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 消息体&lt;/span&gt;
     &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 消息发送时间戳&lt;/span&gt;
     &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isTransient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 是否是暂态消息&lt;/span&gt;
     &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fromPeerId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 消息发送方的 PeerID&lt;/span&gt;

     &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AVMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
     &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AVMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AVMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toPeerIds&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isTransient&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AVMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isTransient&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;LeanMessage 为所有历史消息都提供了存储和查询的功能，存储时间则根据开发者的类型有所不同。&lt;/p&gt;
&lt;h3 id="聊天会话 Session"&gt;聊天会话 Session&lt;/h3&gt;
&lt;p&gt;每一个 Peer 通过开启（open）一个会话（Session）而加入实时消息服务，Peer 可以在一个会话中关注（watch）一个或多个 Peer，当被关注者上下线时，会收到通知。Peer 在开启会话后只能向自己关注的其他 Peers 发送消息，但可以收到任何 Peer 发来的消息，也就是说单向关注时，消息可以顺利地由关注者发往被关注者。&lt;/p&gt;

&lt;p&gt;Session 有如下几种状态：&lt;/p&gt;

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

&lt;p&gt;Session 上可以进行的操作有：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open 以一个 Peer ID 打开 Session&lt;/li&gt;
&lt;li&gt;watch 关注一组 Peer ID，关注后可以收到这个 Peer 的上下线通知，发送消息&lt;/li&gt;
&lt;li&gt;unwatch 取消对一组 Peer ID 的关注&lt;/li&gt;
&lt;li&gt;sendMessage 给一组 Peer ID 发送消息&lt;/li&gt;
&lt;li&gt;queryOnlinePeer 查找当前在线的 Peers&lt;/li&gt;
&lt;li&gt;getHistoryMessageQuery 查找历史消息&lt;/li&gt;
&lt;li&gt;setSignatureFactory 设置签名类（为了保证安全性，后面会讲述）&lt;/li&gt;
&lt;li&gt;close 注销服务，关闭 Session&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一对一的文本聊天"&gt;一对一的文本聊天&lt;/h2&gt;
&lt;p&gt;明白了这三个概念之后，我们就可以开始进入实际聊天环节了。&lt;/p&gt;

&lt;p&gt;首先我们需要在 application 的 onCreate 函数中进行 LeanCloud 最基本的初始化：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="nc"&gt;AVOSCloud&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pleaseReplaceWithYourAppId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pleaseReplaceWithYourAppKey"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来我们来看一下怎么样进行一对一的基本聊天。首先，我们需要开启一个会话（Session），示例代码如清单 2 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SessionManager&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//获取SessionManager实例，以便于后续的操作。这里的 selfId 可以是用户的实际 id，也可以是其他唯一的字符串，譬如「Tom」。&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;watchedIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;watchedIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//打开Session，同时关注一些 PeerID。此时没有关注对象&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意！
一般而言，会话的开启是在用户登录之后的 RootActivity 中进行的。对于支持匿名聊天的应用，也可以在 Application 启动的时候进行。千万不要在一个临时或短命的 Activity 中开启聊天会话。上面代码中 SessionManager 也是 Session 的子类，所以可以直接调用 Session 的方法。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;接下来，我们开始跟「Bob」这个用户进行聊天。为了给他发送消息，我们先要关注（watch）他，代码如下：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watchPeers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后我们给「Bob」发送一条消息：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"嗨，你好，我是 Tom"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好了，这样一条消息就发送过去了。但是问题来了，对于「Bob」而言，他怎么才能收到别人发给他的消息呢？&lt;/p&gt;

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

&lt;span class="cm"&gt;/**
 * 在 session 暂停时调用，一般都是由网络连接丢失导致的隐性调用
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onSessionPaused&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Session 恢复时，一般都是网络连接恢复以后的
 * 这个时候你可以处理一些由于网络异常导致的失败消息
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onSessionResumed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 从某个Peer接收到消息时，会收到一次调用
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 服务器反馈消息已经成功发送时，会收到调用
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessageSent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 在消息发送失败时，产生的调用 在这里你可以保存一下发送失败的消息以便未来重发
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessageFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 当关注的一些peers上线时，产生的调用
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onStatusOnline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 当关注的一些peers下线时，产生的调用
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onStatusOffline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 当与服务器发生交互的过程中的任何错误，都在这里被返回
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从上面接口的定义中，我们可以看到，要接收到别人发过来的消息，只需要响应 onMessage() 方法即可。代码示例如清单 4 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomeMsgReceiver&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AVMessageReceiver&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;avMsg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"onMessage "&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;avMsg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// 进行上层逻辑处理，譬如 UI 展示，或者消息提醒。&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 在 AndroidManifest.xml 文件中声明这一 BoardcastReceiver。&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt; &lt;span class="nl"&gt;android:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;".receiver.CustomeMsgReceiver"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nl"&gt;android:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.BOOT_COMPLETED"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nl"&gt;android:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"com.avoscloud.session.action"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="支持富媒体的聊天消息"&gt;支持富媒体的聊天消息&lt;/h2&gt;
&lt;p&gt;上面的代码演示了如何发送文本信息，但是现在的交互方式已经越来越多样化，图片、语音、视频已是非常普遍的媒体类型。而从 AVMessage 的定义来看，只支持不超过 5KB 大小的文本，那么 LeanMessage 又如何能支持富媒体的聊天消息呢？&lt;/p&gt;

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

&lt;p&gt;对于图片、语音、视频这类较大的非结构化数据，存储到云端文件系统之后，在消息中发送 url 已是业界惯例，并且 LeanMessage 中 AVMessage 类的定义并没有规定消息体是什么类型，所以我们可以充分利用这一扩展空间，结合 AVFile 来发送、接收富媒体的聊天消息。实现方法如清单 5 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AVFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AVFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withAbsoluteLocalPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test.jpg"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getExternalStorageDirectory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/test.jpg"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;saveInBackground&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SaveCallback&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AVException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// error&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// construct message body under json format.&lt;/span&gt;
            &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"context"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"嗨，你好，我是 Tom"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attachment"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUrl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

            &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJSONString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新版本的 LeanMessage SDK 会支持富媒体消息，避免让每个开发者都重复做类似的工作。&lt;/p&gt;
&lt;h2 id="群组聊天"&gt;群组聊天&lt;/h2&gt;
&lt;p&gt;在聊天的需求里，还有一个很重要的场景，就是群组聊天。从前面 AVMessage 的定义我们可以猜到，LeanMessage 应该是支持群聊的，那实际上该如何实现呢？下面我们一步一步来尝试一下。&lt;/p&gt;
&lt;h3 id="基本概念"&gt;基本概念&lt;/h3&gt;
&lt;p&gt;与普通的单聊相比，群聊增加了如下两个基本概念：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;群组 AVGroup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AVGroup 代表一个聊天群组，可以对应到实际的多人聊天、聊天群、聊天室等，每个 AVGroup 有一个唯一的 ID（groupID，由 LeanMessage 云端分配），其定义如清单 6 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AVGroup&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;roomId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个 Peer 加入群后向群发送的消息可以被所有群成员收到。当有新成员加入或者既有成员退出时，所有群成员都会得到通知。AVGroup 上可以进行的操作有：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;kickMember&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;inviteMember&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getGroupId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getSelfId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;AVHistoryMessageQuery&lt;/span&gt; &lt;span class="nf"&gt;getHistoryMessageQuery&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;群组消息接受器 AVGroupMessageReceiver&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;与 AVMessageReceiver 类似，AVGroupMessageReceiver 主要用来处理群组操作的结果。其详细定义如清单 7 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 在加入聊天室成功后被调用 如果join时，没有带groupId,您可以在返回的group中间获取groupId&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onJoined&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//当你被别人邀请进入某个聊天室以后&lt;/span&gt;
&lt;span class="c1"&gt;//  @param group&lt;/span&gt;
&lt;span class="c1"&gt;//  @param byPeerId 这个人邀请了你&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onInvited&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;byPeerId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 当你被别人踢出聊天室以后&lt;/span&gt;
&lt;span class="c1"&gt;// @param group&lt;/span&gt;
&lt;span class="c1"&gt;// @param byPeerId 是他踢了你&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onKicked&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;byPeerId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 处理消息发送成功事件&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessageSent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 用来处理消息发送失败事件.可以缓存起来，事后重发&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessageFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 收到消息以后被调用，一般通过这个接口来处理和接受来自Group的消息&lt;/span&gt;
&lt;span class="c1"&gt;// @param context&lt;/span&gt;
&lt;span class="c1"&gt;// @param group&lt;/span&gt;
&lt;span class="c1"&gt;// @param message&lt;/span&gt;
&lt;span class="c1"&gt;// @param fromPeerId 发消息者&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 处理退出成功事件&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onQuit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 处理Group操作被拒绝的时间&lt;/span&gt;
&lt;span class="c1"&gt;// @param context&lt;/span&gt;
&lt;span class="c1"&gt;// @param group&lt;/span&gt;
&lt;span class="c1"&gt;// @param op　这里可能存在的操作有 "join","invite","kick"&lt;/span&gt;
&lt;span class="c1"&gt;// @param targetIds&lt;/span&gt;
&lt;span class="c1"&gt;//            一般来说是指被操作的对象，在join操作中间就是指groupId本身,&lt;/span&gt;
&lt;span class="c1"&gt;//            invite和kick中则指被邀请或者被踢除的peerIds&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onReject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;targetIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 处理新用户加入事件&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMemberJoin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;joinedPeerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 处理用户退出事件&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMemberLeft&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;leftPeerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 处理所有Group相关的异常&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="加入聊天室"&gt;加入聊天室&lt;/h3&gt;
&lt;p&gt;由于整个实时通信功能都是建立在 Session 的基础上，所以我们要加入一个聊天室也需要建立在一个已经打开的 Session 上。已经打开一个 Session 以后，可以通过以下操作来加入一个 Group：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getGroup&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;&lt;span class="c1"&gt;//准备新建一个聊天室&lt;/span&gt;
&lt;span class="c1"&gt;//Group group = SessionManager.getInstance(selfId).getGroup(groupId);　加入一个已经存在的聊天室&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// LeanMessage 云端会判断 groupId 是否存在，如果不存在就新建一个 Group，否则加入已有 Group&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加入成功之后 AVGroupMessageReceiver 子类中的 onJoined 方法就会被调用。&lt;/p&gt;
&lt;h3 id="往聊天室发送消息"&gt;往聊天室发送消息&lt;/h3&gt;
&lt;p&gt;发送消息非常简单，通过如下代码就可以向特定聊天室发送消息了：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getGroup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AVGroupMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发送成功之后，AVGroupMessageReceiver 子类中的 onMessageSent 方法会被调用，反之则 onMessageFailure 方法会被调用。&lt;/p&gt;
&lt;h3 id="接收聊天室消息"&gt;接收聊天室消息&lt;/h3&gt;
&lt;p&gt;接收一个聊天室的消息，与接收单聊的消息一样，需要开发者实现 AVGroupMessageReceiver 接口，并在 AndroidManifest.xml 中注册即可，如代码清单 8 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomeGroupMsgReceiver&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="n"&gt;avMsg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"onMessage "&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;avMsg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// 进行上层逻辑处理，譬如 UI 展示，或者消息提醒。&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 在 AndroidManifest.xml 文件中声明这一 BoardcastReceiver。&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt; &lt;span class="nl"&gt;android:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;".receiver.CustomeGroupMsgReceiver"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nl"&gt;android:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.BOOT_COMPLETED"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nl"&gt;android:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"com.avoscloud.session.action"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查询聊天室成员"&gt;查询聊天室成员&lt;/h3&gt;
&lt;p&gt;在加入一个聊天室之后，我们第一步就是看看有哪些人在这个群组里面。LeanMessage 和 LeanStorage 是结合在一起的，通过使用 LeanStorage 的数据存储功能，来保存一个聊天室的基本信息（表名：AVOSRealtimeGroups），在 LeanStorage 应用管理平台的数据中心，我们可以看到 AVOSRealtimeGroups 的所有字段。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[LeanStorage][5] 的数据中心 
[LeanStorage][5] 也是 LeanCloud 平台的核心服务之一，提供了应用内数据和文件数据的存储功能。对于应用内数据，LeanStorage 支持 schema free 的存储，开发者不需要事先定义数据的模式，只要符合 JSON 格式的 Object 都可以自由存储到 LeanStorage 云端。同时，LeanStorage 也提供一个 Web 版的数据管理界面，可以非常方便地增、删、改、查任何数据。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当然，在我们知道一个聊天室的 groupId 的时候，也可以在代码中，通过 AVObject 的 fetch 接口来查看这个聊天室的组员情况，代码如清单 9 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AVObject&lt;/span&gt; &lt;span class="n"&gt;groupObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AVObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createWithoutData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AVOSRealtimeGroups"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;groupObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;&lt;span class="c1"&gt;//如果您在UI进程中，请使用异步方法调用&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;groupMembers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groupObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"m"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;谨防系统线程阻塞！
回想一下，在移动应用程序中，长时间的操作（如网络、文件或长的计算）不应该在主系统线程上完成。相反，应在一个单独的工作线程中执行它们。阻塞系统线程会对应用程序的用户界面的响应能力产生负面影响，有可能导致强行关闭您的应用程序。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="成员管理"&gt;成员管理&lt;/h3&gt;
&lt;p&gt;在查询到聊天室成员之后，可以让用户邀请一些自己的朋友加入，作为管理员也可以剔除一些「可怕」的成员。代码如清单 10 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getGroup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toInvite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"peerId1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"peerId2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"peerId3"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inviteMember&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toInvite&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toKickOff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"badBoy1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"badBoy2"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kickMembers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toKickOff&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;邀请成功以后，通知的流程是这样的：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="n"&gt;操作者&lt;/span&gt;&lt;span class="err"&gt;（&lt;/span&gt;&lt;span class="n"&gt;管理员&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;                           &lt;span class="n"&gt;被邀请者&lt;/span&gt;                        &lt;span class="n"&gt;其他人&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;发出请求&lt;/span&gt; &lt;span class="n"&gt;inviteMember&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onInvited&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;                                      &lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onJoined&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onMemberJoin&lt;/span&gt;                          &lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onMemberJoin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相应地，踢人的流程如下：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="n"&gt;操作者&lt;/span&gt;&lt;span class="err"&gt;（&lt;/span&gt;&lt;span class="n"&gt;管理员&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;                           &lt;span class="n"&gt;被踢者&lt;/span&gt;                         &lt;span class="n"&gt;其他人&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;发出请求&lt;/span&gt; &lt;span class="n"&gt;kickMember&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onKicked&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;                                    &lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onQuit&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onMemberLeft&lt;/span&gt;                         &lt;span class="nc"&gt;AVGroupMessageReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onMemberLeft&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查询历史消息"&gt;查询历史消息&lt;/h3&gt;
&lt;p&gt;LeanMessage 会将非暂态消息自动保存在云端，之后开发者可以通过 AVHistoryMessageQuery 这个对象来进行查询。AVHistoryMessageQuery 定义如清单 11 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AVHistoryMessageQuery&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;convid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 设置查询返回集合的大小
     * 默认100，最大1000
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setLimit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 设定聊天的发起人是谁
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 设置查询从哪个时间开始的聊天记录
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setTimestamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 指定聊天记录查询条件中，聊天发送的对象条件
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setPeerIds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peerIds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 同步方法查询聊天记录
     * 请确保在一个异步方法中调用此方法，否则会出现UI线程中的网络请求而导致的UI卡死
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AVHistoryMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;AVException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 异步方法查询聊天记录
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;findInBackground&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HistoryMessageCallback&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 此接口为异步查询聊天记录的回调类
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;HistoryMessageCallback&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过 AVHistoryMessageQuery 查询得到的结果是 AVHistoryMessage，该类的定义如清单 12 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AVHistoryMessage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AVMessage&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * 查看是否属于聊天室聊天记录
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isRoom&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * 查看聊天记录所在的conversation Id，对于 Group 来说等于 GroupID，对于单聊来说，是内部生成的一个值。
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getConvid&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;聊天记录的查询的基本方法跟 AVQuery 类似但是略有不同。针对 Session 的聊天记录和聊天室 Group 的聊天记录查询略有不同，但是基本流程是一样（代码清单 12）:&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;selfId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Tom"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;SessionManager&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;AVHistroyMessageQuery&lt;/span&gt; &lt;span class="n"&gt;sessionHistoryQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHistroyMessageQuery&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;sessionHistoryQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLimit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;//设置查询结果大小&lt;/span&gt;
&lt;span class="n"&gt;sessionHistoryQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPeerIds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 设置单聊的参与方，多个参与者之间是「与」的关系&lt;/span&gt;
&lt;span class="n"&gt;sessionHistoryQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTimestamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1413184345686&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;//查询时间片1413184345686以前的聊天记录&lt;/span&gt;
&lt;span class="n"&gt;sessionHistoryQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findInBackground&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HistoryMessageCallback&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nd"&gt;@Override&lt;/span&gt;
      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AVHistoryMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AVException&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;&lt;span class="c1"&gt;//查询session里的聊天记录&lt;/span&gt;

&lt;span class="nc"&gt;Group&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGroup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"140a534fd092809500e6d651e73400c7"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;AVHistroyMessageQuery&lt;/span&gt; &lt;span class="n"&gt;groupHistoryQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHistoryMessageQuery&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;&lt;span class="c1"&gt;//获取AVHistoryMessageQuery对象来查询聊天室的聊天记录&lt;/span&gt;
&lt;span class="n"&gt;groupHistoryQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findInBackground&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HistoryMessageCallback&lt;/span&gt;&lt;span class="o"&gt;(){&lt;/span&gt;
     &lt;span class="nd"&gt;@Override&lt;/span&gt;
     &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AVHistoryMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;AVException&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AVHistoryMessage&lt;/span&gt; &lt;span class="nl"&gt;msg:&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
          &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
     &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面第一个查询会拿到「Tom」和「Bob」在特定时间点以前的 100 条聊天记录；第二个查询会拿到特定聊天室的所有聊天记录（如果总数不超过 1000 条的话）。&lt;/p&gt;
&lt;h3 id="一览查看所有聊天室"&gt;一览查看所有聊天室&lt;/h3&gt;
&lt;p&gt;查看所有聊天室的方法和查看单个聊天室成员的方法类似，都是直接通过 AVQuery 或者 AVObject 来遍历 AVOSRealtimeGroups 表实现的，这里不再赘述。&lt;/p&gt;
&lt;h2 id="聊天记录和安全"&gt;聊天记录和安全&lt;/h2&gt;
&lt;p&gt;前面实现了单聊、群聊、富媒体聊天诸多功能，但是开发者可能已经发现了，这都是直接调用 LeanMessage SDK 来实现的，对于我们开发者来说，能控制的东西很少，在安全性上会存在一些担心。譬如：万一不怀好意的人破解了我的 appId 和 appKey，是不是就可以在我的聊天社区里面为所欲为？&lt;/p&gt;

&lt;p&gt;为了满足开发者对权限和认证的要求，LeanMessage 还设计了操作签名的机制。我们可以在 LeanCloud 应用控制台、设置、应用选项中强制启用签名（强烈推荐这样做）。启用后，所有的 Session open 和 watch 行为都需要验证签名，这样开发者就可以对用户登录以及他可以关注哪些人，进而可以给哪些人发消息进行充分的控制。&lt;/p&gt;

&lt;p&gt;签名采用 Hmac-sha1 算法，输出字节流的十六进制字符串 (hex dump)，签名的消息格式如下：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nl"&gt;app_id:peer_id:watch_peer_ids:timestamp:&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app_id 是你的应用 ID&lt;/li&gt;
&lt;li&gt;peer_id 是打开此 Session 的 Peer ID&lt;/li&gt;
&lt;li&gt;watch_peer_ids 是 open 或 watch 请求中关注的 peer ids，升序排序后以：分隔&lt;/li&gt;
&lt;li&gt;timestamp 是当前的 UTC 时间距离 unix epoch 的秒数&lt;/li&gt;
&lt;li&gt;nonce 为随机字符串&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在群组操作中，LeanMessage 对加群、邀请和踢出群这三个动作也允许加入签名，它的签名格式是：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nl"&gt;app_id:peer_id:group_id:group_peer_ids:timestamp:nonce:&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app_id, peer_id, timestamp 和 nonce 同上&lt;/li&gt;
&lt;li&gt;group_id 是此次行为关联的群组 ID，对于创建群尚没有 id 的情况，group_id 是空字符串&lt;/li&gt;
&lt;li&gt;group_peer_ids 是：分隔的升序排序的 peer id，即邀请和踢出的 peer_id；对加入群的情况，这里是空字符串&lt;/li&gt;
&lt;li&gt;action 是此次行为的动作，三种行为分别对应常量 join, invite 和 kick&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;签名的 key 是应用的 master key。开发者可以实现自己的 SignatureFactory，调用远程的服务器的签名接口获得签名。如果没有自己的服务器，可以直接在 LeanCloud 的云代码上通过 Web Hosting 动态接口实现自己的签名接口。在移动应用中直接做签名是非常危险的，它可能导致你的 master key 泄漏。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;LeanCloud 的 appKey 分类
在 LeanCloud 平台上申请了应用之后，LeanCloud 会分配给我们两个 key：一个 appKey，一个 master Key。其中 appKey 可以执行一些普通的操作，并且受到 LeanCloud 平台安全设置的限制，类似于操作系统中的普通 User 账号，所以可以直接用在客户端；master Key 则拥有所有权限，类似于操作系统中的 Root/Administrator 账号，所以请妥善保管。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好，有了签名机制之后，我们究竟该如何使用呢？我们只需要实现自己的 SignatureFactory，然后在开启 session 的时候，把这个 signatureFactory 传进去即可。示例代码如清单 13 所示：&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Signature 定义如下，由 LeanMessage 提供&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Signature&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;signedPeerIds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// getter / setter for properties&lt;/span&gt;
    &lt;span class="o"&gt;......&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// customise signature factory，由开发者实现&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;MySignatureFactory&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SignatureFactory&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Signature&lt;/span&gt; &lt;span class="nf"&gt;createSignature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;watchIds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// call remote server for correct signature.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="nd"&gt;@override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Signature&lt;/span&gt; &lt;span class="nf"&gt;createGroupSignature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;targetPeerIds&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// call remote server for correct group signature.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// open session with signature factory.&lt;/span&gt;
&lt;span class="nc"&gt;SignatureFactory&lt;/span&gt; &lt;span class="n"&gt;signatureFacatory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MySignatureFactory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;SessionManager&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSignatureFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signatureFactory&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selfId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;设定了 SignatureFactory 之后，对于需要鉴权的操作，LeanMessage SDK 与服务器端通讯的时候都会带上应用自己生成的 Signature 信息，这样 LeanMessage 服务器端就会使用 app 的 masterKey 来验证信息的有效性，保证聊天渠道的安全。&lt;/p&gt;
&lt;h2 id="结束语"&gt;结束语&lt;/h2&gt;
&lt;p&gt;LeanMessage 是一个非常稳定可靠的聊天服务平台，提供的功能也足以满足我们应用开发者的需求。这里我通过介绍 LeanMessage API 来实现应用内的单聊、群聊、富媒体消息等基本功能，但是 LeanMessage 还支持更多高阶功能，譬如 Super Peer、敏感词过滤、消息实时监听等等，有兴趣的朋友可以继续探索。&lt;/p&gt;

&lt;p&gt;[1]: &lt;a href="https://leancloud.cn/features/message.html" rel="nofollow" target="_blank"&gt;https://leancloud.cn/features/message.html&lt;/a&gt;
  [2]: &lt;a href="https://leancloud.cn" rel="nofollow" target="_blank"&gt;https://leancloud.cn&lt;/a&gt;
  [3]: &lt;a href="https://leancloud.cn/docs/android_guide.html" rel="nofollow" target="_blank"&gt;https://leancloud.cn/docs/android_guide.html&lt;/a&gt;
  [4]: &lt;a href="https://cn.avoscloud.com/docs/realtime.html" rel="nofollow" target="_blank"&gt;https://cn.avoscloud.com/docs/realtime.html&lt;/a&gt;
  [5]: &lt;a href="https://cn.avoscloud.com/features/storage.html" rel="nofollow" target="_blank"&gt;https://cn.avoscloud.com/features/storage.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>beatles</author>
      <pubDate>Fri, 21 Nov 2014 17:40:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/22806</link>
      <guid>https://ruby-china.org/topics/22806</guid>
    </item>
  </channel>
</rss>
