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() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int whichButton) { int whichButton) {
install(); install(app.id);
} }
}); });
ask_alrt.setNegativeButton(getString(R.string.no), ask_alrt.setNegativeButton(getString(R.string.no),
@ -523,7 +523,7 @@ public class AppDetails extends ListActivity {
AlertDialog alert = ask_alrt.create(); AlertDialog alert = ask_alrt.create();
alert.show(); alert.show();
} else } else
install(); install(app.id);
} }
@Override @Override
@ -601,7 +601,7 @@ public class AppDetails extends ListActivity {
// Note that this handles updating as well as installing. // Note that this handles updating as well as installing.
curapk = app.getCurrentVersion(); curapk = app.getCurrentVersion();
if (curapk != null) if (curapk != null)
install(); install(app.id);
return true; return true;
case UNINSTALL: case UNINSTALL:
@ -641,7 +641,7 @@ public class AppDetails extends ListActivity {
} }
// Install the version of this app denoted by 'curapk'. // Install the version of this app denoted by 'curapk'.
private void install() { private void install(final String id) {
String ra = null; String ra = null;
try { try {
@ -666,7 +666,7 @@ public class AppDetails extends ListActivity {
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int whichButton) { int whichButton) {
downloadHandler = new DownloadHandler(curapk, downloadHandler = new DownloadHandler(curapk,
repoaddress); repoaddress, id);
} }
}); });
ask_alrt.setNegativeButton(getString(R.string.no), ask_alrt.setNegativeButton(getString(R.string.no),
@ -694,7 +694,7 @@ public class AppDetails extends ListActivity {
alert.show(); alert.show();
return; return;
} }
downloadHandler = new DownloadHandler(curapk, repoaddress); downloadHandler = new DownloadHandler(curapk, repoaddress, id);
} }
private void removeApk(String id) { private void removeApk(String id) {
@ -708,17 +708,15 @@ public class AppDetails extends ListActivity {
Uri uri = Uri.fromParts("package", pkginfo.packageName, null); Uri uri = Uri.fromParts("package", pkginfo.packageName, null);
Intent intent = new Intent(Intent.ACTION_DELETE, uri); Intent intent = new Intent(Intent.ACTION_DELETE, uri);
startActivityForResult(intent, REQUEST_UNINSTALL); startActivityForResult(intent, REQUEST_UNINSTALL);
((FDroidApp) getApplication()).invalidateApps();
} }
private void installApk(File file) { private void installApk(File file, String id) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW); intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + file.getPath()), intent.setDataAndType(Uri.parse("file://" + file.getPath()),
"application/vnd.android.package-archive"); "application/vnd.android.package-archive");
startActivityForResult(intent, REQUEST_INSTALL); startActivityForResult(intent, REQUEST_INSTALL);
((FDroidApp) getApplication()).invalidateApps();
} }
private void launchApk(String id) { private void launchApk(String id) {
@ -755,8 +753,10 @@ public class AppDetails extends ListActivity {
private Downloader download; private Downloader download;
private ProgressDialog pd; private ProgressDialog pd;
private boolean updating; 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 = new Downloader(apk, repoaddress);
download.start(); download.start();
startUpdates(); startUpdates();
@ -794,7 +794,7 @@ public class AppDetails extends ListActivity {
case DONE: case DONE:
if (pd != null) if (pd != null)
pd.dismiss(); pd.dismiss();
installApk(download.localFile()); installApk(download.localFile(), id);
finished = true; finished = true;
break; break;
case CANCELLED: 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 // Return a list of apps matching the given criteria. Filtering is
// also done based on compatibility and anti-features according to // also done based on compatibility and anti-features according to
// the user's current preferences. // the user's current preferences.
@ -784,6 +795,57 @@ public class DB {
return result; 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) { public List<String> doSearch(String query) {
List<String> ids = new ArrayList<String>(); List<String> ids = new ArrayList<String>();

View File

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

View File

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

View File

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

View File

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