Ricezione di una notifica GCM all'interno del servizio Android
-
21-12-2019 - |
Domanda
Ho un Service
che funziona insieme alla mia applicazione principale.Lo scopo di questo servizio è sincronizzarsi con un server principale in background.Per evitare di interrogare costantemente il server principale per gli aggiornamenti, ho implementato la messaggistica cloud di Google per avvisare l'app quando ci sono modifiche da scaricare.
Quando inserisco il GcmBroadcastReceiver
class nel mio pacchetto principale, riceve la multa di notifica.Tuttavia, vorrei che questo venisse ricevuto dal mio servizio, in modo che possa quindi attivare un downloadChanges()
funzione anche all'interno del servizio.Non voglio davvero che l'app riceva la notifica, quindi debba usarla IPC
comunicare con il servizio, sembra un po' complicato.
Esiste un modo per far sì che il mio servizio riceva la notifica GCM anziché l'app principale?
Attualmente il mio codice è così:
public class SyncService extends Service {
// lots of other stuff in this class
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "GCM received!");
downloadChanges();
}
}
}
Tuttavia, ottengo un ClassNotFoundException
quando si riceve la notifica del GCM.Qual è il modo corretto in cui il mio servizio in background gestisce una notifica GCM?
Manifesto:
<application
android:name="com.appnl.myapp.CustomApp"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppTheme" >
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<!-- android.name=".GcmBroadcastReceiver" -->
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.appnl.gcm" />
</intent-filter>
</receiver>
<service
android:name="service.SyncService"
android:enabled="true"
android:exported="false"
android:icon="@drawable/ic_launcher"
android:label="@string/service_name" >
<intent-filter>
<action android:name="service.SyncService" />
</intent-filter>
</service>
<!-- activity list -->
Registro di sistema:
04-11 12:40:19.474: E/AndroidRuntime(28044): FATAL EXCEPTION: main
04-11 12:40:19.474: E/AndroidRuntime(28044): java.lang.RuntimeException: Unable to instantiate receiver com.appnl.myapp.GcmBroadcastReceiver: java.lang.ClassNotFoundException: Didn't find class "com.appnl.myapp.GcmBroadcastReceiver" on path: /data/app/com.appnl.myapp-1.apk
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.app.ActivityThread.handleReceiver(ActivityThread.java:2493)
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.app.ActivityThread.access$1600(ActivityThread.java:159)
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1392)
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.os.Handler.dispatchMessage(Handler.java:99)
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.os.Looper.loop(Looper.java:137)
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.app.ActivityThread.main(ActivityThread.java:5419)
04-11 12:40:19.474: E/AndroidRuntime(28044): at java.lang.reflect.Method.invokeNative(Native Method)
04-11 12:40:19.474: E/AndroidRuntime(28044): at java.lang.reflect.Method.invoke(Method.java:525)
04-11 12:40:19.474: E/AndroidRuntime(28044): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
04-11 12:40:19.474: E/AndroidRuntime(28044): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
04-11 12:40:19.474: E/AndroidRuntime(28044): at dalvik.system.NativeStart.main(Native Method)
04-11 12:40:19.474: E/AndroidRuntime(28044): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.appnl.myapp.GcmBroadcastReceiver" on path: /data/app/com.appnl.myapp-1.apk
04-11 12:40:19.474: E/AndroidRuntime(28044): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:64)
04-11 12:40:19.474: E/AndroidRuntime(28044): at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
04-11 12:40:19.474: E/AndroidRuntime(28044): at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
04-11 12:40:19.474: E/AndroidRuntime(28044): at android.app.ActivityThread.handleReceiver(ActivityThread.java:2488)
04-11 12:40:19.474: E/AndroidRuntime(28044): ... 10 more
Modificare:Dovrei aggiungere che il mio servizio è in a separato pacchetto al mio pacchetto principale.Ho la sensazione che sia questo a causare il problema, ma potrei sbagliarmi.
Soluzione
Il tuo problema principale è la denominazione del ricevitore nel tuo manifest.Poiché è una classe interna, è necessario includere il nome della classe esterna e aggiungere il suo nome separato da $.Esempio...
com.appnl.myapp.SyncService$GcmBroadcastReceiver
Detto questo, tuttavia, non sono sicuro che questo sia un buon modo di fare le cose.
Innanzitutto il ciclo di vita di a BroadcastReceiver
è lungo quanto il onReceive(...)
il metodo viene eseguito e quel metodo dovrebbe eseguire solo un lavoro minimo.A meno che il tuo downloadChanges()
avvia semplicemente un'operazione asincrona di qualche tipo (e quindi ritorna immediatamente) bloccherai il file onReceive(...)
metodo.
In secondo luogo il tuo approccio presuppone il Service
è in esecuzione in modo permanente.Se è effettivamente così e puoi garantire che continuerà a funzionare, allora va bene, ma potresti rischiare di scaricare molto la batteria.
Se non hai bisogno di un funzionamento permanente Service
allora personalmente terrei il BroadcastReceiver
separare e utilizzare un IntentService
Invece.Quindi riceveresti semplicemente la trasmissione e poi entreresti onReceive(...)
utilizzo startService(...)
.Ciò garantirebbe la BroadcastReceiver
ha una vita breve e un IntentService
utilizza il proprio thread di lavoro (evitando così a NetworkOnMainThreadException
) e si terminerà automaticamente una volta completato il suo lavoro.