-
Notifications
You must be signed in to change notification settings - Fork 241
Move bookmark database operations off the UI thread #1665
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
base: master
Are you sure you want to change the base?
Changes from all commits
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 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,10 +5,12 @@ | |||||||||||||||||||
| import android.net.Uri; | ||||||||||||||||||||
| import android.os.Bundle; | ||||||||||||||||||||
| import androidx.annotation.Nullable; | ||||||||||||||||||||
| import androidx.core.util.Pair; | ||||||||||||||||||||
| import androidx.fragment.app.Fragment; | ||||||||||||||||||||
| import android.view.Menu; | ||||||||||||||||||||
| import android.view.MenuInflater; | ||||||||||||||||||||
| import android.view.MenuItem; | ||||||||||||||||||||
| import android.widget.Toast; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import com.gh4a.BaseFragmentPagerActivity; | ||||||||||||||||||||
| import com.gh4a.R; | ||||||||||||||||||||
|
|
@@ -18,11 +20,14 @@ | |||||||||||||||||||
| import com.gh4a.fragment.UserFragment; | ||||||||||||||||||||
| import com.gh4a.utils.ApiHelpers; | ||||||||||||||||||||
| import com.gh4a.utils.IntentUtils; | ||||||||||||||||||||
| import com.gh4a.utils.RxUtils; | ||||||||||||||||||||
| import com.gh4a.utils.StringUtils; | ||||||||||||||||||||
| import com.meisolsson.githubsdk.model.User; | ||||||||||||||||||||
| import com.meisolsson.githubsdk.model.UserType; | ||||||||||||||||||||
| import com.meisolsson.githubsdk.service.users.UserService; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import io.reactivex.Single; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| public class UserActivity extends BaseFragmentPagerActivity { | ||||||||||||||||||||
| public static Intent makeIntent(Context context, User user) { | ||||||||||||||||||||
| if (user != null && user.type() == UserType.Mannequin) { | ||||||||||||||||||||
|
|
@@ -45,6 +50,7 @@ public static Intent makeIntent(Context context, String login) { | |||||||||||||||||||
| private String mUserLogin; | ||||||||||||||||||||
| private User mUser; | ||||||||||||||||||||
| private UserFragment mUserFragment; | ||||||||||||||||||||
| private Boolean mIsBookmarked; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private static final int ID_LOADER_USER = 0; | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -57,6 +63,7 @@ protected void onCreate(Bundle savedInstanceState) { | |||||||||||||||||||
| super.onCreate(savedInstanceState); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| setContentShown(false); | ||||||||||||||||||||
| refreshBookmarkState(); | ||||||||||||||||||||
| loadUser(false); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -83,6 +90,7 @@ protected int[] getTabTitleResIds() { | |||||||||||||||||||
| @Override | ||||||||||||||||||||
| public void onRefresh() { | ||||||||||||||||||||
| mUser = null; | ||||||||||||||||||||
| mIsBookmarked = null; | ||||||||||||||||||||
| setContentShown(false); | ||||||||||||||||||||
| invalidateTabs(); | ||||||||||||||||||||
| invalidateOptionsMenu(); | ||||||||||||||||||||
|
|
@@ -129,10 +137,8 @@ public boolean onCreateOptionsMenu(Menu menu) { | |||||||||||||||||||
| public boolean onPrepareOptionsMenu(Menu menu) { | ||||||||||||||||||||
| MenuItem bookmarkAction = menu.findItem(R.id.bookmark); | ||||||||||||||||||||
| if (bookmarkAction != null) { | ||||||||||||||||||||
| String url = "https://github.com/" + mUserLogin; | ||||||||||||||||||||
| bookmarkAction.setTitle(BookmarksProvider.hasBookmarked(this, url) | ||||||||||||||||||||
| ? R.string.remove_bookmark | ||||||||||||||||||||
| : R.string.bookmark); | ||||||||||||||||||||
| bookmarkAction.setTitle(mIsBookmarked != null && mIsBookmarked | ||||||||||||||||||||
| ? R.string.remove_bookmark : R.string.bookmark); | ||||||||||||||||||||
| bookmarkAction.setVisible(mUser != null); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -160,19 +166,47 @@ public boolean onOptionsItemSelected(MenuItem item) { | |||||||||||||||||||
| return true; | ||||||||||||||||||||
| case R.id.bookmark: { | ||||||||||||||||||||
| String urlString = url.toString(); | ||||||||||||||||||||
| if (BookmarksProvider.hasBookmarked(this, urlString)) { | ||||||||||||||||||||
| BookmarksProvider.removeBookmark(this, urlString); | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| BookmarksProvider.saveBookmark(this, mUserLogin, | ||||||||||||||||||||
| BookmarksProvider.Columns.TYPE_USER, | ||||||||||||||||||||
| urlString, mUser.name(), true); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| String userName = mUser != null ? mUser.name() : null; | ||||||||||||||||||||
| registerTemporarySubscription(Single.fromCallable(() -> { | ||||||||||||||||||||
| boolean hasBookmarked = BookmarksProvider.hasBookmarked(this, urlString); | ||||||||||||||||||||
| boolean success = hasBookmarked | ||||||||||||||||||||
| ? BookmarksProvider.removeBookmarkInternal(this, urlString) | ||||||||||||||||||||
| : BookmarksProvider.saveBookmarkInternal(this, mUserLogin, | ||||||||||||||||||||
| BookmarksProvider.Columns.TYPE_USER, urlString, userName); | ||||||||||||||||||||
| return new Pair<>(success, !hasBookmarked); | ||||||||||||||||||||
| }).compose(RxUtils::doInBackground).subscribe(result -> { | ||||||||||||||||||||
| if (result.first) { | ||||||||||||||||||||
| Toast.makeText(this, | ||||||||||||||||||||
| result.second ? R.string.bookmark_saved : R.string.bookmark_removed, | ||||||||||||||||||||
| result.second ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT) | ||||||||||||||||||||
| .show(); | ||||||||||||||||||||
| mIsBookmarked = result.second; | ||||||||||||||||||||
| invalidateOptionsMenu(); | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| refreshBookmarkState(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }, e -> handleActionFailure("Toggling bookmark failed", e))); | ||||||||||||||||||||
| return true; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return super.onOptionsItemSelected(item); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private void refreshBookmarkState() { | ||||||||||||||||||||
| String url = mUserLogin != null ? "https://github.com/" + mUserLogin : null; | ||||||||||||||||||||
| if (url == null) { | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
Comment on lines
+196
to
+200
|
||||||||||||||||||||
| String url = mUserLogin != null ? "https://github.com/" + mUserLogin : null; | |
| if (url == null) { | |
| return; | |
| } | |
| if (mUserLogin == null) { | |
| return; | |
| } | |
| String url = IntentUtils.createBaseUriForUser(mUserLogin).build().toString(); |
Copilot
AI
Apr 22, 2026
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.
refreshBookmarkState() can run concurrently with the toggle operation (and with other refresh calls), and whichever query finishes last will overwrite mIsBookmarked. This can leave the menu title/icon in the wrong state (e.g., initial refresh returns "not bookmarked" after a successful save). Consider keeping a dedicated Disposable for bookmark-state loads and disposing any in-flight request before starting a new one, or otherwise sequencing requests so only the latest result updates mIsBookmarked (e.g., via a request counter / switchMapSingle).
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.
Similar to
UserActivity,refreshBookmarkState()and the togglefromCallablecan be in-flight at the same time, and out-of-order completion can overwritemIsBookmarkedwith stale data. Consider cancelling any previous bookmark-state request before starting a new one, or otherwise ensuring only the most recent query result updatesmIsBookmarked.