-
Notifications
You must be signed in to change notification settings - Fork 617
Move SharedPreferences usage to background thread #4480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
0c02f5e
233d913
a4b694f
b2b29f9
b5aea47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,8 +116,6 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution { | |
|
||
@Override | ||
@NonNull | ||
// TODO(b/261014422): Use an explicit executor in continuations. | ||
@SuppressLint("TaskMainThread") | ||
public UpdateTask updateIfNewReleaseAvailable() { | ||
return updateIfNewReleaseAvailableTaskCache.getOrCreateTask( | ||
() -> { | ||
|
@@ -126,9 +124,18 @@ public UpdateTask updateIfNewReleaseAvailable() { | |
remakeUpdateConfirmationDialog = false; | ||
dialogHostActivity = null; | ||
|
||
lifecycleNotifier | ||
.applyToForegroundActivityTask(this::showSignInConfirmationDialog) | ||
.onSuccessTask(lightweightExecutor, unused -> signInTester()) | ||
signInStorage | ||
.getSignInStatus() | ||
.onSuccessTask( | ||
lightweightExecutor, | ||
signedIn -> { | ||
if (signedIn) { | ||
return Tasks.forResult(null); | ||
} | ||
return lifecycleNotifier | ||
.applyToForegroundActivityTask(this::showSignInConfirmationDialog) | ||
.onSuccessTask(lightweightExecutor, unused -> signInTester()); | ||
}) | ||
.onSuccessTask(lightweightExecutor, unused -> checkForNewRelease()) | ||
.continueWithTask( | ||
lightweightExecutor, | ||
|
@@ -178,10 +185,6 @@ public UpdateTask updateIfNewReleaseAvailable() { | |
|
||
@NonNull | ||
private Task<Void> showSignInConfirmationDialog(Activity hostActivity) { | ||
if (isTesterSignedIn()) { | ||
return Tasks.forResult(null); | ||
} | ||
|
||
if (showSignInDialogTask == null || showSignInDialogTask.getTask().isComplete()) { | ||
showSignInDialogTask = new TaskCompletionSource<>(); | ||
} | ||
|
@@ -225,7 +228,7 @@ private Task<Void> showSignInConfirmationDialog(Activity hostActivity) { | |
|
||
@Override | ||
public boolean isTesterSignedIn() { | ||
return signInStorage.getSignInStatus(); | ||
return signInStorage.getSignInStatusBlocking(); | ||
} | ||
|
||
@Override | ||
|
@@ -242,19 +245,12 @@ public void signOutTester() { | |
} | ||
|
||
@Override | ||
@NonNull | ||
// TODO(b/261014422): Use an explicit executor in continuations. | ||
@SuppressLint("TaskMainThread") | ||
public synchronized Task<AppDistributionRelease> checkForNewRelease() { | ||
if (!isTesterSignedIn()) { | ||
return Tasks.forException( | ||
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
} | ||
|
||
return checkForNewReleaseTaskCache.getOrCreateTask( | ||
() -> | ||
newReleaseFetcher | ||
.checkForNewRelease() | ||
assertTesterIsSignedIn() | ||
.onSuccessTask( | ||
lightweightExecutor, unused -> newReleaseFetcher.checkForNewRelease()) | ||
.onSuccessTask(lightweightExecutor, release -> cachedNewRelease.set(release)) | ||
.onSuccessTask( | ||
lightweightExecutor, | ||
|
@@ -274,6 +270,21 @@ public synchronized Task<AppDistributionRelease> checkForNewRelease() { | |
})); | ||
} | ||
|
||
private Task<Void> assertTesterIsSignedIn() { | ||
return signInStorage | ||
.getSignInStatus() | ||
.onSuccessTask( | ||
lightweightExecutor, | ||
signedIn -> { | ||
if (!signedIn) { | ||
return Tasks.forException( | ||
new FirebaseAppDistributionException( | ||
"Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
} | ||
return Tasks.forResult(null); | ||
}); | ||
} | ||
|
||
@Override | ||
@NonNull | ||
public UpdateTask updateApp() { | ||
|
@@ -285,35 +296,39 @@ public UpdateTask updateApp() { | |
* basic configuration and false for advanced configuration. | ||
*/ | ||
private UpdateTask updateApp(boolean showDownloadInNotificationManager) { | ||
if (!isTesterSignedIn()) { | ||
UpdateTaskImpl updateTask = new UpdateTaskImpl(); | ||
updateTask.setException( | ||
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
return updateTask; | ||
} | ||
return TaskUtils.onSuccessUpdateTask( | ||
cachedNewRelease.get(), | ||
signInStorage.getSignInStatus(), | ||
lightweightExecutor, | ||
release -> { | ||
if (release == null) { | ||
LogWrapper.v(TAG, "New release not found."); | ||
signedIn -> { | ||
if (!signedIn) { | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE)); | ||
} | ||
if (release.getDownloadUrl() == null) { | ||
LogWrapper.v(TAG, "Download failed to execute."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.DOWNLOAD_URL_NOT_FOUND, | ||
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE)); | ||
} | ||
|
||
if (release.getBinaryType() == BinaryType.AAB) { | ||
return aabUpdater.updateAab(release); | ||
} else { | ||
return apkUpdater.updateApk(release, showDownloadInNotificationManager); | ||
"Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh actually yes! It's a little tricky because I have to use |
||
} | ||
return TaskUtils.onSuccessUpdateTask( | ||
cachedNewRelease.get(), | ||
lightweightExecutor, | ||
release -> { | ||
if (release == null) { | ||
LogWrapper.v(TAG, "New release not found."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE)); | ||
} | ||
if (release.getDownloadUrl() == null) { | ||
LogWrapper.v(TAG, "Download failed to execute."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.DOWNLOAD_URL_NOT_FOUND, | ||
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE)); | ||
} | ||
|
||
if (release.getBinaryType() == BinaryType.AAB) { | ||
return aabUpdater.updateAab(release); | ||
} else { | ||
return apkUpdater.updateApk(release, showDownloadInNotificationManager); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,32 +16,55 @@ | |
|
||
import android.content.Context; | ||
import android.content.SharedPreferences; | ||
import com.google.firebase.components.Lazy; | ||
import com.google.android.gms.tasks.Task; | ||
import com.google.android.gms.tasks.TaskCompletionSource; | ||
import com.google.android.gms.tasks.Tasks; | ||
import com.google.firebase.annotations.concurrent.Background; | ||
import java.util.concurrent.Executor; | ||
|
||
/** Class that handles storage for App Distribution SignIn persistence. */ | ||
class SignInStorage { | ||
|
||
private static final String SIGNIN_PREFERENCES_NAME = "FirebaseAppDistributionSignInStorage"; | ||
private static final String SIGNIN_TAG = "firebase_app_distribution_signin"; | ||
|
||
private final Lazy<SharedPreferences> signInSharedPreferences; | ||
|
||
SignInStorage(Context applicationContext) { | ||
this.signInSharedPreferences = | ||
new Lazy( | ||
() -> | ||
// TODO(lkellogg): This constructs a SharedPreferences object, which touches disk | ||
// and therefore should be run on a @Background executor. This could ideally be | ||
// done by a new shared component. | ||
applicationContext.getSharedPreferences( | ||
SIGNIN_PREFERENCES_NAME, Context.MODE_PRIVATE)); | ||
private final Context applicationContext; | ||
@Background private final Executor backgroundExecutor; | ||
|
||
SignInStorage(Context applicationContext, @Background Executor backgroundExecutor) { | ||
this.applicationContext = applicationContext; | ||
this.backgroundExecutor = backgroundExecutor; | ||
} | ||
|
||
Task<Void> setSignInStatus(boolean testerSignedIn) { | ||
return getSharedPreferences() | ||
.onSuccessTask( | ||
backgroundExecutor, | ||
sharedPreferences -> { | ||
sharedPreferences.edit().putBoolean(SIGNIN_TAG, testerSignedIn).apply(); | ||
return null; | ||
}); | ||
} | ||
|
||
Task<Boolean> getSignInStatus() { | ||
return getSharedPreferences() | ||
.onSuccessTask( | ||
backgroundExecutor, | ||
sharedPreferences -> Tasks.forResult(sharedPreferences.getBoolean(SIGNIN_TAG, false))); | ||
} | ||
|
||
boolean getSignInStatusBlocking() { | ||
return getSharedPreferencesBlocking().getBoolean(SIGNIN_TAG, false); | ||
} | ||
|
||
void setSignInStatus(boolean testerSignedIn) { | ||
this.signInSharedPreferences.get().edit().putBoolean(SIGNIN_TAG, testerSignedIn).apply(); | ||
private Task<SharedPreferences> getSharedPreferences() { | ||
TaskCompletionSource<SharedPreferences> taskCompletionSource = new TaskCompletionSource<>(); | ||
backgroundExecutor.execute( | ||
() -> taskCompletionSource.setResult(getSharedPreferencesBlocking())); | ||
return taskCompletionSource.getTask(); | ||
} | ||
|
||
boolean getSignInStatus() { | ||
return signInSharedPreferences.get().getBoolean(SIGNIN_TAG, false); | ||
private SharedPreferences getSharedPreferencesBlocking() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add comment explaining that this might involve I/O There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
return applicationContext.getSharedPreferences(SIGNIN_PREFERENCES_NAME, Context.MODE_PRIVATE); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe extract a method named something like
signInIfNeeded()
orensureSignedIn()
which encapsulates thisThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually, this seems like
TesterSignInManager.signInTester()
- could that be called from here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TesterSignInManager.signInTester()
opens the actual sign in screen in the browser.Agreed that it would be nice to extract this though, if only to make this method more readable. I called it
signInWithConfirmationIfNeeded()
.