GCM SERVICE_NOT_AVAILABLE on Android 2.2

2019-01-05 03:32发布

I am getting the error "SERVICE_NOT_AVAILABLE" on my GoogleCloudMessaging.register() call on a Android 2.2 device.

I am writing an app that uses GoogleCloudMessaging using the new Google Play Services. I implemented it using the guidelines provided on the Android website and my source contains a lot of error checking and handling code such as making sure the Google Play Services is installed, or updated. The GCM registration code also implements a exponential backoff as google as suggested one to implement to handle the SERVICE_NOT_AVAILABLE error.

I have tested my application on a wide array of devices including ICS, JB, Honey Comb and even 2.3.x devices. The GCM registration works and I am able to send messages to it via GCM. However on a 2.2 device I am continuously getting a SERVICE_NOT_AVAILABLE error on the GoogleCloudMessaging.register() call even with the exponential backoff in place.

The device that GCM is failing on is a Samsung SGH-I896 to be exact and the phone has 2 google accounts on it. I've read that this error might be caused by a misconfigured time, but the time is set to be automatic. The phone is not moded and is running samsung stock ROM.

I have also tried rebooting the device as well as reinstalling Google Play Services without luck. Any help on this issue will be greatly appreciated.

EDIT: I tried to implement GCM using the old gcm.jar and GCMRegistrar and it ended up working on the device. However I hardly consider this a good solution to the problem as Google has stopped supporting this method afaik.

EDIT2: See the accepted answer.

9条回答
Luminary・发光体
2楼-- · 2019-01-05 03:53

I experienced the same problem. GCM works fine on my Tablet running Android 4.04, but always received a SERVICE_NOT_AVAILABLE on my smartphone running Android 2.3.

I found following workaround not using (so far as I know) any deprecated classes. Add the action "com.google.android.c2dm.intent.REGISTRATION" to the GCMBroadcastReceiver in your manifest. This will enable to receive the registration_id at your GCMBroadcastReceiver.

<receiver
   android:name="YOUR_PACKAGE_NAME.GCMBroadcastReceiver"
   android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
         <action android:name="com.google.android.c2dm.intent.RECEIVE" />
         <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

         <category android:name="YOUR_PACKAGE_NAME" />
      </intent-filter>
</receiver>

After that your GCMBroadcastReceiver is able to receive the registration_id:

public void onReceive(Context context, Intent intent) {
   String regId = intent.getExtras().getString("registration_id");
   if(regId != null && !regId.equals("")) {
      /* Do what ever you want with the regId eg. send it to your server */
   }
}

Although I still get a SERVICE_NOT_AVAILABLE error, I can handle the registration_id in my GCMBroadcastReceiver and I am able to send messages to my smartphone. Quite weird, but it works for me.

查看更多
Juvenile、少年°
3楼-- · 2019-01-05 03:54

SERVICE_NOT_AVAILABLE may be caused by an old or missing GooglePlayServices. Use GooglePlayServicesUtil to check the version and if wrong redirect to market so user can manually install it - this will fix the SERVICE_NOT_AVAILABLE and also get all improvements and bug fixes ( and new bugs :-).

If you can't do this - there is no point in using register(), the method expects the matching play service to retrieve the result. It does generate the same broadcast, for backward compatibility. Using the REGISTER intent directly or the deprecated library will continue to work - I would suggest using the intent, GCMRegistrar is tied to the old implementation in GoogleServicesFramework (it has a call to intent.setPackage(GSF_PACKAGE)), so it can't benefit from any fixes.

Having the latest version of GCM - by using GooglePlayServicesUtil to deal with devices that didn't get the automatic install - can help not only for registration.

查看更多
三岁会撩人
4楼-- · 2019-01-05 03:54

After reading the post from marnanish, I tried the code below, and I have successfully got my Activity to receive a registration. Just add this:

<activity
    android:name=".MyActivity"
    ... />
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="your.package.here" />
    </intent-filter>
</activity>

in your AndroidManifest.xml

查看更多
狗以群分
5楼-- · 2019-01-05 03:56

If the marked answer is not working for you (as it was for me), check the person in front of your device, maybe shake it and then enable the internet connection. Worked for me ;-)

Without internet, you receive the same Exception and I spent the afternoon chasing complex problems with the service....

查看更多
不美不萌又怎样
6楼-- · 2019-01-05 03:59

So, there is another thing to watch out for that tripped me up. I had it working fine in emulator + with test application, but then I had issues deploying it on actual devices.
Was consistently getting the SERVICE_NOT_AVAILABLE error, and resorted to an approach similar to the answer above:

     IntentFilter filter = new IntentFilter("com.google.android.c2dm.intent.REGISTRATION");
     BroadcastReceiver receiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "Got into onReceive for manual GCM retrieval. "
             + intent.getExtras());
             String regId = intent.getStringExtra("registration_id");

             if (regId != null && !regId.isEmpty()) {
                 theResult.add(regId);
             } else {
                 theResult.add("");
             }
             lastDitchEffortComplete = true;
             }
         };
     cont.registerReceiver(receiver, filter);

This sort-of worked. But it was taking an oddly long amount of time to receive this information, and in a wait-loop I had, it would ONLY ever retrieve it AFTER I'd given up and let things proceed.

This got me thinking that there might be another cause to the SERVICE_NOT_AVAILABLE msg.

This is the code I had to do the fetching/registration with GCM:

new AsyncTask<Void, Void, Void>() {
                final long timeSleep = 1000;

                @Override
                protected Void doInBackground(Void... params) {
                    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(cont);
                    String registrationId = "";
                    int count = 0;
                    while (count < 1) {
                        try {
                            count++;
                            Log.i(TAG, "Starting GCM Registration Attempt #" + count);
                            registrationId = gcm.register(CLOUDAPPID);
                            rph.storeRegistrationId(registrationId);
                            return null;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        Log.w(TAG, "Registration failed.  Retrying in " + timeSleep + " ms.");
                        try {
                            Thread.sleep(timeSleep);
                        } catch (InterruptedException e) {
                        }
                    }

                    return null;
                }
            }.execute().get();

The main thing to look for here is the last line.

.execute.get();

A "clever/cheating" way to block on asynchronous off-thread results.

It turns out, that IS what is causing the problem. As soon as I just had:

.execute();

Let it pass through, and made the algorithm/code work with reporting back and not needing the registration id right away, then it was fine. I didn't even need the manual broadcast receiver, it just worked like it should.

查看更多
We Are One
7楼-- · 2019-01-05 04:01

I had the same problem but it was happening on all devices. It took me forever to find my problem but I thought I would post it in case it matches someone else's case. Here is a simplified version of my problem:

public class Login extends Activity {
    Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        context = getApplicationContext();

        //Get GCM registration id
        new GcmRegistrationAsyncTask().execute(context);

        Set<Tuple> timeline = new HashSet<Tuple>();
        try {
            //Get data from Redis database
            timeline = new RedisAsyncTask().execute(context).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

When I add the second AsyncTask with a return using get(), I would get the GCM 'SERVICE_NOT_AVAILABLE' every time even though I could still retrieve the registration id from the GcmBroadcastReceiver onReceive(). However, when I run:

public class Login extends Activity {
    Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        context = getApplicationContext();

        new GcmRegistrationAsyncTask().execute(context);
        new RedisAsyncTask().execute(context);
    }
}

the GCM would receive the registration id without issue.

查看更多
登录 后发表回答