关于从 C2DM 切换到 GCM 的问题
Posted
技术标签:
【中文标题】关于从 C2DM 切换到 GCM 的问题【英文标题】:questions about switching from C2DM to GCM 【发布时间】:2015-10-06 12:13:38 【问题描述】:我正在将我的应用从 C2DM 转换为 GCM,并且有一些问题。
我已修改此处的 gcmquickstart 示例:
git clone https://github.com/googlesamples/google-services.git
在我扩展应用程序的类中,我有:
public class MyApp extends Application
@Override
public void onCreate()
super.onCreate();
if (this.checkPlayServices())
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
else
Log.e(TAG,"Failed checkforGCM");
AFAIK,这会启动服务并且它将永远运行,或者如果操作系统将其关闭,那么操作系统将重新启动它。对吗?
checkPlayServices() 确保这是 android 2.3 或更高版本并安装了 Google Play 服务,因为这些是使用 GCM 的唯一要求。那是对的吗?
public class RegistrationIntentService extends IntentService
private static final String TAG = "RegIntentService";
public RegistrationIntentService()
super(TAG);
@Override
protected void onHandleIntent(Intent intent)
try
// In the (unlikely) event that multiple refresh operations
// occur simultaneously,
// ensure that they are processed sequentially.
synchronized (TAG)
// [START register_for_gcm]
// Initially this call goes out to the network to
// retrieve the token,
// subsequent calls are local.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.googleProjectId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Log.d(TAG, "GCM Registration Token: " + token);
/* Store the token */
// [END register_for_gcm]
catch (Exception e)
Log.e(TAG, "Failed to complete token refresh", e);
什么“注册 GCM”?是这样吗:
InstanceID instanceID = InstanceID.getInstance(this);
什么“得到令牌”?是这样吗:
String token = instanceID.getToken(getString(R.string.googleProjectId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
我对刷新操作的讨论有点困扰。具体来说,我是否必须定期刷新令牌?如果是这样,那么在哪里,如何以及哪一个(InstanceID instanceID 或 String token)?
AFAIK,GCM 将在适当时向我发送一个新令牌。我所要做的就是准备好接受它并随时存储它。对吗?
使用如上所示的 RegistrationIntentService 可以让我随时接受/存储令牌。对吗?
同步也有点麻烦。这似乎暗示谷歌可以向我发送多个注册令牌。有什么方法可以触发它,以便我可以看到对我的应用的更大影响?
还有什么“后续调用是本地的”?调用以获取我在此处收到并存储在设备上的令牌?
public class MyInstanceIDListenerService extends InstanceIDListenerService
private static final String TAG = "MyInstanceIDLS";
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. This call is initiated by the
* InstanceID provider.
*/
// [START refresh_token]
@Override
public void onTokenRefresh()
// Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
// [END refresh_token]
这是我最困惑的地方。基于阅读代码,我认为这个想法是如果 GCM 服务器决定我需要另一个 instanceID,那么它会将必要性传达给 MyInstanceIDListenerService。此时,MyInstanceIDListenerService 将导致 RegistrationIntentService 运行并获取新的 instanceID 和令牌。对吗?
/** Intent Service to handle each incoming GCM message */
public class MyGcmListenerService extends GcmListenerService
final static String TAG = "GcmService";
/**
* Called when message is received.
*
* @param from SenderID of the sender.
* @param data Data bundle containing message data as key/value pairs.
* For Set of keys use data.keySet().
*/
// [START receive_message]
@Override
public void onMessageReceived(String from, Bundle data)
//do stuff with data communicated from my server to this app
//via the GCM registration token received in
//RegistrationIntentService. onHandleIntent(Intent intent)
// [END receive_message]
这项服务对我来说是最容易理解的。这个练习的重点是让 GCM 为我的应用程序分配一个令牌,我的应用程序将令牌传递给我们的一个服务器,我们的服务器向 GCM 发送一条消息,告诉它“将这个发送给使用这个令牌的人”,然后它在上面的代码中到达我的应用程序。对吗?
这是将所有内容组合在一起的 AndroidManifest:
<!-- [START gcm_receiver] -->
<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" />
<category android:name="my.package" />
</intent-filter>
</receiver>
<!-- [END gcm_receiver] -->
<!-- [START gcm_listener] -->
<service
android:name="my.package.MyGcmListenerService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<!-- [END gcm_listener] -->
<service
android:name="my.package.MyInstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
<service
android:name="my.package.RegistrationIntentService"
android:exported="false">
</service>
为什么我必须在 MyApp 中显式启动 RegistrationIntentService:
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
但不是其他服务?
【问题讨论】:
【参考方案1】:if (this.checkPlayServices()) Intent intent = new Intent(this, RegistrationIntentService.class); startService(intent);
这会启动服务,并且它将永远运行,或者如果操作系统将其关闭,那么操作系统将重新启动它。对吗?
没有。 IntentService 只会在完成onHandleIntent
中的代码之前存在
checkPlayServices()
确保这是 Android 2.3 或更高版本并安装了 Google Play 服务,因为这些是使用 GCM 的唯一要求。对吗?
是的。和不。您还需要网络连接。我喜欢在检查 Play Services 之前检查网络。
什么“注册 GCM”?是这样吗:
InstanceID instanceID = InstanceID.getInstance(this);
什么“得到令牌”?是这样吗:
String token = instanceID.getToken(getString(R.string.googleProjectId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
是的。 getInstance()
将向 GCM 注册。 getToken
会给你注册给这个用户的令牌。
这项服务对我来说是最容易理解的。这个练习的重点是让 GCM 为我的应用程序分配一个令牌,我的应用程序将令牌传递给我们的一个服务器,我们的服务器向 GCM 发送一条消息,告诉它“用这个令牌发送给那个人”,然后它在上面的代码中到达我的应用程序。对吗?
是的。差不多。
为什么我必须在 MyApp 中显式启动 RegistrationIntentService:
Intent intent = new Intent(this, RegistrationIntentService.class); startService(intent);
但不是其他服务?
您在清单中声明了InstanceIDListenerService
和GcmListenerService
,因为它们需要一直运行。如果您没有注册,那么他们不会做任何事情。 RegistrationService
未在清单中调用,因为您可能不希望它立即启动。例如,如果您想让您的用户注册您的应用程序,那么一旦通过身份验证,您就可以通过启动此服务向 gcm 注册。
【讨论】:
"IntentService 将只存在直到它完成 onHandleIntent 中的代码" OK,这回答了为什么我必须在 2 个地方启动 RegistrationIntentService。我想剩下的唯一问题是 GCM 是否会自动假定 InstanceIDListenerService 和 GcmListenerService 正在为任何注册的客户端运行?以上是关于关于从 C2DM 切换到 GCM 的问题的主要内容,如果未能解决你的问题,请参考以下文章
从 C2DM 迁移到 GCM for Android SDK 19
C2DM 关闭 - 如果仍然使用 C2DM API for GCM 会发生啥?
为啥 C2DM/GCM 不使用 SMS 作为传输来节省电池寿命?