Skip to content

Commit

Permalink
refactor: request rate reduction (#646)
Browse files Browse the repository at this point in the history
* refactor: reduce off screen page limit to reduce preemptive loading

* refactor: add last sync marker for syncing local - remote user state

* chore: minor refactoring and optimisations

* chore: add documentation to public APIs

* refactor: add `getModel` to ActivityBase
  • Loading branch information
wax911 authored Mar 25, 2024
1 parent 409d43e commit 3a20994
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 46 deletions.
2 changes: 1 addition & 1 deletion app/src/main/assets/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Read the **FAQ** for issues regarding NSFW and notifications. Goto **Options** -
## What's Changed

### Enhancements
- Reduce the number of items loaded per page, hopefully will result in less request timeouts
- Additional optimizations to reduce the number of requests should result in slightly lower rate limit issues

### Current Issues
- Gifs may show artifacts if more than one is playing at a given moment (with experimental markdown support in settings)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;

import com.miguelcatalan.materialsearchview.MaterialSearchView;
Expand Down Expand Up @@ -73,7 +74,7 @@ public abstract class ActivityBase<M, P extends CommonPresenter> extends AppComp

protected long id;

protected int offScreenLimit = 3;
protected int offScreenLimit = 2;
protected boolean disableNavigationStyle;
protected static final int REQUEST_PERMISSION = 102;

Expand Down Expand Up @@ -366,7 +367,8 @@ protected ViewModelBase<M> getViewModel() {
@SuppressWarnings("unchecked")
protected void setViewModel(boolean stateSupported) {
if(viewModel == null) {
viewModel = ViewModelProviders.of(this).get(ViewModelBase.class);
ViewModelProvider provider = new ViewModelProvider(this);
viewModel = provider.get(ViewModelBase.class);
viewModel.setContext(this);
if(!viewModel.getModel().hasActiveObservers())
viewModel.getModel().observe(this, this);
Expand All @@ -375,6 +377,11 @@ protected void setViewModel(boolean stateSupported) {
}
}

@Nullable
protected M getModel() {
return viewModel != null ? viewModel.snapshot() : null;
}

/**
* Called when the model state is changed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;

import com.annimon.stream.IntPair;
Expand Down Expand Up @@ -229,7 +230,8 @@ public ViewModelBase<VM> getViewModel() {
@SuppressWarnings("unchecked")
protected void setViewModel(boolean stateSupported) {
if(viewModel == null) {
viewModel = ViewModelProviders.of(this).get(ViewModelBase.class);
ViewModelProvider provider = new ViewModelProvider(this);
viewModel = provider.get(ViewModelBase.class);
viewModel.setContext(getContext());
if(!viewModel.getModel().hasActiveObservers())
viewModel.getModel().observe(this, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class AvatarIndicatorView : FrameLayout, CustomView, View.OnClickListener, BaseC
get() = presenter.database?.currentUser

private lateinit var binding: WidgetAvatarIndicatorBinding
private var mLastSynced: Long = 0

override fun onInit() {
binding = WidgetAvatarIndicatorBinding.inflate(context.getLayoutInflater(), this, true)
Expand Down Expand Up @@ -76,8 +75,6 @@ class AvatarIndicatorView : FrameLayout, CustomView, View.OnClickListener, BaseC
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
override fun onModelChanged(consumer: BaseConsumer<User>) {
if (consumer.requestMode == KeyUtil.USER_CURRENT_REQ) {
if (DateUtil.timeDifferenceSatisfied(KeyUtil.TIME_UNIT_MINUTES, mLastSynced, 15))
mLastSynced = System.currentTimeMillis()
checkLastSyncTime()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class ViewModelBase<T>: ViewModel(), RetroCallback<T> {

val params = Bundle()

/**
* @return A live data snapshot value [T]? at the time this function is invoked
*/
fun snapshot(): T? = model.value

fun setContext(context: Context?) {
context?.apply {
emptyMessage = getString(R.string.layout_empty_response)
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/com/mxt/anitrend/presenter/base/BasePresenter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ open class BasePresenter(context: Context?) : CommonPresenter(context) {
private var favouriteYears: List<String>? = null
private var favouriteFormats: List<String>? = null

/**
* Avoids running a delegate if the specified interval time has not elapsed since the last
* call of this function
*
* @param intervalInMinutes time interval in minutes
* @param action delegate to invoke if the the time interval has elapsed since the last run
*
* @see KeyUtil.TIME_UNIT_MINUTES
*/
inline fun updateUserLastSyncTimeStampIf(intervalInMinutes: Int = 15, action: () -> Unit) {
val lastSyncedAt = settings.lastUserSyncTime
if (DateUtil.timeDifferenceSatisfied(KeyUtil.TIME_UNIT_MINUTES, lastSyncedAt, intervalInMinutes)) {
action()
settings.lastUserSyncTime = System.currentTimeMillis()
}
}

@IdRes
fun getNavigationItem(): Int {
return when (settings.startupPage) {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/mxt/anitrend/util/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ class Settings(
}
}

var lastUserSyncTime: Long
get() = getLong(_lastUserSyncTime, 0)
set(value) {
edit {
putLong(_lastUserSyncTime, value)
}
}

fun saveSeasonYear(year: Int) {
edit {
putInt(KeyUtil.arg_seasonYear, year)
Expand Down Expand Up @@ -391,5 +399,6 @@ class Settings(
private const val _reviewSort = "_reviewSort"
private const val _staffSort = "_staffSort"
private const val _experimentalMarkdown = "_experimentalMarkdown"
private const val _lastUserSyncTime = "_lastUserSyncTime"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ private const val HTTP_LIMIT_REACHED = 429

private const val TAG = "ErrorUtil"
private const val Retry_After = "Retry-After"
private const val RateLimit_Limit = "X-RateLimit-Limit"
private const val RateLimit_Remaining = "X-RateLimit-Remaining"

/**
* Converts the response error response into an object.
Expand All @@ -28,16 +26,18 @@ fun Response<*>?.apiError(): String {
val headers = headers()
val errors = getError()
return if (code() != HTTP_LIMIT_REACHED) {
errors?.firstOrNull()?.message ?: "Unable to provide information regarding error!"
} else
"${headers.get(RateLimit_Remaining)} of ${headers.get(RateLimit_Limit)} requests remaining, please retry after ${headers.get(Retry_After)} seconds"
errors?.firstOrNull()?.message ?: "Unexpected HTTP/${code()} from server"
} else {
val waitPeriod = headers.get(Retry_After) ?: 60
"Too many requests, please retry after $waitPeriod seconds"
}
}
} catch (ex: Exception) {
ex.printStackTrace()
Timber.tag(TAG).e(ex)
return "Unexpected error encountered"
return "Unable to recover from encountered error"
}

return "Unable to provide information regarding error!"
return "Unable to provide information regarding error"
}

Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ public class CharacterActivity extends ActivityBase<CharacterBase, BasePresenter
protected @BindView(R.id.smart_tab) SmartTabLayout smartTabLayout;
protected @BindView(R.id.coordinator) CoordinatorLayout coordinatorLayout;

private CharacterBase model;

private FavouriteToolbarWidget favouriteWidget;

@Override
Expand Down Expand Up @@ -71,6 +69,7 @@ public boolean onCreateOptionsMenu(Menu menu) {
if(isAuth) {
MenuItem favouriteMenuItem = menu.findItem(R.id.action_favourite);
favouriteWidget = (FavouriteToolbarWidget) favouriteMenuItem.getActionView();
CharacterBase model = getModel();
if(model != null)
favouriteWidget.setModel(model);
}
Expand All @@ -79,6 +78,7 @@ public boolean onCreateOptionsMenu(Menu menu) {

@Override
public boolean onOptionsItemSelected(MenuItem item) {
CharacterBase model = getModel();
if(model != null) {
switch (item.getItemId()) {
case R.id.action_share:
Expand Down Expand Up @@ -111,14 +111,15 @@ protected void onActivityReady() {
@Override
protected void onResume() {
super.onResume();
if(model == null)
if(getModel() == null)
makeRequest();
else
updateUI();
}

@Override
protected void updateUI() {
CharacterBase model = getModel();
if(model != null)
if(favouriteWidget != null)
favouriteWidget.setModel(model);
Expand All @@ -141,7 +142,6 @@ protected void makeRequest() {
@Override
public void onChanged(@Nullable CharacterBase model) {
super.onChanged(model);
this.model = model;
updateUI();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
public class MediaActivity extends ActivityBase<MediaBase, MediaPresenter> implements View.OnClickListener {

private ActivitySeriesBinding binding;
private MediaBase model;

private @KeyUtil.MediaType String mediaType;

Expand Down Expand Up @@ -87,6 +86,7 @@ public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.media_base_menu, menu);
menu.findItem(R.id.action_favourite).setVisible(isAuth);

MediaBase model = getModel();
malMenuItem = menu.findItem(R.id.action_mal);
malMenuItem.setVisible(model != null && model.getIdMal() > 0);

Expand All @@ -104,6 +104,7 @@ public boolean onCreateOptionsMenu(Menu menu) {

@Override
public boolean onOptionsItemSelected(MenuItem item) {
MediaBase model = getModel();
if(model != null) {
switch (item.getItemId()) {
case R.id.action_manage:
Expand Down Expand Up @@ -153,14 +154,15 @@ protected void onActivityReady() {
@Override
protected void onResume() {
super.onResume();
if(model == null)
if(getModel() == null)
makeRequest();
else
updateUI();
}

@Override
protected void updateUI() {
MediaBase model = getModel();
if(model != null) {
binding.setModel(model);
binding.setOnClickListener(this);
Expand Down Expand Up @@ -196,15 +198,17 @@ protected void makeRequest() {
@Override
public void onChanged(@Nullable MediaBase model) {
super.onChanged(model);
this.model = model;
updateUI();
}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.series_banner:
CompatUtil.INSTANCE.imagePreview(view, model.getBannerImage(), R.string.image_preview_error_series_banner);
MediaBase model = getModel();
if (model != null) {
CompatUtil.INSTANCE.imagePreview(view, model.getBannerImage(), R.string.image_preview_error_series_banner);
}
break;
}
}
Expand All @@ -217,6 +221,7 @@ protected void onDestroy() {
}

private void setMenuItemIcons() {
MediaBase model = getModel();
if (model != null) {
if (model.getMediaListEntry() != null && manageMenuItem != null)
manageMenuItem.setIcon(CompatUtil.INSTANCE.getDrawable(this, R.drawable.ic_mode_edit_white_24dp));
Expand All @@ -226,6 +231,7 @@ private void setMenuItemIcons() {
}

private void setFavouriteWidgetMenuItemIcon() {
MediaBase model = getModel();
if(model != null && favouriteWidget != null)
favouriteWidget.setModel(model);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected void onActivityReady() {
@Override
protected void updateUI() {
viewPager.setAdapter(pageAdapter);
viewPager.setOffscreenPageLimit(offScreenLimit + 2);
viewPager.setOffscreenPageLimit(offScreenLimit);
smartTabLayout.setViewPager(viewPager);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.mxt.anitrend.base.custom.consumer.BaseConsumer;
import com.mxt.anitrend.base.custom.view.image.WideImageView;
import com.mxt.anitrend.databinding.ActivityProfileBinding;
import com.mxt.anitrend.model.entity.base.StaffBase;
import com.mxt.anitrend.model.entity.base.UserBase;
import com.mxt.anitrend.presenter.base.BasePresenter;
import com.mxt.anitrend.util.CompatUtil;
Expand Down Expand Up @@ -47,7 +48,6 @@ public class ProfileActivity extends ActivityBase<UserBase, BasePresenter> imple

private ActivityProfileBinding binding;
private String userName;
private UserBase model;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
Expand Down Expand Up @@ -84,7 +84,7 @@ protected void onPostCreate(@Nullable Bundle savedInstanceState) {
@Override
protected void onPostResume() {
super.onPostResume();
if(model == null)
if(getModel() == null)
onActivityReady();
else
updateUI();
Expand All @@ -100,6 +100,7 @@ public boolean onCreateOptionsMenu(Menu menu) {

@Override
public boolean onOptionsItemSelected(MenuItem item) {
UserBase model = getModel();
switch (item.getItemId()) {
case R.id.action_notification:
startActivity(new Intent(ProfileActivity.this, NotificationActivity.class));
Expand Down Expand Up @@ -150,6 +151,7 @@ protected void onActivityReady() {
protected void updateUI() {
binding.setOnClickListener(this);
binding.profileStatsWidget.setParams(getIntent().getExtras());
UserBase model = getModel();
WideImageView.setImage(binding.profileBanner, model.getBannerImage());
if(getPresenter().isCurrentUser(model.getId())) {
new TutorialUtil().setContext(this)
Expand Down Expand Up @@ -191,7 +193,6 @@ public void onChanged(@Nullable UserBase model) {
super.onChanged(model);
if(model != null) {
this.id = model.getId();
this.model = model;
updateUI();
} else
NotifyUtil.INSTANCE.createAlerter(this, R.string.text_user_model, R.string.layout_empty_response, R.drawable.ic_warning_white_18dp, R.color.colorStateRed);
Expand All @@ -201,7 +202,10 @@ public void onChanged(@Nullable UserBase model) {
public void onClick(View view) {
switch (view.getId()) {
case R.id.profile_banner:
CompatUtil.INSTANCE.imagePreview(view, model.getBannerImage(), R.string.image_preview_error_profile_banner);
UserBase model = getModel();
if (model != null) {
CompatUtil.INSTANCE.imagePreview(view, model.getBannerImage(), R.string.image_preview_error_profile_banner);
}
break;
}
}
Expand Down
Loading

0 comments on commit 3a20994

Please sign in to comment.