Skip to content

Instantly share code, notes, and snippets.

@metalwihen
Created March 7, 2018 11:00
Show Gist options
  • Save metalwihen/387d9d56f20ee22907466814c028f765 to your computer and use it in GitHub Desktop.
Save metalwihen/387d9d56f20ee22907466814c028f765 to your computer and use it in GitHub Desktop.
Android O - Service Changes

BEFORE

BroadcastReceiver

public class RebootBroadcastReceiver extends BroadcastReceiver {

    AlarmReceiver alarm = new AlarmReceiver();

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            alarm.setAlarm(context);
        }
    }
}

AndroidManifest:

        <service android:name=".alarm.UpdateRemoteConfigIntentService" />
        <receiver android:name=".alarm.AlarmReceiver" />
        <receiver
            android:name=".alarm.RebootBroadcastReceiver"
            android:enabled="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

IntentServices:

public class UpdateRemoteConfigIntentService extends IntentService {

    private static final String TAG = "UpdateRemoteConfigIntentService";

    public UpdateRemoteConfigIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        KayakoLogger.d(TAG, "Update remote config values");

        try {
            // Fetch remote config in background
            RemoteConfig.getInstance().fetchNewValuesSync();
        } finally {

            // Intent may be null if the service is being restarted after its process has gone away, and it had previously returned anything except START_STICKY_COMPATIBILITY.
            // if your service gets killed and restarted while in the middle of such work (so the Intent gets re-delivered to perform the work again), it will at that point no longer be holding a wake lock
            if (intent == null) {
                return;
            }

            // Release the wake lock
            AlarmReceiver.completeWakefulIntent(intent);
        }
    }
}

AlarmManager

public class AlarmReceiver extends WakefulBroadcastReceiver {
    private static final String TAG = "AlarmReceiver";
    private AlarmManager alarmManager;
    private PendingIntent alarmIntent; // The pending intent that is triggered when the alarm fires.

    private int mUniqueCode = 1; // ensure that whenenver the alarm is set, it will update (replace) the existing PendingIntent (instead of starting a new separate one).

    @Override
    public void onReceive(Context context, Intent intent) {
        // start Update Remote Config service with wakelock
        Intent updateRemoteConfigService = new Intent(context, UpdateRemoteConfigIntentService.class);
        startWakefulService(context, updateRemoteConfigService);
    }

    /**
     * Sets a repeating alarm that runs at regular periods. When the
     * alarm fires, the app broadcasts an Intent to this WakefulBroadcastReceiver.
     * <p/>
     * Also, according to: http://developer.android.com/reference/android/app/AlarmManager.html
     * - If there is already an alarm scheduled for the same IntentSender, that previous alarm will first be canceled.
     * - If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one.
     *
     * @param context
     */
    public void setAlarm(Context context) {
        alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, AlarmReceiver.class);
        alarmIntent = PendingIntent.getBroadcast(context, mUniqueCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        // Set the alarm to fire every 1 hour (session-timeout on server is 6 hours)
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                AlarmManager.INTERVAL_HOUR, AlarmManager.INTERVAL_HOUR, alarmIntent);

        // Enable {@code RebootBroadcastReceiver} to automatically restart the alarm when the
        // device is rebooted.
        ComponentName receiver = new ComponentName(context, RebootBroadcastReceiver.class);
        PackageManager pm = context.getPackageManager();
        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    }

    public void cancelAlarm(Context context) {
        // If the alarm has been set, cancel it.
        if (alarmManager != null) {
            alarmManager.cancel(alarmIntent);
        }

        // Disable {@code RebootBroadcastReceiver} so that
        // it doesn't automatically restart the alarm when the device is rebooted.
        ComponentName receiver = new ComponentName(context, RebootBroadcastReceiver.class);
        PackageManager pm = context.getPackageManager();
        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }
}

AFTER

build.gradle

compile 'com.firebase:firebase-jobdispatcher:0.8.5'

AndroidManifest

<service
    android:name=".alarm2.UpdateRemoteConfigJob"
    android:exported="false">
    <intent-filter>
        <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
    </intent-filter>
</service>

JobDispatcher

public class MyJobManager {
    private static final String TAG = "MyJobManager";

    FirebaseJobDispatcher mDispatcher;

    public MyJobManager(Context context) {
        // Create a new dispatcher using the Google Play driver.
        mDispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
    }

    public void enableJob() {
        Job myJob = mDispatcher.newJobBuilder()
                .setService(UpdateRemoteConfigJob.class)
                .setTag(UpdateRemoteConfigJob.TAG)
                .setRecurring(true) // repeat indefinitely until disabled
                .setLifetime(Lifetime.FOREVER)
                .setTrigger(Trigger.executionWindow(0, 60)) // trigger anytime once per day (24hours)
                //.setTrigger(Trigger.executionWindow(0, 24 * 60 * 60 * 60)) // trigger anytime once per day (24hours)
                .setReplaceCurrent(true)
                .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
                .build();

        mDispatcher.mustSchedule(myJob);
    }

    public void disableJob() {
        mDispatcher.cancel(UpdateRemoteConfigJob.TAG);
    }
}

JobService

public class UpdateRemoteConfigJob extends JobService {

    public static final String TAG = "UpdateRemoteConfigJob";
    private Call call;

    @Override
    public boolean onStartJob(final JobParameters job) {
        call = RemoteConfig.getInstance().fetchNewValuesAsync(new RemoteConfig.OnValuesFetched() {
            @Override
            public void onValuesFetched() {
                jobFinished(job, true);
            }
        });

        return false; // Answers the question: "Is there still work going on?"
    }

    @Override
    public boolean onStopJob(JobParameters job) {
        if (call != null) {
            call.cancel();
            call = null;
        }
        return false;  // Answers the question: "Should this job be retried?"
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment