Replace rate limiting code with RX.

Now that we have RX as a dependency, it can be used as a nice concise way to
achieve certain tasks. Rate limiting is one thing it does well - via the
`debounce` mechanism:

  http://reactivex.io/documentation/operators/debounce.html

The semantics of this code is the same as before, limiting content change notifications
to one per second.
This commit is contained in:
Peter Serwylo 2016-06-02 14:24:04 +10:00
parent fa7f57a18a
commit 371312ef65

View File

@ -14,10 +14,12 @@ import org.fdroid.fdroid.Utils;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
/**
* Handles all updates to {@link InstalledAppProvider}, whether checking the contents
* versus what Android says is installed, or processing {@link Intent}s that come
@ -39,13 +41,34 @@ public class InstalledAppProviderService extends IntentService {
private static final String EXTRA_PACKAGE_INFO = "org.fdroid.fdroid.data.extra.PACKAGE_INFO";
private ScheduledExecutorService worker;
private boolean notifyChangeNeedsSending;
/**
* This is for notifing the users of this {@link android.content.ContentProvider}
* that the contents has changed. Since {@link Intent}s can come in slow
* or fast, and this can trigger a lot of UI updates, the actual
* notifications are rate limited to one per second.
*/
private PublishSubject<Void> notifyEvents;
public InstalledAppProviderService() {
super("InstalledAppProviderService");
}
@Override
public void onCreate() {
super.onCreate();
notifyEvents = PublishSubject.create();
notifyEvents.debounce(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.subscribe(new Action1<Void>() {
@Override
public void call(Void voidArg) {
Utils.debugLog(TAG, "Notifying content providers (so they can update the relevant views).");
getContentResolver().notifyChange(AppProvider.getContentUri(), null);
getContentResolver().notifyChange(ApkProvider.getContentUri(), null);
}
});
}
/**
* Inserts an app into {@link InstalledAppProvider} based on a {@code package:} {@link Uri}.
* This has no checks for whether it is inserting an exact duplicate, whatever is provided
@ -134,7 +157,7 @@ public class InstalledAppProviderService extends IntentService {
} else if (ACTION_DELETE.equals(action)) {
deleteAppFromDb(this, packageName);
}
notifyChange();
notifyEvents.onNext(null);
}
}
@ -172,32 +195,4 @@ public class InstalledAppProviderService extends IntentService {
Uri uri = InstalledAppProvider.getAppUri(packageName);
context.getContentResolver().delete(uri, null, null);
}
/**
* This notifies the users of this {@link android.content.ContentProvider}
* that the contents has changed. Since {@link Intent}s can come in slow
* or fast, and this can trigger a lot of UI updates, the actual
* notifications are rate limited to one per second.
*/
private void notifyChange() {
notifyChangeNeedsSending = true;
Runnable task = new Runnable() {
@Override
public void run() {
if (notifyChangeNeedsSending) {
Utils.debugLog(TAG, "Notifying content providers (so they can update the relevant views).");
getContentResolver().notifyChange(AppProvider.getContentUri(), null);
getContentResolver().notifyChange(ApkProvider.getContentUri(), null);
notifyChangeNeedsSending = false;
} else {
worker.shutdown();
worker = null;
}
}
};
if (worker == null || worker.isShutdown()) {
worker = Executors.newSingleThreadScheduledExecutor();
worker.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);
}
}
}