通讯 App 的市场正在突飞猛涨,人们正在寻找标准文本通讯的替代品。既然机会难得,为何不创建一个通讯 App 呢?本文将带你使用 Android 完成 WhatsApp 的框架克隆(我们将其命名为 KiiChat),利用 Kii Cloud 移动后端服务器可更容易、简便地创建这类App。
如果你是外星人,你大概不知道 WhatsApp(刚被 Facebook 收购)是当下最流行的跨平台移动通讯 App。你可以用它与世界各地的用户交换消息(不需要付短信费用),创建群组,无限地发送图片、视频和音频消息。
开发 Skype、WhatsApp 或者 LINE 等即时通讯 App 时,服务器端功能的开发是必不可少的,并且到现在为止,个人开发者还不得不实现这一切(这是一个大的意外)。幸运的是,使用诸如 Kii 等后端服务可以在几个小时内快速开发一个非常实用的 App。在这篇文章的最后,您将有机会下载完整的示例项目。该 App 通过我们的 Android SDK可以做以下事情:
1. 支持用户群组(以及群组级 Object Bucket)
2. 修改数据存储的默认权限(通过在 Object Bucket 中新增 ACL)
3. 给已订阅了 Topic 的用户发送消息(通过 Push to User 功能)
4. 有新消息时,通知 App(通过 Push to App 功能)
5. 查询 Cloud 上存储的 Object(消息、图片等等)
6. 上传图片(通过上传 Object Body)
7. 下载图片(通过下载 Object Body)
8. 使用 Facebook 账号登录
创建 App 后端
在 Kii 的开发者平台上注册你的 App 是使用 Android SDK 的先决条件。注册完成后,你的 App 会有一个相关联的 App Key 和 App ID,你需要借助它们初始化 Kii SDK。
注册 App
1. 登录开发者平台:https://developer.kii.com
2. 点击 “Create App” 按钮。这将弹出 “Create An App” 菜单。
3. 输入 App 的名称,选择期望的服务器位置和所需的手机应用平台(这个项目中,你需要选择 Android),然后点击 “Create” 按钮。
这些就是在使用 Kii Cloud 时用来识别 App 的认证凭证。
复制这些凭证,并将它们添加到项目的常数文件 ApplicationConst.java 中。在下一步:“初始化后端”中我们将用到它们。
初始化后端
现在是时候将你实际的 App 和 在上一步中创建的 App 后端链接起来了。简单地说,你需要将我们的 SDK 添加到你的 Android 项目中,并使用 App ID 和 App Key 初始化 Kii。具体步骤如下。
配置项目
按照如下步骤将 Kii Cloud SDK 集成到 App 项目中:
1. 把最新的 Kii Library(KiiCloudStorageSDK-xxx.jar)拷贝到项目的 libs 目录下 {Android App 项目的根目录}/libs
请确保它作为一个引用 Library 被包含在项目的属性中。
2. 在 App 的 AndroidManifest.xml 中添加如下权限:
1
2
3
4
5
6
7
8
|
&
lt
;
uses
-
permission
android
:
name
=
"android.permission.INTERNET"
/
&
gt
;
&
lt
;
uses
-
permission
android
:
name
=
"android.permission.WRITE_EXTERNAL_STORAGE"
/
&
gt
;
&
lt
;
uses
-
permission
android
:
name
=
"android.permission.GET_ACCOUNTS"
/
&
gt
;
&
lt
;
uses
-
permission
android
:
name
=
"android.permission.WAKE_LOCK"
/
&
gt
;
&
lt
;
uses
-
permission
android
:
name
=
"android.permission.READ_PHONE_STATE"
/
&
gt
;
&
lt
;
uses
-
permission
android
:
name
=
"com.google.android.c2dm.permission.RECEIVE"
/
&
gt
;
&
lt
;
uses
-
permission
android
:
name
=
"com.kii.sample.chat.permission.C2D_MESSAGE"
/
&
gt
;
&
lt
;
permission
android
:
name
=
"com.kii.sample.chat.permission.C2D_MESSAGE"
android
:
protectionLevel
=
"signature"
/
&
gt
;
|
C2DM 权限是 Push 通知的必备条件。
3. 在 KiiChatApplication 类中添加如下代码初始化 SDK:
1
2
3
4
5
6
7
|
import
com
.
kii
.
cloud
.
storage
.
*
;
// Configures the SDK to use the specified Application ID and Key.
// It must be called prior to any API calls.
// It is ok to call this method multiple times
Kii
.
initialize
(
ApplicationConst
.
APP_ID
,
ApplicationConst
.
APP_KEY
,
Kii
.
Site
.
JP
)
;
KiiAnalytics
.
initialize
(
context
,
ApplicationConst
.
APP_ID
,
ApplicationConst
.
APP_KEY
,
KiiAnalytics
.
Site
.
JP
)
;
|
在该方法的占位参数中插入你自己 App 的凭证(即,APP_ID 和 APP_KEY)。还需要传递你之前所选的服务器位置(Site.US、Site.JP 或者 Site.CN)给该方法。
对象模型
通讯 App 的对象模型:
IUser:用户基本信息(用户名、E-Mail 和 URI)的接口
KiiObjectWrapper:一个封装,用于在 KiiObject 上快速获取和设置 Key-Value,以及获取低级别的 Object 信息(ID、创建时间和修改时间)
ChatUser:ChatUser Object 的封装,继承自 KiiObjectWrapper 并实现了 IUser 接口。是对通讯系统中用户的抽象。ChatUser Object 是存储在一个 App 级的 Bucket 中的。
ChatFriend:ChatFriend Object 的封装,继承自 KiiObjectWrapper 并实现了 IUser 接口。与ChatUser 相似,不同的是 ChatFriend Object 是存储在一个用户级的 Bucket 中的(因为只有该用户可以查看他的朋友)。
ChatMessage:ChatMessage Object 的封装,继承自 KiiObjectWrapper(消息本身就是一个KiiObject)
ChatRoom:对聊天室中 KiiGroup 用户和相关消息列表的抽象。
ChatStamp:ChatStamp Object 的封装,继承自 KiiObjectWrapper(Stamp 本身就是一个KiiObject)。Stamp 是可以在会话中发送的图片。
我们在介绍 App 功能时,会详细介绍每个模型的类(详见下文)。
加载用户
MainActivity
在项目的 AndroidManifest.xml 文件中,你可以看到主界面是com.kii.sample.chat.ui.MainActivity。如果已经存储了某用户的认证令牌(Kii 支持使用令牌登录,所以用户下次启动 App 时不需要再次登录),该 Activity 将会使用令牌登录并直接跳转到聊天主界面(ChatMainActivity):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
String
token
=
PreferencesManager
.
getStoredAccessToken
(
)
;
if
(
!
TextUtils
.
isEmpty
(
token
)
)
{
KiiUser
.
loginWithToken
(
new
KiiUserCallBack
(
)
{
@
Override
public
void
onLoginCompleted
(
int
token
,
KiiUser
user
,
Exception
e
)
{
if
(
e
==
null
)
{
ChatRoom
.
ensureSubscribedBucket
(
user
)
;
Intent
intent
=
new
Intent
(
MainActivity
.
this
,
ChatMainActivity
.
class
)
;
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
)
;
startActivity
(
intent
)
;
}
else
{
PreferencesManager
.
setStoredAccessToken
(
""
)
;
Intent
intent
=
new
Intent
(
MainActivity
.
this
,
SigninActivity
.
class
)
;
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
)
;
startActivity
(
intent
)
;
}
}
}
,
token
)
;
}
else
{
Intent
intent
=
new
Intent
(
MainActivity
.
this
,
SigninActivity
.
class
)
;
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
)
;
startActivity
(
intent
)
;
}
|
请注意,我们使用了 KiiUserCallback 作为登录方法(Kii Cloud 支持使用异步非阻塞方式调用 API,所以在等待后台返回结果期间你可以继续做别的事情)。此例中,如果没有存储令牌(或是令牌认证失败),将跳转到 SigninActivity,在这个 Activity 中你可以使用标准登录/注册界面(“Sign-in with your e-mail”)或是使用 Facebook 账号登录。
如果点选了屏幕上方的 “Remember Me”,Kii 用户令牌将被存储并用于之后的登录(之前已解释过)。当点击 “Create new account” 你会看到一个典型的注册界面:
用户的注册和登录
Kii 提供了即开即用的用户注册,同时可以验证用户的 Email/短信。但是,最重要的是 用于注册或登录的代码十分简单。如下是 SignupTask 中最重要的部分(在 SignupDialogFragment 中定义的):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@
Override
protected
Boolean
doInBackground
(
Void
.
.
.
params
)
{
try
{
KiiUser
.
Builder
builder
=
KiiUser
.
builderWithEmail
(
email
)
;
KiiUser
kiiUser
=
builder
.
build
(
)
;
kiiUser
.
setDisplayname
(
username
)
;
kiiUser
.
register
(
password
)
;
Logger
.
i
(
"registered user uri="
+
kiiUser
.
toUri
(
)
.
toString
(
)
)
;
return
super
.
doInBackground
(
params
)
;
}
catch
(
Exception
e
)
{
Logger
.
e
(
"failed to sign up"
,
e
)
;
return
false
;
}
}
|
利用 Kii User Builder 你可以使用用户名、Email 和/或 电话号码创建用户。在 Builder 中设置基本信息后,只需要传递密码给 KiiUser.register() method 即可完成注册!(此处,因为注册是 Android 异步任务中的一部分,所以我们使用的是注册 API 的阻塞模式)
登录 Fragment 和注册十分相似(SigninDialogFragment):
当输入某用户的 Email 和密码,你便会收到后端的响应(通过 Exception 表征成功或失败)。代码还是很简单(详见 SigninDialogFragment),还需要诸如用户名、Email 或是电话号码(此处是 Email)的用户 ID 以及密码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
KiiUser
.
logIn
(
new
KiiUserCallBack
(
)
{
@
Override
public
void
onLoginCompleted
(
int
token
,
KiiUser
user
,
Exception
e
)
{
if
(
e
!=
null
)
{
Logger
.
e
(
"Unable to login."
,
e
)
;
ToastUtils
.
showShort
(
getActivity
(
)
,
"Unable to login"
)
;
SimpleProgressDialogFragment
.
hide
(
getFragmentManager
(
)
)
;
return
;
}
if
(
checkRemember
)
{
Logger
.
i
(
user
.
getAccessToken
(
)
)
;
PreferencesManager
.
setStoredAccessToken
(
user
.
getAccessToken
(
)
)
;
}
new
PostSigninTask
(
user
.
getDisplayname
(
)
,
user
.
getEmail
(
)
)
.
execute
(
)
;
}
}
,
email
,
password
)
;
|
你可能注意到,与之前的注册示例不同,我们现在传递了一个隐式的 Callback 用来异步调用注册 API(在之前的例子中,用户注册是在 Android 异步任务中被调用的,所以不需要使用 Callback,但是你要知道异步方法可用的)。Kii 非阻塞 API 的优点是与后端的通信是运行在独立的线程上的,并且启动方法的调用是立刻返回的(当 Callback 被调用时,你可以处理后端结果),如此便可避免阻塞 UI。
目前为止,我们通过注册或登录获得了一个有效用户。这意味着,你可以在代码的任何地方调用KiiUser.getCurrentUser() 获取当前用户。现在,需要把用户映射到我们的用户模型类:ChatUser,并初始化用户用于接收 Push 通知(用于在 App 的用户间互相收发消息)。这些是在执行注册/登录任务后在 ChatUserInitializeTask.initializeChatUser() 中实现的:
1
2
3
4
5
6
|
KiiUser
kiiUser
=
KiiUser
.
getCurrentUser
(
)
;
ChatUser
user
=
ChatUser
.
findByUri
(
kiiUser
.
toUri
(
)
)
;
if
(
user
==
null
)
{
user
=
new
ChatUser
(
kiiUser
.
toUri
(
)
.
toString
(
)
,
username
,
email
)
;
user
.
getKiiObject
(
)
.
save
(
)
;
}
|
除了获取 ChatUser 外,其他重要的任务(配置用户准备接收 Push 通知)都是在ChatUserInitializeTask.initializeChatUser() 期间处理的。
使用 Facebook 账号登录
为了归纳用户加载机制,我们先看看使用 Facebook 登录。Kii 允许通过 Kii Social Connect 使用Twitter 和 Facebook 账号登录 App。只需要在开发者平台添加一些参数,并在 App 中添加一些代码,用户便可以使用他们的 Facebook 账号注册和登录。
首先,需要在 Facebook 上创建一个 App,然后在 Kii 开发者平台上将 App 关联到刚建的 Facebook App 。过程如下:
创建一个 Facebook App
首先,需要创建一个 Facebook App 并获取 Facebook Application ID
详见 Facebook 的官方指南。你也可以在我们的 DZone 外部文章中获取详细信息。
为 Facebook 配置 Kii App
按如下步骤在开发者平台上为你的 App 设置 Facebook Application ID。
1. 在 App 控制台中,点击 “Edit” 按钮。
2. 点击 “Settings”,弹出 App 配置菜单。
3. 粘贴你的 Facebook App ID
如下代码阐述通过 Kii Social Connect 使用 Facebook 登录 KiiChat:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
KiiFacebookConnect
connect
=
(
KiiFacebookConnect
)
Kii
.
socialConnect
(
SocialNetwork
.
FACEBOOK
)
;
connect
.
initialize
(
ApplicationConst
.
FACEBOOK_APP_ID
,
null
,
null
)
;
Bundle
options
=
new
Bundle
(
)
;
String
[
]
permission
=
new
String
[
]
{
"email"
}
;
options
.
putStringArray
(
KiiFacebookConnect
.
FACEBOOK_PERMISSIONS
,
permission
)
;
connect
.
logIn
(
SigninActivity
.
this
,
options
,
new
KiiSocialCallBack
(
)
{
public
void
onLoginCompleted
(
SocialNetwork
network
,
KiiUser
user
,
Exception
exception
)
{
if
(
exception
==
null
)
{
if
(
checkRemember
.
isChecked
(
)
)
{
Logger
.
i
(
user
.
getAccessToken
(
)
)
;
PreferencesManager
.
setStoredAccessToken
(
user
.
getAccessToken
(
)
)
;
}
new
PostSigninTask
(
user
.
getDisplayname
(
)
,
user
.
getEmail
(
)
)
.
execute
(
)
;
}
else
{
Logger
.
e
(
"failed to sign up"
,
exception
)
;
ToastUtils
.
showShort
(
SigninActivity
.
this
,
"Unable to sign up"
)
;
}
}
}
)
;
|
代码简介:
1. 传递目标社交网络服务名(此处为 SocialNetwork.FACEBOOK)给 socialConnect 方法创建一个社交网络连接器实例。
2. 传递 Facebook App ID 给 initialize 方法初始化社交网络连接器。
3. 使用 logIn 方法启动注册过程。如果指定的 Facebook 账号是新的,如有需要 SDK 将先执行登录过程。因为使用的是非阻塞方法,所以使用了一个 Callback 获取结果。
上面的代码中,我们为执行 logIn 方法设置了权限用于从 Facebook 获取邮箱地址。当用户使用此选项且用于登录的 Facebook 账号是新的,Kii Cloud 将从 Facebook 提取他的邮箱地址,并检查是否存在具有相同邮箱地址的 Kii 账号。如果存在这样的 Kii 账号且邮箱地址已经验证,Kii Cloud 将不会执行注册过程,但是会把指定的 Facebook 账号连接到该 Kii 账号。与之前描述的注册和登录过程一样,使用 Facebook 账号登录成功后,当调用 KiiUser.getCurrentUser() 时,系统会返回一个有效的 Kii User。
不要忘记,在处理 Facebook 登录的 Android Activity 中实现 onActivityResult 方法,该方法需要包含 Facebook 验证的代码:
1
2
3
4
|
@
Override
protected
void
onActivityResult
(
int
requestCode
,
int
resultCode
,
Intent
data
)
{
Kii
.
socialConnect
(
SocialNetwork
.
FACEBOOK
)
.
respondAuthOnActivityResult
(
requestCode
,
resultCode
,
data
)
;
}
|
你可以将 Facebook 账号连接或取消连接到 Kii 用户,详见我们的 【 Facebook integration guide 】。
登出
用户可以随时登出 App。登出的代码在 FriendListFragment 中,也十分容易理解:
1
2
3
4
5
|
PreferencesManager
.
setStoredAccessToken
(
""
)
;
KiiUser
.
logOut
(
)
;
intent
=
new
Intent
(
getActivity
(
)
,
MainActivity
.
class
)
;
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
)
;
startActivity
(
intent
)
;
|
加载用户的功能完成了。我们继续关注添加好友的功能。
添加好友
当点击 App 标题中 “+” 图标(在 Friends 页时)时,将显式一个搜索栏用于查询后端中的用户,并将他们添加为你的好友(如下图所示,我已经添加了一个名为 “pub2″ 的好友):
Kii Cloud 提供了一个强大的查询系统,帮助你快速查询所需的数据。一旦输入了关键字,UserListLoader 将从后端获取用户:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@
Override
public
List
&
lt
;
ChatUser
&
gt
;
loadInBackground
(
)
{
List
&
lt
;
ChatUser
&
gt
;
users
=
new
ArrayList
&
lt
;
ChatUser
&
gt
;
(
)
;
try
{
List
&
lt
;
ChatUser
&
gt
;
results
=
ChatUser
.
searchByKeyword
(
keyword
)
;
for
(
ChatUser
user
:
results
)
{
if
(
!
TextUtils
.
equals
(
user
.
getUri
(
)
,
KiiUser
.
getCurrentUser
(
)
.
toUri
(
)
.
toString
(
)
)
)
{
users
.
add
(
user
)
;
}
}
}
catch
(
Exception
e
)
{
Logger
.
e
(
"Unable to list users"
,
e
)
;
}
return
users
;
}
|
此处,ChatUser.searchByKeyword() 提供了底层代码查询后端中的 ChatUser:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
static
List
&
lt
;
ChatUser
&
gt
;
searchByKeyword
(
String
keyword
)
throws
Exception
{
KiiQuery
query
=
null
;
if
(
TextUtils
.
equals
(
"*"
,
keyword
)
)
{
query
=
new
KiiQuery
(
)
;
}
else
{
query
=
new
KiiQuery
(
KiiClause
.
or
(
KiiClause
.
startsWith
(
FIELD_USERNAME
,
keyword
)
,
KiiClause
.
startsWith
(
FIELD_EMAIL
,
keyword
)
)
)
;
}
List
&
lt
;
ChatUser
&
gt
;
users
=
new
ArrayList
&
lt
;
ChatUser
&
gt
;
(
)
;
List
&
lt
;
KiiObject
&
gt
;
objects
=
getBucket
(
)
.
query
(
query
)
.
getResult
(
)
;
for
(
KiiObject
object
:
objects
)
{
users
.
add
(
new
ChatUser
(
object
)
)
;
}
return
users
;
}
|
当你使用关键字 * 执行查询时,一个不含查询条件的 KiiQuery 会被创建(同 “catch all” 查询),用于获取所有已注册的用户。
如果关键字不同,则会添加 2 个查询条件 ,用于从头匹配用户名或 Email。当创建 Query 并传递给一个 Object Bucket(KiiBucket.query())时,就会获得查询结果。
获取用户列表后,你可以点击列表中的用户将其加为好友:
当添加一个好友时,ChatUser 将转换为 ChatFriend,之后将展示在一个独立的好友列表中,其特征与之前描述的全局用户十分相似(除了好友是储存在一个用户级 Bucket 中以外,这些好友只能被该用户查看)
发送消息
你可以点击好友列表中的好友邀请他们聊天:
聊天室的建立是在 ChatListFragment.NewChatTask : 中实现的,基本上是添加参与者到一个KiiGroup 中,然后创建一个 KiiTopic 用于发送邀请消息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@
Override
protected
KiiGroup
doInBackground
(
Void
.
.
.
params
)
{
try
{
String
chatRoomName
=
ChatRoom
.
getChatRoomName
(
KiiUser
.
getCurrentUser
(
)
,
this
.
chatFriend
)
;
String
uniqueKey
=
ChatRoom
.
getUniqueKey
(
KiiUser
.
getCurrentUser
(
)
,
this
.
chatFriend
)
;
for
(
int
i
=
0
;
i
&
lt
;
getListView
(
)
.
getCount
(
)
;
i
++
)
{
KiiGroup
kiiGroup
=
(
KiiGroup
)
getListView
(
)
.
getItemAtPosition
(
i
)
;
if
(
TextUtils
.
equals
(
uniqueKey
,
ChatRoom
.
getUniqueKey
(
kiiGroup
)
)
)
{
return
kiiGroup
;
}
}
KiiGroup
kiiGroup
=
Kii
.
group
(
chatRoomName
)
;
KiiUser
target
=
KiiUser
.
createByUri
(
Uri
.
parse
(
this
.
chatFriend
.
getUri
(
)
)
)
;
target
.
refresh
(
)
;
kiiGroup
.
addUser
(
target
)
;
kiiGroup
.
save
(
)
;
KiiBucket
chatBucket
=
ChatRoom
.
getBucket
(
kiiGroup
)
;
KiiUser
.
getCurrentUser
(
)
.
pushSubscription
(
)
.
subscribeBucket
(
chatBucket
)
;
KiiTopic
topic
=
target
.
topicOfThisUser
(
ApplicationConst
.
TOPIC_INVITE_NOTIFICATION
)
;
Data
data
=
new
Data
(
)
;
data
.
put
(
ChatRoom
.
CHAT_GROUP_URI
,
kiiGroup
.
toUri
(
)
.
toString
(
)
)
;
KiiPushMessage
message
=
KiiPushMessage
.
buildWith
(
data
)
.
build
(
)
;
topic
.
sendMessage
(
message
)
;
Logger
.
i
(
"sent notification to "
+
target
.
toUri
(
)
.
toString
(
)
)
;
return
kiiGroup
;
}
catch
(
Exception
e
)
{
Logger
.
e
(
"failed to start chat"
,
e
)
;
return
null
;
}
}
|
通过上面的代码,你可以看到 ChatRoom 是匹配到 KiiGroup 的。然后目标用户(聊天的目标)被添加到群组中。发送的消息是保存在一个群组级 Bucket 中的,并且当前用户订阅了该 Bucket 用于接收 Push 通知(所有订阅了该群组级 Bucket 的用户都会收到在该群组中发送的消息)。最后,使用群组 URI 构建一条消息发送邀请通知给目标用户。
上面的代码中,我们没有看到任何关于 Push 通知的低等级配置。其实,在用户注册或登录时,已经完成了。在“用户的注册和登录”一节中,我们描述过在ChatUserInitializeTask.initializeChatUser() 初始化过程中如何将一个用户匹配到 ChatUser。那个方法同时也初始化了 Push 通知系统,所以用户可以收发消息。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
KiiUser
kiiUser
=
KiiUser
.
getCurrentUser
(
)
;
ChatUser
user
=
ChatUser
.
findByUri
(
kiiUser
.
toUri
(
)
)
;
if
(
user
==
null
)
{
user
=
new
ChatUser
(
kiiUser
.
toUri
(
)
.
toString
(
)
,
username
,
email
)
;
user
.
getKiiObject
(
)
.
save
(
)
;
}
KiiUser
.
pushInstallation
(
)
.
install
(
GCMUtils
.
register
(
)
)
;
KiiTopic
topic
=
KiiUser
.
topic
(
ApplicationConst
.
TOPIC_INVITE_NOTIFICATION
)
;
try
{
topic
.
save
(
)
;
}
catch
(
ConflictException
e
)
{
}
KiiACL
acl
=
topic
.
acl
(
)
;
acl
.
putACLEntry
(
new
KiiACLEntry
(
KiiAnyAuthenticatedUser
.
create
(
)
,
TopicAction
.
SEND_MESSAGE_TO_TOPIC
,
true
)
)
;
try
{
acl
.
save
(
)
;
}
catch
(
ACLOperationException
e
)
{
Throwable
t
=
e
.
getCause
(
)
;
if
(
!
(
t
instanceof
ConflictException
)
)
{
throw
e
;
}
}
KiiPushSubscription
subscription
=
kiiUser
.
pushSubscription
(
)
;
try
{
subscription
.
subscribe
(
topic
)
;
}
catch
(
ConflictException
e
)
{
}
kiiUser
.
set
(
INITIALIZED_USER_KEY
,
true
)
;
kiiUser
.
update
(
)
;
|
上面的代码中,你可以看到我们为当前用户初始化了 Push 通知,创建了一个 KiiTopic 用于接收邀请通知,同时修改了 Topic 的权限允许所有已注册用户发送邀请。最后,订阅 Topic 用于接收邀请。
接下来是用户收发消息阶段。ChatActivity 类封装了所有的聊天交互。请看点击 “Send” 按钮时,调用的代码:
1
2
3
4
5
6
7
8
9
10
|
this
.
btnSend
.
setOnClickListener
(
new
OnClickListener
(
)
{
@
Override
public
void
onClick
(
View
v
)
{
btnSend
.
setEnabled
(
false
)
;
final
ChatMessage
message
=
new
ChatMessage
(
kiiGroup
)
;
message
.
setMessage
(
editMessage
.
getText
(
)
.
toString
(
)
)
;
message
.
setSenderUri
(
KiiUser
.
getCurrentUser
(
)
.
toUri
(
)
.
toString
(
)
)
;
new
SendMessageTask
(
message
)
.
execute
(
)
;
}
}
)
;
|
在上面的代码中,一个 ChatMessage 在当前用户群组中被初始化,该消息的文本来自 UI 文本框,发送方被设置为当前用户。请看 SendMessageTask 方法:
1
2
3
4
5
6
7
8
9
10
|
@
Override
protected
Boolean
doInBackground
(
Void
.
.
.
params
)
{
try
{
this
.
message
.
getKiiObject
(
)
.
save
(
)
;
ChatStamp
.
sendUsageEvent
(
this
.
message
)
;
return
true
;
}
catch
(
Exception
e
)
{
Logger
.
e
(
"failed to send messsage"
,
e
)
;
return
false
;
}
|
这里,消息被保存到后端,同时触发相应用户群组的变化。此时,所有群组成员都会收到 Push 通知。注意,同时发送的还有一个分析 Event (详见 ChatStamp.sendUsageEvent() )。Kii Cloud 能够统计这类 Event(以及所有存储的数据),更甚的,你可以在一个 Web 控制台自定义指标和维度将信息分类统计(结果是可视化的)。
本节总结,让我们回顾一下如何接收消息通知。为了接收 Push 通知,需要在 Android 中建立 BroadcastReceiver:
1
2
3
4
5
6
7
8
9
10
|
private
final
BroadcastReceiver
handleMessageReceiver
=
new
BroadcastReceiver
(
)
{
@
Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
updateMessage
(
false
)
;
}
}
;
private
void
updateMessage
(
boolean
showProgress
)
{
new
GetMessageTask
(
showProgress
)
.
execute
(
)
;
}
|
然后 GetMessageTask 使用相应群组的 ChatRoom 刷新消息列表(包含刚传入的那条):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@
Override
protected
List
&
lt
;
ChatMessage
&
gt
;
doInBackground
(
Void
.
.
.
params
)
{
try
{
ChatRoom
chatRoom
=
new
ChatRoom
(
kiiGroup
)
;
List
&
lt
;
ChatMessage
&
gt
;
messages
=
null
;
if
(
lastGotTime
==
null
)
{
messages
=
chatRoom
.
getMessageList
(
)
;
}
else
{
messages
=
chatRoom
.
getMessageList
(
lastGotTime
)
|