关于从 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);

但不是其他服务?

您在清单中声明了InstanceIDListenerServiceGcmListenerService,因为它们需要一直运行。如果您没有注册,那么他们不会做任何事情。 RegistrationService 未在清单中调用,因为您可能不希望它立即启动。例如,如果您想让您的用户注册您的应用程序,那么一旦通过身份验证,您就可以通过启动此服务向 gcm 注册。

【讨论】:

"IntentService 将只存在直到它完成 onHandleIntent 中的代码" OK,这回答了为什么我必须在 2 个地方启动 RegistrationIntentService。我想剩下的唯一问题是 GCM 是否会自动假定 InstanceIDListenerService 和 GcmListenerService 正在为任何注册的客户端运行?

以上是关于关于从 C2DM 切换到 GCM 的问题的主要内容,如果未能解决你的问题,请参考以下文章

从 C2DM 迁移到 GCM for Android SDK 19

C2DM 注册 ID 与 GCM 注册 ID?

C2DM 关闭 - 如果仍然使用 C2DM API for GCM 会发生啥?

为啥 C2DM/GCM 不使用 SMS 作为传输来节省电池寿命?

Gcm 或 C2DM 应用程序启动问题。(Titanium android)

新 Parse Android SDK 附带的新 GCM(C2DM) 权限是不是会禁用当前应用用户的自动更新?