无法向 GCM 注册 Android 应用程序:不允许启动服务意图
Unable to Register Android App with GCM : Not allowed to start service Intent
使用 Android Studio 构建应用程序。
应用程序配置中存在一些问题,会引发 java.lang.SecurityException.
项目存储在https://github.com/snambi/gcm_register
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<permission android:name="org.antennae.gcmtests.gcmtest.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="org.antennae.gcmtests.gcmtest.permission.C2D_MESSAGE"/>
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyGcmIntentService" android:exported="false" ></service>
</application>
MainActivity
public class MainActivity extends ActionBarActivity {
public static final String EXTRA_MESSAGE = "message";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
// GcmWrapper startup
GcmWrapper gcmwrapper = new GcmWrapper(context);
String registrationId = gcmwrapper.getRegId();
Toast.makeText(context, "REG_ID = "+ registrationId, Toast.LENGTH_SHORT).show();
}
}
使用 GCM ( GcmWrapper ) 注册的代码
public class GcmWrapper {
private Context context;
public GcmWrapper( Context context){
this.context = context;
}
public String getRegId(){
String regId = getRegIdFromPrefs();
if( regId == null || regId.trim().equals("") ){
registerWithGcm();
}
return regId;
}
public String getRegIdFromPrefs(){
String registrationId=null;
SharedPreferences preferences = context.getSharedPreferences(Constants.PREF_ANTENNAE, Context.MODE_PRIVATE);
registrationId = preferences.getString(Constants.PREF_REGISTRATION_ID, "");
int appVersion = AppVersionUtils.getAppVersion(context);
SharedPreferences prefs =PreferencesUtil.getSharedPreferences(context);
if( appVersion != AppVersionUtils.getSavedAppVersion( prefs ) ){
// if different, clear the saved regId
registrationId=null;
}
return registrationId;
}
public void registerWithGcm(){
GcmRegistrationTask registerTask = new GcmRegistrationTask(context, Constants.PROJECT_ID);
registerTask.execute();
}
public static class GcmRegistrationTask extends AsyncTask{
private Context context;
private String projectId;
public GcmRegistrationTask( Context context, String projectId ){
this.context = context;
this.projectId = projectId;
}
@Override
protected Object doInBackground(Object[] params) {
String regId = null;
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
try {
regId = gcm.register( projectId );
} catch (IOException e) {
e.printStackTrace();
}
return regId;
}
}
}
抛出异常
06-18 22:54:01.737 24266-24317/org.antennae.gcmtests.gcmtest E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.SecurityException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gms (has extras) } without permission com.google.android.c2dm.permission.RECEIVE
at android.app.ContextImpl.startServiceAsUser(ContextImpl.java:1412)
at android.app.ContextImpl.startService(ContextImpl.java:1394)
at android.content.ContextWrapper.startService(ContextWrapper.java:473)
at com.google.android.gms.gcm.GoogleCloudMessaging.zzx(Unknown Source)
at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
at org.antennea.gcm.GcmWrapper$GcmRegistrationTask.doInBackground(GcmWrapper.java:70)
at android.os.AsyncTask.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
看起来这对其他人来说也是一个问题(参见 here)。
我检查了官方 GCM 文档,确实可以验证您需要将 uses-permission
放在应用程序标签之外。
顺便说一下,您似乎在使用已弃用的 C2DM 库。您可能希望使用 latest one 来避免以后迁移的麻烦。文档中也提供了一个示例项目。
使用 Android Studio 构建应用程序。 应用程序配置中存在一些问题,会引发 java.lang.SecurityException.
项目存储在https://github.com/snambi/gcm_register
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<permission android:name="org.antennae.gcmtests.gcmtest.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="org.antennae.gcmtests.gcmtest.permission.C2D_MESSAGE"/>
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyGcmIntentService" android:exported="false" ></service>
</application>
MainActivity
public class MainActivity extends ActionBarActivity {
public static final String EXTRA_MESSAGE = "message";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
// GcmWrapper startup
GcmWrapper gcmwrapper = new GcmWrapper(context);
String registrationId = gcmwrapper.getRegId();
Toast.makeText(context, "REG_ID = "+ registrationId, Toast.LENGTH_SHORT).show();
}
}
使用 GCM ( GcmWrapper ) 注册的代码
public class GcmWrapper {
private Context context;
public GcmWrapper( Context context){
this.context = context;
}
public String getRegId(){
String regId = getRegIdFromPrefs();
if( regId == null || regId.trim().equals("") ){
registerWithGcm();
}
return regId;
}
public String getRegIdFromPrefs(){
String registrationId=null;
SharedPreferences preferences = context.getSharedPreferences(Constants.PREF_ANTENNAE, Context.MODE_PRIVATE);
registrationId = preferences.getString(Constants.PREF_REGISTRATION_ID, "");
int appVersion = AppVersionUtils.getAppVersion(context);
SharedPreferences prefs =PreferencesUtil.getSharedPreferences(context);
if( appVersion != AppVersionUtils.getSavedAppVersion( prefs ) ){
// if different, clear the saved regId
registrationId=null;
}
return registrationId;
}
public void registerWithGcm(){
GcmRegistrationTask registerTask = new GcmRegistrationTask(context, Constants.PROJECT_ID);
registerTask.execute();
}
public static class GcmRegistrationTask extends AsyncTask{
private Context context;
private String projectId;
public GcmRegistrationTask( Context context, String projectId ){
this.context = context;
this.projectId = projectId;
}
@Override
protected Object doInBackground(Object[] params) {
String regId = null;
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
try {
regId = gcm.register( projectId );
} catch (IOException e) {
e.printStackTrace();
}
return regId;
}
}
}
抛出异常
06-18 22:54:01.737 24266-24317/org.antennae.gcmtests.gcmtest E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.SecurityException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gms (has extras) } without permission com.google.android.c2dm.permission.RECEIVE
at android.app.ContextImpl.startServiceAsUser(ContextImpl.java:1412)
at android.app.ContextImpl.startService(ContextImpl.java:1394)
at android.content.ContextWrapper.startService(ContextWrapper.java:473)
at com.google.android.gms.gcm.GoogleCloudMessaging.zzx(Unknown Source)
at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
at org.antennea.gcm.GcmWrapper$GcmRegistrationTask.doInBackground(GcmWrapper.java:70)
at android.os.AsyncTask.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
看起来这对其他人来说也是一个问题(参见 here)。
我检查了官方 GCM 文档,确实可以验证您需要将 uses-permission
放在应用程序标签之外。
顺便说一下,您似乎在使用已弃用的 C2DM 库。您可能希望使用 latest one 来避免以后迁移的麻烦。文档中也提供了一个示例项目。