Android: AlarmManager used to call a Service to no

2019-05-19 03:32发布

问题:

Edit Adding this line in my manifest solved my problem (the Service is well created).

<service android:name=".TimersService" >

Post

I'm currently trying to implement alarms to notify the user that a countdown has finished. I have a method createAlarm() that adds a new Alarm through an AlarmManager. This method is currently called inside a Fragment. It looks like this:

private final void createAlarm(String name, long milliInFuture) {

        Intent myIntent = new Intent(getActivity().getApplication(),
                TimersService.class);

        AlarmManager alarmManager = (AlarmManager) getActivity()
                .getSystemService(Context.ALARM_SERVICE);

        PendingIntent pendingIntent = PendingIntent.getService(getActivity()
                .getApplication(), 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        alarmManager.set(AlarmManager.RTC_WAKEUP,
                milliInFuture, pendingIntent); 

    }

I expect this method to add an Alarm. The Alarm should be called even if the device is in sleep mode. It should be called at time milliInFuture (which is System.currentTimeMillis()+ some time). When the alarm is up, it should start a Service. The Service is the following. This Service should do only one thing: notify the user that the alarm has finished. My Service class is as follows:

public class TimersService extends Service {

    private NotificationManager mNM;
    private int NOTIFICATION = 3456;


    public class LocalBinder extends Binder {
        TimersService getService() {
            return TimersService.this;
        }
    }

    @Override
    public void onCreate() {
        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        showNotification();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("LocalService", "Received start id " + startId + ": " + intent);
            return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mNM.cancel(NOTIFICATION);
        Toast.makeText(this, "Alarm", Toast.LENGTH_SHORT).show();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

        private final IBinder mBinder = new LocalBinder();

    private void showNotification() {

        final NotificationCompat.Builder builder = new NotificationCompat.Builder(getBaseContext());
        builder.setSmallIcon(R.drawable.clock_alarm);
        builder.setContentTitle("Time is up");
        builder.setContentText("SLIMS");
        builder.setVibrate(new long[] { 0, 200, 100, 200 });
        final Notification notification = builder.build(); 

        mNM.notify(NOTIFICATION, notification);
        NOTIFICATION += 1;
    }

}

When I run my code, my method createAlarm is called. But my Service is never created. I wrote this code based on Alexander's Fragotsis's one found here. And my Service class is inspired from the Android references of the Service class.

Any idea why my Service is not being called ? Is there anything I should write in my Manifest about the Alarm,Service or Notification ?

Thank you for your help

Ho and I would appreciate any suggestion about my code. If you know an easier way to notify the user after a fixed amount of time, let me know !

回答1:

Since all you're doing is notifying your user one time, a service is not the best approach for this. Services are meant to do work in the background. A notification is not really the type of work suited for a Service - it's too short. Therefore, I suggest you use a BroadcastReceiver instead.

The class should be something like this:

public class TimerReceiver extends BroadcastReceiver {
  private static final int NOTIFICATION = 3456; 

/*since you're always doing a 1-time notification, we can make this final and static, the number
 won't change. If you want it to change, consider using SharedPreferences or similar to keep track 
 of the number. You would have the same issue with a Service since you call stopself() and so,
 you would delete the object every time.*/

  @Override
  public void onReceive(Context context,Intent intent) {

    final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
    builder.setSmallIcon(R.drawable.clock_alarm);
    builder.setContentTitle("Time is up");
    builder.setContentText("SLIMS");
    builder.setVibrate(new long[] { 0, 200, 100, 200 });
    final Notification notification = builder.build(); 

    mNM.notify(NOTIFICATION, notification);
  }

To call the receiver, you need to change both the Intent to point to the new class and getService() needs to be getBroadcast(). Therefore this

Intent myIntent = new Intent(getActivity().getApplication(),
                TimersService.class);


PendingIntent pendingIntent = PendingIntent.getService(getActivity()
                .getApplication(), 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);

needs to be

Intent myIntent = new Intent(getActivity().getApplication(),
                TimerReceiver.class);

PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity()
                .getApplication(), 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);

Also, you should be able to safely change getActivity().getApplication() into just getActivity()

Lastly you need a manifest declaration:

<receiver android:name=".TimerReceiver" ></receiver>