Drastically improve performance after installs/uninstalls.

- Only reload those apps that actually need reloading, not all of them
- Reloading all the stuff inside DB.App is not necessary - only the install
  information can be changed
This commit is contained in:
Daniel Martí 2013-07-07 00:27:12 +02:00
parent d39047bfea
commit 9c4aa9127f
6 changed files with 99 additions and 20 deletions

View File

@ -510,7 +510,7 @@ public class AppDetails extends ListActivity {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
install();
install(app.id);
}
});
ask_alrt.setNegativeButton(getString(R.string.no),
@ -523,7 +523,7 @@ public class AppDetails extends ListActivity {
AlertDialog alert = ask_alrt.create();
alert.show();
} else
install();
install(app.id);
}
@Override
@ -601,7 +601,7 @@ public class AppDetails extends ListActivity {
// Note that this handles updating as well as installing.
curapk = app.getCurrentVersion();
if (curapk != null)
install();
install(app.id);
return true;
case UNINSTALL:
@ -641,7 +641,7 @@ public class AppDetails extends ListActivity {
}
// Install the version of this app denoted by 'curapk'.
private void install() {
private void install(final String id) {
String ra = null;
try {
@ -666,7 +666,7 @@ public class AppDetails extends ListActivity {
public void onClick(DialogInterface dialog,
int whichButton) {
downloadHandler = new DownloadHandler(curapk,
repoaddress);
repoaddress, id);
}
});
ask_alrt.setNegativeButton(getString(R.string.no),
@ -694,7 +694,7 @@ public class AppDetails extends ListActivity {
alert.show();
return;
}
downloadHandler = new DownloadHandler(curapk, repoaddress);
downloadHandler = new DownloadHandler(curapk, repoaddress, id);
}
private void removeApk(String id) {
@ -708,17 +708,15 @@ public class AppDetails extends ListActivity {
Uri uri = Uri.fromParts("package", pkginfo.packageName, null);
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
startActivityForResult(intent, REQUEST_UNINSTALL);
((FDroidApp) getApplication()).invalidateApps();
}
private void installApk(File file) {
private void installApk(File file, String id) {
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + file.getPath()),
"application/vnd.android.package-archive");
startActivityForResult(intent, REQUEST_INSTALL);
((FDroidApp) getApplication()).invalidateApps();
}
private void launchApk(String id) {
@ -755,8 +753,10 @@ public class AppDetails extends ListActivity {
private Downloader download;
private ProgressDialog pd;
private boolean updating;
private String id;
public DownloadHandler(DB.Apk apk, String repoaddress) {
public DownloadHandler(DB.Apk apk, String repoaddress, String appid) {
id = appid;
download = new Downloader(apk, repoaddress);
download.start();
startUpdates();
@ -794,7 +794,7 @@ public class AppDetails extends ListActivity {
case DONE:
if (pd != null)
pd.dismiss();
installApk(download.localFile());
installApk(download.localFile(), id);
finished = true;
break;
case CANCELLED:

View File

@ -653,6 +653,17 @@ public class DB {
}
}
// Repopulate the details for the given app.
// If 'apkrepo' is non-zero, only apks from that repo are
// populated.
public void repopulateDetails(App app, int apkRepo) {
populateAppDetails(app);
for (Apk apk : app.apks) {
populateApkDetails(apk, apkRepo);
}
}
// Return a list of apps matching the given criteria. Filtering is
// also done based on compatibility and anti-features according to
// the user's current preferences.
@ -784,6 +795,57 @@ public class DB {
return result;
}
public List<App> refreshApps(List<App> apps, List<String> invalidApps) {
List<PackageInfo> installedPackages = mContext.getPackageManager()
.getInstalledPackages(0);
long startTime = System.currentTimeMillis();
for (String appid : invalidApps) {
App app = null;
int index = -1;
for (App oldapp : apps) {
index++;
if (oldapp.id.equals(appid)) {
app = oldapp;
break;
}
}
if (app == null) continue;
PackageInfo installed = null;
for (PackageInfo appInfo : installedPackages) {
if (appInfo.packageName.equals(appid)) {
installed = appInfo;
break;
}
}
if (installed != null) {
app.installedVersion = installed.versionName;
app.installedVerCode = installed.versionCode;
} else {
app.installedVersion = null;
app.installedVerCode = 0;
}
Apk curver = app.getCurrentVersion();
if (curver != null
&& app.installedVersion != null
&& app.installedVerCode < curver.vercode) {
app.hasUpdates = true;
app.updateVersion = curver.version;
}
apps.set(index, app);
}
Log.d("FDroid", "Refreshing " + invalidApps.size() + " apps took "
+ (System.currentTimeMillis() - startTime) + " ms");
return apps;
}
public List<String> doSearch(String query) {
List<String> ids = new ArrayList<String>();

View File

@ -65,6 +65,7 @@ public class FDroidApp extends Application {
icon_path.mkdir();
apps = null;
invalidApps = new ArrayList<String>();
Context ctx = getApplicationContext();
DB.initDB(ctx);
UpdateService.schedule(ctx);
@ -76,15 +77,16 @@ public class FDroidApp extends Application {
// Set when something has changed (database or installed apps) so we know
// we should invalidate the apps.
private volatile boolean appsInvalid = false;
private volatile boolean appsAllInvalid = false;
private Semaphore appsInvalidLock = new Semaphore(1, false);
private List<String> invalidApps;
// Set apps invalid. Call this when the database has been updated with
// new app information, or when the installed packages have changed.
public void invalidateApps() {
public void invalidateAllApps() {
try {
appsInvalidLock.acquire();
appsInvalid = true;
appsAllInvalid = true;
} catch (InterruptedException e) {
// Don't care
} finally {
@ -92,6 +94,12 @@ public class FDroidApp extends Application {
}
}
// Invalidate a single app
public void invalidateApp(String id) {
Log.d("FDroid", "Invalidating "+id);
invalidApps.add(id);
}
// Get a list of all known applications. Should not be called when the
// database is locked (i.e. between DB.getDB() and db.releaseDB(). The
// contents should never be modified, it's for reading only.
@ -100,9 +108,9 @@ public class FDroidApp extends Application {
boolean invalid = false;
try {
appsInvalidLock.acquire();
invalid = appsInvalid;
invalid = appsAllInvalid;
if (invalid) {
appsInvalid = false;
appsAllInvalid = false;
Log.d("FDroid", "Dropping cached app data");
}
} catch (InterruptedException e) {
@ -118,6 +126,14 @@ public class FDroidApp extends Application {
} finally {
DB.releaseDB();
}
} else if (!invalidApps.isEmpty()) {
try {
DB db = DB.getDB();
apps = db.refreshApps(apps, invalidApps);
invalidApps.clear();
} finally {
DB.releaseDB();
}
}
if (apps == null)
return new ArrayList<DB.App>();

View File

@ -27,8 +27,9 @@ public class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
Log.d("FDroid", "PackageReceiver invalidating apps");
((FDroidApp) ctx.getApplicationContext()).invalidateApps();
String appid = intent.getData().getSchemeSpecificPart();
Log.d("FDroid", "PackageReceiver invalidating "+appid);
((FDroidApp) ctx.getApplicationContext()).invalidateApp(appid);
}
}

View File

@ -74,7 +74,7 @@ public class Preferences extends PreferenceActivity implements
} finally {
DB.releaseDB();
}
((FDroidApp) getApplication()).invalidateApps();
((FDroidApp) getApplication()).invalidateAllApps();
File dp = DB.getDataPath();
deleteAll(dp);

View File

@ -248,7 +248,7 @@ public class UpdateService extends IntentService implements ProgressListener {
sendStatus(STATUS_INFO, getString(R.string.status_downloading_icons));
for (DB.App app : acceptedapps)
getIcon(app, repos);
((FDroidApp) getApplication()).invalidateApps();
((FDroidApp) getApplication()).invalidateAllApps();
}
}