diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/startup/StartupInitializer.java b/app/src/main/java/com/d4rk/androidtutorials/java/startup/StartupInitializer.java index f6d63aa8..27e5380e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/startup/StartupInitializer.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/startup/StartupInitializer.java @@ -3,10 +3,13 @@ import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Helper used to enqueue a one-off background job for initializing ads and * cookies. The work is only scheduled once per app launch sequence. @@ -14,6 +17,7 @@ public final class StartupInitializer { private static final String WORK_NAME = "startup_init"; + private static final AtomicBoolean HAS_SCHEDULED = new AtomicBoolean(false); private StartupInitializer() { // no-op @@ -24,10 +28,19 @@ private StartupInitializer() { * is already enqueued, this call is ignored. */ public static void schedule(@NonNull Context context) { + if (!HAS_SCHEDULED.compareAndSet(false, true)) { + return; + } + OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder( StartupWorker.class).build(); WorkManager.getInstance(context).enqueueUniqueWork( WORK_NAME, ExistingWorkPolicy.KEEP, workRequest); } + + @VisibleForTesting + static void resetForTesting() { + HAS_SCHEDULED.set(false); + } } diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/startup/StartupInitializerTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/startup/StartupInitializerTest.java index afacd50f..20b47003 100644 --- a/app/src/test/java/com/d4rk/androidtutorials/java/startup/StartupInitializerTest.java +++ b/app/src/test/java/com/d4rk/androidtutorials/java/startup/StartupInitializerTest.java @@ -6,6 +6,8 @@ import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.MockedStatic; @@ -14,9 +16,20 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; public class StartupInitializerTest { + @Before + public void setUp() { + StartupInitializer.resetForTesting(); + } + + @After + public void tearDown() { + StartupInitializer.resetForTesting(); + } + @Test public void schedule_enqueuesWorkWithKeepPolicy() { Context context = mock(Context.class); @@ -34,4 +47,24 @@ public void schedule_enqueuesWorkWithKeepPolicy() { ); } } + + @Test + public void schedule_multipleCalls_enqueuesWorkOnlyOnce() { + Context context = mock(Context.class); + WorkManager workManager = mock(WorkManager.class); + + try (MockedStatic mockedWorkManager = mockStatic(WorkManager.class)) { + mockedWorkManager.when(() -> WorkManager.getInstance(context)).thenReturn(workManager); + + StartupInitializer.schedule(context); + StartupInitializer.schedule(context); + + verify(workManager).enqueueUniqueWork( + eq("startup_init"), + eq(ExistingWorkPolicy.KEEP), + any(OneTimeWorkRequest.class) + ); + verifyNoMoreInteractions(workManager); + } + } }