rate limit InstallApp install/delete notifications to 1000ms

InstallAppProviderService now processes install and delete events one at a
time, where InstalledAppCacheUpdater made a batch of changes which it ran
all at once.  This means that InstallAppProviderService will send out a
flood of notifications when first initializing, since it will index every
single installed app and send a notification for each one.  This makes the
GUI lock up. This commit puts a rate limit on those notifications if they
start coming fast.  They are limited to one per second.
This commit is contained in:
Hans-Christoph Steiner 2016-05-27 16:33:49 +02:00
parent d734e584f6
commit 906a26414a

View File

@ -14,6 +14,9 @@ import org.fdroid.fdroid.Utils;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/** /**
* Handles all updates to {@link InstalledAppProvider}, whether checking the contents * Handles all updates to {@link InstalledAppProvider}, whether checking the contents
@ -36,6 +39,9 @@ public class InstalledAppProviderService extends IntentService {
private static final String EXTRA_PACKAGE_INFO = "org.fdroid.fdroid.data.extra.PACKAGE_INFO"; private static final String EXTRA_PACKAGE_INFO = "org.fdroid.fdroid.data.extra.PACKAGE_INFO";
private ScheduledExecutorService worker;
private boolean notifyChangeNeedsSending;
public InstalledAppProviderService() { public InstalledAppProviderService() {
super("InstalledAppProviderService"); super("InstalledAppProviderService");
} }
@ -125,10 +131,7 @@ public class InstalledAppProviderService extends IntentService {
} else if (ACTION_DELETE.equals(action)) { } else if (ACTION_DELETE.equals(action)) {
deleteAppFromDb(this, packageName); deleteAppFromDb(this, packageName);
} }
notifyChange();
Utils.debugLog(TAG, "Notifying content providers (so they can update the relevant views).");
getContentResolver().notifyChange(AppProvider.getContentUri(), null);
getContentResolver().notifyChange(ApkProvider.getContentUri(), null);
} }
} }
@ -160,4 +163,32 @@ public class InstalledAppProviderService extends IntentService {
Uri uri = InstalledAppProvider.getAppUri(packageName); Uri uri = InstalledAppProvider.getAppUri(packageName);
context.getContentResolver().delete(uri, null, null); 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);
}
}
} }