Can "repeated code" be avoided for multiple alarms (previously was: Multiple alarms = Multiple Boot receivers)?

StackOverflow https://stackoverflow.com/questions/21701656

سؤال

I have a very subtle question that might look grotesque, to more expert eyes than mine.

I'm implementing 2 distinct alarms, that survive reboot/shutdown and show a notification when fired.
One is a repeating daily reminder, and the other one is a monthly reminder which is reset by the app each time the user completes a given task.
Just to remind him/her to do it again when a month is passed.

Now, everything is working just fine.
So what's the problem?

Nothing, but I have the BootReceiver, AlarmReceiver and AlarmService doubled
Only the Notification builder is in common.

My question is then: Can I unify those elements and not have them splitted for any alarm?
Because if not, if in the future I'll need to schedule a weekly alarm, I'd have to make another boot receiver, alarm receiver and alarm service.

Which doesn't seem too smart, to me (say I add a weekly and a yearly tasks: I'd have to add 2 more of all receivers and services!! Which seems crazy, to me).
But maybe I'm wrong and things are to be like this?

In a previous app I wrote (before recognizing that it didn't pass reboots), it worked with the alarms sharing all the classes.

Thank you, guys, for your time.

If you need to see my code, just ask for it. But it's a bit long...

This is my Manifest file, just to show what my doubt is about:

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.scheduler2"
    android:versionCode="1"
    android:versionName="1.14.02.11 b"
    >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18"
    />

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:label="@string/app_name"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/Theme.Sample"
        >

        <!-- The Main Activity -->
        <activity
            android:name="com.example.android.scheduler2.ACT_Base"
            android:label="@string/app_name"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- The Preference Activity -->
        <activity android:name="com.example.android.scheduler2.ACT_Prefs" />

        <!-- 2 Alarms = 2 Alarm Boot receivers -->
        <!-- The One Shot Alarm Boot Receiver -->
        <receiver
            android:name="com.example.android.scheduler2.RCV_Boot_One"
            android:enabled="false"
            >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <!-- The Repeating Alarm Boot Receiver -->
        <receiver
            android:name="com.example.android.scheduler2.RCV_Boot_Rep"
            android:enabled="false"
            >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <!-- 2 Alarms = 2 Alarm receivers -->
        <!-- The One Shot Alarm Receiver -->
        <receiver android:name="com.example.android.scheduler2.RCV_Alarm_One" />
        <!-- The Repeating Alarm Receiver -->
        <receiver android:name="com.example.android.scheduler2.RCV_Alarm_Rep" />

        <!-- 2 Alarms = 2 Alarm services -->
        <!-- The One Shot Alarm Service -->
        <service android:name="com.example.android.scheduler2.SVC_Alarm_One" />
        <!-- The Repeating Alarm Service -->
        <service android:name="com.example.android.scheduler2.SVC_Alarm_Rep" />
    </application>
</manifest>
هل كانت مفيدة؟

المحلول

As I already suggested in the comments section something like the following may be a solution.

This approach requieres four classes:

  1. A BootBroadcastReceiver.class which extends BroadcastReceiver to listen to the onBootCompleted broadcast. As soon as the boot is completed this class will start the AlarmStarterService.class.
  2. The AlarmStarterService.class which extends Service will start both alarms then.
  3. The AlarmBroadcastReceiver.class which also extends BroadcastReceiver will receive the broadcasts fired by the alarms and then start the ShowNotificationService.class and passing the ID to it through the Intent.
  4. And finally the ShowNotificationService.class which also extends Service and will show a notification based on the ID passed trough the Intent.

The BootBroadcastReceiver may look like this.

public class BootBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        context.startService(new Intent(context, AlarmStarterService.class));
    }
}

public class AlarmStarterService extends Service {

    public static final int REQUEST_CODE_DAILY = 10000;
    public static final int REQUEST_CODE_MONTHLY = 10001;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        createAlarm();

        stopSelf();
    }  

    private void createAlarm() {
        PendingIntent pIntentDaily = getPendingIntent(REQUEST_CODE_DAILY);
        PendingIntent pIntentMonthly = getPendingIntent(REQUEST_CODE_MONTHLY);

        AlarmManager am = (AlarmManager) this
                .getSystemService(Context.ALARM_SERVICE);

        am.setRepeating(AlarmManager.RTC, TIME_DESIRED, AlarmManager.INTERVAL_DAY, pIntentDaily);
        am.setRepeating(AlarmManager.RTC, TIME_DESIRED, AlarmManager.INTERVAL_DAY * 30, pIntentMonthly );
    }

    private PendingIntent getPendingIntent(int requestCode) {
        Intent i= new Intent(this, AlarmBroadcastReceiver.class);
        i.putExtra("_id", requestCode);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                     

        return PendingIntent.getBroadcast(this, requestCode, i, PendingIntent.FLAG_UPDATE_CURRENT);
    }
}


public class AlarmBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        context.startService(new Intent(context, ShowNotificationService.class)
                .putExtra("_id", intent.getIntExtra("_id", -1)));
    }
}


public class ShowNotificationService extends Service {

    int id;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        id = intent.getIntExtra("_id", -1);
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        switch(id) {
            case AlarmStarterService.REQUEST_CODE_DAILY:
                showDailyNotification();
                break;
            case larmStarterService.REQUEST_CODE_MONTHLY:
                showMonthlyNotification();
                break;
        }

        stopSelf();
    }  
}

نصائح أخرى

I think what you are trying to do can be done using only one for both of them, and handle the different cases (even if it exceeds two) through setting application preferences.

Like you can save the last monthly alarm time and the last weekly and set the alarm to fire with the earliest time.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top