I'm trying to use a background service so that I can check for new content on the app every 30 minutes and notify the user with a notification if there is any new content. The problem is that that service does not seem that it is starting at all. I'm not entirely sure what I have wrong.
I've followed this article to implement a notification service and this question as well - Trying to start a service on boot on Android.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.____.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>
<receiver android:name="com.____.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name="com.____.NotificationService"/>
</application>
The BootReceiver should start the Service
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent startServiceIntent = new Intent(context, NotificationService.class);
context.startService(startServiceIntent);
}
}
The service NotificationService right now is set to display a simple notification
public class NotificationService extends Service {
private WakeLock mWakeLock;
/**
* Simply return null, since our Service will not be communicating with
* any other components. It just does its work silently.
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* This is where we initialize. We call this when onStart/onStartCommand is
* called by the system. We won't do anything with the intent here, and you
* probably won't, either.
*/
private void handleIntent(Intent intent) {
// obtain the wake lock
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My App");
mWakeLock.acquire();
// check the global background data setting
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
if (cm.getActiveNetworkInfo() == null) {
stopSelf();
return;
}
// do the actual work, in a separate thread
new PollTask().execute();
}
private class PollTask extends AsyncTask<Void, Void, Void> {
/**
* This is where YOU do YOUR work. There's nothing for me to write here
* you have to fill this in. Make your HTTP request(s) or whatever it is
* you have to do to get your updates in here, because this is run in a
* separate thread
*/
@SuppressLint("NewApi") @Override
protected Void doInBackground(Void... params) {
// do stuff!
// get last added date time of offer
// every 60 minutes check for new offers
Notification notification = new Notification.Builder(getApplicationContext())
.setContentText("New Content Available")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(0)
.build();
return null;
}
/**
* In here you should interpret whatever you fetched in doInBackground
* and push any notifications you need to the status bar, using the
* NotificationManager. I will not cover this here, go check the docs on
* NotificationManager.
*
* What you HAVE to do is call stopSelf() after you've pushed your
* notification(s). This will:
* 1) Kill the service so it doesn't waste precious resources
* 2) Call onDestroy() which will release the wake lock, so the device
* can go to sleep again and save precious battery.
*/
@Override
protected void onPostExecute(Void result) {
// handle your data
stopSelf();
}
}
/**
* This is deprecated, but you have to implement it if you're planning on
* supporting devices with an API level lower than 5 (Android 2.0).
*/
@Override
public void onStart(Intent intent, int startId) {
handleIntent(intent);
}
/**
* This is called on 2.0+ (API level 5 or higher). Returning
* START_NOT_STICKY tells the system to not restart the service if it is
* killed because of poor resource (memory/cpu) conditions.
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
return START_NOT_STICKY;
}
/**
* In onDestroy() we release our wake lock. This ensures that whenever the
* Service stops (killed for resources, stopSelf() called, etc.), the wake
* lock will be released.
*/
public void onDestroy() {
super.onDestroy();
mWakeLock.release();
}
}
Debugging the app with breakpoints in both BootRecevier and NotificationService are never triggered. Maybe I'm misunderstanding the way Background Services work.
UPDATE
I found this article about why the BroadcastRecevier is not being called. One of the mentioned points is PendingIntent requestCode missing
I've updated the BootRecevier as follows to test out the service...called every 1 minute:
@Override
public void onReceive(Context context, Intent intent) {
int minutes = 1;
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, NotificationService.class);
PendingIntent pi = PendingIntent.getService(context, 54321, i, 0);
am.cancel(pi);
if (minutes > 0) {
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + minutes*60*1000,
minutes*60*1000, pi);
}
}
Changing 0 to a 'unique' requestCode
54321 somehow started triggering the service. The issue is that the service does not carry on working when the app is closed...not sure if that's a completely different thing altogether.
UPDATE 2
I've updated the doInBackground
method in the NotificationService, using this example to display notifications:
@SuppressLint("NewApi") @Override
protected Void doInBackground(Void... params) {
Context mContext = getApplicationContext();
// invoke default notification service
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mContext);
mBuilder.setContentTitle("[Notification Title]");
mBuilder.setContentText("[Notification Text]");
mBuilder.setTicker(mContext.getString(R.string.app_name));
mBuilder.setSmallIcon(R.drawable.ic_launcher);
Intent resultIntent = new Intent(mContext, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_ONE_SHOT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
return null;
}
I've tested it on an emulator and the Background Service works even when the app is closed. However, testing the app on an actual device did not display any notifications.