Skip to content

Commit 337977f

Browse files
committed
Move SharedPreferences usage to background thread
1 parent 8e75ef4 commit 337977f

File tree

7 files changed

+253
-135
lines changed

7 files changed

+253
-135
lines changed

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/FirebaseAppDistributionImpl.java

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,6 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
116116

117117
@Override
118118
@NonNull
119-
// TODO(b/261014422): Use an explicit executor in continuations.
120-
@SuppressLint("TaskMainThread")
121119
public UpdateTask updateIfNewReleaseAvailable() {
122120
return updateIfNewReleaseAvailableTaskCache.getOrCreateTask(
123121
() -> {
@@ -126,9 +124,18 @@ public UpdateTask updateIfNewReleaseAvailable() {
126124
remakeUpdateConfirmationDialog = false;
127125
dialogHostActivity = null;
128126

129-
lifecycleNotifier
130-
.applyToForegroundActivityTask(this::showSignInConfirmationDialog)
131-
.onSuccessTask(lightweightExecutor, unused -> signInTester())
127+
signInStorage
128+
.getSignInStatus()
129+
.onSuccessTask(
130+
lightweightExecutor,
131+
signedIn -> {
132+
if (signedIn) {
133+
return Tasks.forResult(null);
134+
}
135+
return lifecycleNotifier
136+
.applyToForegroundActivityTask(this::showSignInConfirmationDialog)
137+
.onSuccessTask(lightweightExecutor, unused -> signInTester());
138+
})
132139
.onSuccessTask(lightweightExecutor, unused -> checkForNewRelease())
133140
.continueWithTask(
134141
lightweightExecutor,
@@ -178,10 +185,6 @@ public UpdateTask updateIfNewReleaseAvailable() {
178185

179186
@NonNull
180187
private Task<Void> showSignInConfirmationDialog(Activity hostActivity) {
181-
if (isTesterSignedIn()) {
182-
return Tasks.forResult(null);
183-
}
184-
185188
if (showSignInDialogTask == null || showSignInDialogTask.getTask().isComplete()) {
186189
showSignInDialogTask = new TaskCompletionSource<>();
187190
}
@@ -225,7 +228,7 @@ private Task<Void> showSignInConfirmationDialog(Activity hostActivity) {
225228

226229
@Override
227230
public boolean isTesterSignedIn() {
228-
return signInStorage.getSignInStatus();
231+
return signInStorage.getSignInStatusBlocking();
229232
}
230233

231234
@Override
@@ -242,19 +245,12 @@ public void signOutTester() {
242245
}
243246

244247
@Override
245-
@NonNull
246-
// TODO(b/261014422): Use an explicit executor in continuations.
247-
@SuppressLint("TaskMainThread")
248248
public synchronized Task<AppDistributionRelease> checkForNewRelease() {
249-
if (!isTesterSignedIn()) {
250-
return Tasks.forException(
251-
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE));
252-
}
253-
254249
return checkForNewReleaseTaskCache.getOrCreateTask(
255250
() ->
256-
newReleaseFetcher
257-
.checkForNewRelease()
251+
assertTesterIsSignedIn()
252+
.onSuccessTask(
253+
lightweightExecutor, unused -> newReleaseFetcher.checkForNewRelease())
258254
.onSuccessTask(lightweightExecutor, release -> cachedNewRelease.set(release))
259255
.onSuccessTask(
260256
lightweightExecutor,
@@ -274,6 +270,21 @@ public synchronized Task<AppDistributionRelease> checkForNewRelease() {
274270
}));
275271
}
276272

273+
private Task<Void> assertTesterIsSignedIn() {
274+
return signInStorage
275+
.getSignInStatus()
276+
.onSuccessTask(
277+
lightweightExecutor,
278+
signedIn -> {
279+
if (!signedIn) {
280+
return Tasks.forException(
281+
new FirebaseAppDistributionException(
282+
"Tester is not signed in", AUTHENTICATION_FAILURE));
283+
}
284+
return Tasks.forResult(null);
285+
});
286+
}
287+
277288
@Override
278289
@NonNull
279290
public UpdateTask updateApp() {
@@ -285,35 +296,39 @@ public UpdateTask updateApp() {
285296
* basic configuration and false for advanced configuration.
286297
*/
287298
private UpdateTask updateApp(boolean showDownloadInNotificationManager) {
288-
if (!isTesterSignedIn()) {
289-
UpdateTaskImpl updateTask = new UpdateTaskImpl();
290-
updateTask.setException(
291-
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE));
292-
return updateTask;
293-
}
294299
return TaskUtils.onSuccessUpdateTask(
295-
cachedNewRelease.get(),
300+
signInStorage.getSignInStatus(),
296301
lightweightExecutor,
297-
release -> {
298-
if (release == null) {
299-
LogWrapper.v(TAG, "New release not found.");
302+
signedIn -> {
303+
if (!signedIn) {
300304
return getErrorUpdateTask(
301305
new FirebaseAppDistributionException(
302-
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE));
303-
}
304-
if (release.getDownloadUrl() == null) {
305-
LogWrapper.v(TAG, "Download failed to execute.");
306-
return getErrorUpdateTask(
307-
new FirebaseAppDistributionException(
308-
ErrorMessages.DOWNLOAD_URL_NOT_FOUND,
309-
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
310-
}
311-
312-
if (release.getBinaryType() == BinaryType.AAB) {
313-
return aabUpdater.updateAab(release);
314-
} else {
315-
return apkUpdater.updateApk(release, showDownloadInNotificationManager);
306+
"Tester is not signed in", AUTHENTICATION_FAILURE));
316307
}
308+
return TaskUtils.onSuccessUpdateTask(
309+
cachedNewRelease.get(),
310+
lightweightExecutor,
311+
release -> {
312+
if (release == null) {
313+
LogWrapper.v(TAG, "New release not found.");
314+
return getErrorUpdateTask(
315+
new FirebaseAppDistributionException(
316+
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE));
317+
}
318+
if (release.getDownloadUrl() == null) {
319+
LogWrapper.v(TAG, "Download failed to execute.");
320+
return getErrorUpdateTask(
321+
new FirebaseAppDistributionException(
322+
ErrorMessages.DOWNLOAD_URL_NOT_FOUND,
323+
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
324+
}
325+
326+
if (release.getBinaryType() == BinaryType.AAB) {
327+
return aabUpdater.updateAab(release);
328+
} else {
329+
return apkUpdater.updateApk(release, showDownloadInNotificationManager);
330+
}
331+
});
317332
});
318333
}
319334

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/FirebaseAppDistributionRegistrar.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,15 @@ private FirebaseAppDistribution buildFirebaseAppDistribution(
110110
firebaseInstallationsApiProvider,
111111
new TesterApiHttpClient(firebaseApp),
112112
blockingExecutor);
113-
SignInStorage signInStorage = new SignInStorage(context);
113+
SignInStorage signInStorage = new SignInStorage(context, backgroundExecutor);
114114
FirebaseAppDistributionLifecycleNotifier lifecycleNotifier =
115115
FirebaseAppDistributionLifecycleNotifier.getInstance();
116116
ReleaseIdentifier releaseIdentifier = new ReleaseIdentifier(firebaseApp, testerApiClient);
117117
FirebaseAppDistribution appDistribution =
118118
new FirebaseAppDistributionImpl(
119119
firebaseApp,
120-
new TesterSignInManager(firebaseApp, firebaseInstallationsApiProvider, signInStorage),
120+
new TesterSignInManager(
121+
firebaseApp, firebaseInstallationsApiProvider, signInStorage, blockingExecutor),
121122
new NewReleaseFetcher(
122123
firebaseApp.getApplicationContext(), testerApiClient, releaseIdentifier),
123124
new ApkUpdater(firebaseApp, new ApkInstaller(), blockingExecutor),

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/SignInStorage.java

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,55 @@
1616

1717
import android.content.Context;
1818
import android.content.SharedPreferences;
19-
import com.google.firebase.components.Lazy;
19+
import com.google.android.gms.tasks.Task;
20+
import com.google.android.gms.tasks.TaskCompletionSource;
21+
import com.google.android.gms.tasks.Tasks;
22+
import com.google.firebase.annotations.concurrent.Background;
23+
import java.util.concurrent.Executor;
2024

2125
/** Class that handles storage for App Distribution SignIn persistence. */
2226
class SignInStorage {
2327

2428
private static final String SIGNIN_PREFERENCES_NAME = "FirebaseAppDistributionSignInStorage";
2529
private static final String SIGNIN_TAG = "firebase_app_distribution_signin";
2630

27-
private final Lazy<SharedPreferences> signInSharedPreferences;
28-
29-
SignInStorage(Context applicationContext) {
30-
this.signInSharedPreferences =
31-
new Lazy(
32-
() ->
33-
// TODO(lkellogg): This constructs a SharedPreferences object, which touches disk
34-
// and therefore should be run on a @Background executor. This could ideally be
35-
// done by a new shared component.
36-
applicationContext.getSharedPreferences(
37-
SIGNIN_PREFERENCES_NAME, Context.MODE_PRIVATE));
31+
private final Context applicationContext;
32+
private final Executor backgroundExecutor;
33+
34+
SignInStorage(Context applicationContext, @Background Executor backgroundExecutor) {
35+
this.applicationContext = applicationContext;
36+
this.backgroundExecutor = backgroundExecutor;
37+
}
38+
39+
Task<Void> setSignInStatus(boolean testerSignedIn) {
40+
return getSharedPreferences()
41+
.onSuccessTask(
42+
backgroundExecutor,
43+
sharedPreferences -> {
44+
sharedPreferences.edit().putBoolean(SIGNIN_TAG, testerSignedIn).apply();
45+
return null;
46+
});
47+
}
48+
49+
Task<Boolean> getSignInStatus() {
50+
return getSharedPreferences()
51+
.onSuccessTask(
52+
backgroundExecutor,
53+
sharedPreferences -> Tasks.forResult(sharedPreferences.getBoolean(SIGNIN_TAG, false)));
54+
}
55+
56+
boolean getSignInStatusBlocking() {
57+
return getSharedPreferencesBlocking().getBoolean(SIGNIN_TAG, false);
3858
}
3959

40-
void setSignInStatus(boolean testerSignedIn) {
41-
this.signInSharedPreferences.get().edit().putBoolean(SIGNIN_TAG, testerSignedIn).apply();
60+
private Task<SharedPreferences> getSharedPreferences() {
61+
TaskCompletionSource<SharedPreferences> taskCompletionSource = new TaskCompletionSource<>();
62+
backgroundExecutor.execute(
63+
() -> taskCompletionSource.setResult(getSharedPreferencesBlocking()));
64+
return taskCompletionSource.getTask();
4265
}
4366

44-
boolean getSignInStatus() {
45-
return signInSharedPreferences.get().getBoolean(SIGNIN_TAG, false);
67+
private SharedPreferences getSharedPreferencesBlocking() {
68+
return applicationContext.getSharedPreferences(SIGNIN_PREFERENCES_NAME, Context.MODE_PRIVATE);
4669
}
4770
}

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/TesterSignInManager.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@
3333
import com.google.android.gms.tasks.TaskCompletionSource;
3434
import com.google.android.gms.tasks.Tasks;
3535
import com.google.firebase.FirebaseApp;
36+
import com.google.firebase.annotations.concurrent.Blocking;
37+
import com.google.firebase.annotations.concurrent.Lightweight;
3638
import com.google.firebase.appdistribution.FirebaseAppDistributionException;
3739
import com.google.firebase.appdistribution.FirebaseAppDistributionException.Status;
3840
import com.google.firebase.inject.Provider;
3941
import com.google.firebase.installations.FirebaseInstallationsApi;
4042
import java.util.List;
43+
import java.util.concurrent.Executor;
4144

4245
/** Class that handles signing in the tester. */
4346
class TesterSignInManager {
@@ -51,6 +54,9 @@ class TesterSignInManager {
5154
private final SignInStorage signInStorage;
5255
private final FirebaseAppDistributionLifecycleNotifier lifecycleNotifier;
5356

57+
// TODO: remove synchronized block usage in this class so this does not have to be blocking
58+
@Blocking private final Executor blockingExecutor;
59+
5460
private final Object signInTaskLock = new Object();
5561

5662
@GuardedBy("signInTaskLock")
@@ -62,24 +68,28 @@ class TesterSignInManager {
6268
TesterSignInManager(
6369
@NonNull FirebaseApp firebaseApp,
6470
@NonNull Provider<FirebaseInstallationsApi> firebaseInstallationsApiProvider,
65-
@NonNull final SignInStorage signInStorage) {
71+
@NonNull final SignInStorage signInStorage,
72+
@Blocking Executor blockingExecutor) {
6673
this(
6774
firebaseApp,
6875
firebaseInstallationsApiProvider,
6976
signInStorage,
70-
FirebaseAppDistributionLifecycleNotifier.getInstance());
77+
FirebaseAppDistributionLifecycleNotifier.getInstance(),
78+
blockingExecutor);
7179
}
7280

7381
@VisibleForTesting
7482
TesterSignInManager(
7583
@NonNull FirebaseApp firebaseApp,
7684
@NonNull Provider<FirebaseInstallationsApi> firebaseInstallationsApiProvider,
7785
@NonNull final SignInStorage signInStorage,
78-
@NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier) {
86+
@NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier,
87+
@Lightweight Executor blockingExecutor) {
7988
this.firebaseApp = firebaseApp;
8089
this.firebaseInstallationsApiProvider = firebaseInstallationsApiProvider;
8190
this.signInStorage = signInStorage;
8291
this.lifecycleNotifier = lifecycleNotifier;
92+
this.blockingExecutor = blockingExecutor;
8393

8494
lifecycleNotifier.addOnActivityCreatedListener(this::onActivityCreated);
8595
lifecycleNotifier.addOnActivityResumedListener(this::onActivityResumed);
@@ -119,11 +129,22 @@ void onActivityResumed(Activity activity) {
119129
@SuppressLint("TaskMainThread")
120130
@NonNull
121131
public Task<Void> signInTester() {
122-
if (signInStorage.getSignInStatus()) {
123-
LogWrapper.v(TAG, "Tester is already signed in.");
124-
return Tasks.forResult(null);
125-
}
132+
return signInStorage
133+
.getSignInStatus()
134+
.onSuccessTask(
135+
blockingExecutor,
136+
signedIn -> {
137+
if (signedIn) {
138+
LogWrapper.v(TAG, "Tester is already signed in.");
139+
return Tasks.forResult(null);
140+
}
141+
return doSignInTester();
142+
});
143+
}
126144

145+
// TODO(b/261014422): Use an explicit executor in continuations.
146+
@SuppressLint("TaskMainThread")
147+
private Task<Void> doSignInTester() {
127148
synchronized (signInTaskLock) {
128149
if (signInTaskCompletionSource != null
129150
&& !signInTaskCompletionSource.getTask().isComplete()) {
@@ -138,10 +159,15 @@ public Task<Void> signInTester() {
138159
.get()
139160
.getId()
140161
.addOnFailureListener(
162+
blockingExecutor,
141163
handleTaskFailure(ErrorMessages.AUTHENTICATION_ERROR, Status.AUTHENTICATION_FAILURE))
142-
.onSuccessTask(this::getForegroundActivityAndOpenSignInFlow)
143-
// Catch any unexpected failures to be safe.
144-
.addOnFailureListener(handleTaskFailure(ErrorMessages.UNKNOWN_ERROR, Status.UNKNOWN));
164+
.addOnSuccessListener(
165+
blockingExecutor,
166+
fid ->
167+
getForegroundActivityAndOpenSignInFlow(fid)
168+
.addOnFailureListener(
169+
blockingExecutor,
170+
handleTaskFailure(ErrorMessages.UNKNOWN_ERROR, Status.UNKNOWN)));
145171

146172
return signInTaskCompletionSource.getTask();
147173
}

0 commit comments

Comments
 (0)