在后端即服务 (BaaS) 平台上开发移动应用程序
比较 Firebase 和 SynergyKit 在开发聊天应用程序时的异同
引言
如今,实时应用程序变得越来越受欢迎,但这类应用程序的开发并非易事。这也是为什么新的实时平台不断涌现的原因。它们将使实时应用程序的实现更加容易。开发人员不再需要担心与服务器的数据同步,而且他们可以节省高达 80% 的产品开发时间,并将更多精力投入到 UX/UI 上。
世界上最著名的实时平台很可能是 Firebase,截至今日,其注册用户已超过 200,000。另一方面,SynergyKit 是捷克公司 Letsgood.com(Etnetera Group)新开发平台的名称。目前 SynergyKit 处于公测阶段。
SynergyKit 还是 Firebase?
为了比较这两种产品,我将以一个非常常见的用例——聊天应用程序的一部分——为例。该应用程序会实时显示用户消息,当应用程序不在前台时,会通过推送通知发送。您可以通过 Facebook 个人资料进行注册和登录。
包含源代码的示例应用程序可在 GitHub 上找到。
应用程序截图
应用程序的外观基于 Synergit 的示例应用程序,并略有修改。
安装
SynergyKit
SynergyKit SDK for Android 的最低要求是 Android SDK 14 版本。
需要在 build.gradle 文件中的 dependencies 元素下添加以下依赖项。
dependencies {
...
compile 'com.letsgood:synergykit-sdk-android:2.1.7'
}
由于应用程序使用互联网访问,因此有必要向 Android 请求使用互联网的权限。这可以在 AndroidManifest.xml 文件中完成,您需要在 manifest 元素中添加以下代码。
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
接下来,您需要在首次使用前初始化 SynergyKit SDK。我建议在 Application 类的 onCreate 方法中进行。
public void onCreate() {
super.onCreate();
Synergykit.init(APPLICATION_TENANT, APPLICATION_KEY);
...
}
Firebase
Firebase SDK for Android 的最低要求是 Android SDK 10 版本。
需要在 build.gradle 文件中添加以下依赖项和 packagingOptions。
android {
...
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE-FIREBASE.txt'
exclude 'META-INF/NOTICE'
}
}
dependencies {
...
compile 'com.firebase:firebase-client-android:2.3.0'
}
由于应用程序使用互联网访问,因此有必要向 Android 请求使用互联网的权限。这可以在 AndroidManifest.xml 文件中完成,您需要将此代码插入 manifest 元素。
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
在首次使用之前,Firebase 需要 Context 才能运行,我建议在 Application 类的 onCreate 方法中进行。
public void onCreate() {
super.onCreate();
Firebase.setAndroidContext(this);
...
}
用户登录
用户将通过 Facebook 登录,Facebook 提供的姓名将被保存并用于发送消息。将为每个用户保存其在线状态信息。
SynergyKit
SDK 不支持从 Facebook AccessToken 获取姓名,因此您需要在登录前获取该姓名。这可以通过 GraphRequest 完成。用户的姓名将保存在 Name 变量中。
GraphRequest request = GraphRequest.newMeRequest(AccessToken.getCurrentAccessToken(), new GraphRequest.GraphJSONObjectCallback() {
@Override
public void onCompleted(JSONObject object, GraphResponse response) {
String name = null;
try {
name = object.getString("name"); // user's name on Facebook
} catch (JSONException e) {
e.printStackTrace();
}
}
});
Bundle parameters = new Bundle();
parameters.putString("fields", "name");
request.setParameters(parameters);
request.executeAsync();
在 User 参数中将传递 SynergyKitUser,其中将设置姓名、在线状态,并将保存数据。
Synergykit.linkFacebook(user, synergykitFacebookAuthData, new UserResponseListener() {
@Override
public void doneCallback(int statusCode, SynergykitUser user) {
// login successful
}
@Override
public void errorCallback(int statusCode, SynergykitError errorObject) {
// login error
}
});
Firebase
通过 Firebase 登录更简单。您无需通过 GraphRequest 获取用户姓名。Firebase SDK for Android 会自行解决这个问题。
private Firebase firebase;
...
firebase = new Firebase("https://<APP_URL>.firebaseio.com"); // onCreate init
...
firebase.authWithOAuthToken("facebook", accessToken.getToken(), new Firebase.AuthResultHandler() {
@Override
public void onAuthenticated(AuthData authData) {
// login successful
setOnline(true); // my method described below
}
@Override
public void onAuthenticationError(FirebaseError firebaseError) {
// login error
}
});
但这只是用户登录,而不是保存用户。要保存用户,我将使用一个在服务器上更改用户在线状态的函数。该函数在下面的“在线状态设置”部分进行说明。
在线状态设置
显示聊天界面的用户处于在线状态。当应用程序被另一个应用程序覆盖、显示关闭或用户关闭应用程序时,它将切换到离线状态。这些函数会调用 onResume 和 onPause,因此它们会设置正确的状态。
SynergyKit
下面描述的函数将用户的在线状态设置为“在线”。参数 online 是用户的状态。第二个参数 paralleMode 将导致此操作异步运行。
private void setOnline(boolean online, boolean parallelMode) { if (user == null) return; user.setOnline(online); Synergykit.updateUser(user, new UserResponseListener() { @Override public void doneCallback(int statusCode, SynergykitUser synergykitUser) { user = (SKUser) synergykitUser; // user successfully updated } @Override public void errorCallback(int statusCode, SynergykitError synergykitError) { // user update failed } }, parallelMode); }
Firebase
下面描述的函数将用户的在线状态设置为“在线”。如果用户不存在,则会创建用户。Firebase 使用 JSON 数据,因此这里使用了 map。
private void setOnline(boolean online) {
Map<String, Object> map = new HashMap<>();
map.put("name", userName); // user's name
map.put("online", online); // online status
firebaseUsers.child("" + uId).setValue(map); // create or override previous values
}
消息传递
消息传递通过将消息添加到两个平台的集合来解决,应用程序会侦听通过 websocket 接收到的更改。此集合会自动侦听新消息。
SynergyKit
创建了一个包含所有必要属性的类,并为了 SynergyKit 的需求而扩展了 SynergykitObject。以下代码会将一条消息添加到集合中。
Synergykit.createRecord(COLLECTION_MESSAGES, message, new ResponseListener() {
@Override
public void doneCallback(int statusCode, SynergykitObject synergykitObject) {
// message sent
}
@Override
public void errorCallback(int statusCode, SynergykitError synergykitError) {
// sending message failed
}
}, true);
Firebase
创建了一个包含所有必要属性的类。以下代码会将一条消息添加到集合中。
firebaseMessages.push().setValue(message, new Firebase.CompletionListener() {
@Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
// sending message failed
} else {
// message sent
}
}
});
接收消息
两个平台的接收消息都是通过侦听添加到集合的事件来解决的。应用程序将显示最后 100 条消息。
SynergyKit
以下代码从 messages 集合中获取最后 100 条记录。
SynergykitUri synergyKitUri = UriBuilder.newInstance()
.setResource(Resource.RESOURCE_DATA) // setting resource to data
.setCollection(COLLECTION_MESSAGES) // setting collection to messages
.setOrderByDesc("createdAt") // sorting by "createdAt"
.setTop(prevMessageCount) // top 100 messages
.build();
SynergykitConfig config = SynergykitConfig.newInstance()
.setParallelMode(true)
.setType(SKMessage[].class) // setting result type
.setUri(synergyKitUri); // setting URI created above
Synergykit.getRecords(config, new RecordsResponseListener() {
@Override
public void doneCallback(int statusCode, SynergykitObject[] synergykitObjects) {
SKMessage[] messages = (SKMessage[]) synergykitObjects; // posledních 100 zpráv sestupně
}
@Override
public void errorCallback(int statusCode, SynergykitError synergykitError) {}
});
以下代码将注册一个套接字以侦听 created 事件。
// listening on socket for event created in collection messages
Synergykit.onSocket(EVENT_CREATED, COLLECTION_MESSAGES, new SocketEventListener() {
@Override
public void call(Object... objects) {
String data = objects[0].toString(); // JSON data
final SKMessage message = GsonWrapper.getGson().fromJson(data, SKMessage.class); // get message
}
@Override
public void subscribed() {}
@Override
public void unsubscribed() {}
});
Synergykit.connectSocket();
Firebase
Query 用于过滤和减少消息数量。ChildEventListener 允许侦听更改。
Query query = firebaseMessages.orderByChild("timestamp").limitToLast(prevMessageCount);
query.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot == null) return; // no data received
FBMessage message = dataSnapshot.getValue(FBMessage.class); // get message
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
@Override
public void onCancelled(FirebaseError firebaseError) {}
});
推送通知
当用户不在聊天屏幕时,将接收并显示推送通知。推送通知的形式为“发送者:消息”,并在接收时播放默认通知声音。有关 GCM 的推送通知信息。
SynergyKit
前提是已在 Google Console 中创建了应用程序。在 SynergyKit 的应用程序设置中,有必要输入所谓的 GCM API 密钥,这可以在“设置 -> Android GCM”选项卡中完成。
必须将新的依赖项添加到 build.gradle 文件中。
dependencies {
...
compile 'com.google.android.gms:play-services-gcm:7.5.0'
}
注册 ID 是通过向 GCM 服务注册获得的。
regid = gcm.register(SENDER_ID);
先前获得的注册 ID 必须保存在将发送推送通知的服务器上。SynergyKit 将此信息保存在 SynergyKitPlatform 类的实例中。SynergyKitPlatform 类是 SynergykitUser 类或其子类的组成部分。
if (platform == null) { // create new platform
SynergykitPlatform platform = new SynergykitPlatform(regid); // passing gcm registration id
Synergykit.addPlatform(platform, new PlatformResponseListener() {
@Override
public void doneCallback(int statusCode, SynergykitPlatform platform) {
// add successful
}
@Override
public void errorCallback(int statusCode, SynergykitError errorObject) {
// add failed
}
}, true);
} else if (!platform.getRegistrationId().equals(regid)) { // id is different
platform.setRegistrationId(regid);
Synergykit.updatePlatform(platform, null, true); // update platform without listener
}
Cloud Code 允许在服务器端运行代码。可以从设备中启用或在集合事件发生时自动启用。代码是用 JavaScript 编写的,可在浏览器 IDE 中编写,也可以在那里进行调试。
我已将触发器设置为 messages 集合中的“created”事件,因此在收到消息后将调用 Cloud Code。以下 Cloud Code 会获取所有在线属性设置为 false 的用户,并将向他们发送通知。
var queryOfflineUsers = Synergykit.Query(Synergykit.User()); // begin query
// gets all users with online state set to false
queryOfflineUsers.where().attribute("online").isEqualTo(false).find({
success: function(offline_users, code) {
if (code == 200) {
var notification = Synergykit.Notification(offline_users); // creating notification with all offline users
var text = parameters.name + ': ' + parameters.text; // ziskani textu notifikace
notification.set("alert", text); // pridani textu k notifikaci
notification.send({ // odeslani notifikace
success: function(result, statusCode) {
callback({
result: 'success',
offline_users: offline_users
});
},
error: function(error, statusCode) {
callback;
}
});
} else {
callback;
}
},
error: callback
});
通知已发送给所有用户,但应用程序尚未接收到。需要在 Android 中实现 GCM 通知接收。
通过以下代码,我在 manifest 中请求了相关权限。
<manifest ... >
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
android:name="<PACKAGE_NAME>.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="<PACKAGE_NAME>.permission.C2D_MESSAGE" />
</manifest>
GcmReceiver 接收来自 GCM 的消息,并必须添加到 manifest 中。
<manifest ... >
<application ... >
...
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
...
</application>
</manifest>
MyGcmListenerService 是一个接收来自 GCMReceiver 的消息并随后创建通知的服务。该服务也必须添加到 manifest 中。
<manifest ... >
<application ... >
...
<service
android:name="<PACKAGE_NAME>.MyGcmListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
...
</application>
</manifest>
MyGcmListenerService 类在用户接受通知时向用户显示通知,这些通知来自 GcmReceiver。
public class MyGcmListenerService extends GcmListenerService {
@Override
public void onMessageReceived(String from, Bundle data) {
String message = data.getString("alert"); // get text of message
sendNotification(message);
}
private void sendNotification(String message) {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
... // setup notification
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* notification ID */, notificationBuilder.build());
}
}
Firebase
Firebase 不支持发送推送通知,其实现并不容易。尽管如此,互联网上有很多教程可以帮助您自行解决此问题,并构建自己的服务器应用程序。
结论
对于此特定应用程序,使用 SynergyKit 更好。首先,要在 Firebase 平台上创建推送通知,您需要使用第三方产品。其次,SynergyKit 允许将部分代码移到服务器,从而延长设备的续航时间。服务器部分可以在浏览器中进行修改,这些更改不需要更新应用程序。两个平台都提供简单的用户登录、读写数据功能。