From 73262b1c01e3a902c171a5de2825f8ab0f2b44fb Mon Sep 17 00:00:00 2001 From: Raphael Moll Date: Tue, 8 May 2012 19:13:01 -0700 Subject: SDK Manager: rework package loader. When the SDK Manager window opens, the process is changed to: - first a package loader is created that only checks the local cache xml files. It populates the package list based on what the client last got, essentially. - next a regular package loader is created that will respect the expiration and refresh parameters of the download cache. This means for users, in the majority of cases when remote servers do not change, the package list will be populated as fast as possible and then an asynchronous refresh happens. Change-Id: Ifd1f58412dcc643eaae37257a9bc0a01fc222c90 --- .../sdklib/internal/repository/DownloadCache.java | 15 +++ .../sdklib/internal/repository/ITaskFactory.java | 14 +++ .../sdkuilib/internal/repository/UpdaterData.java | 132 ++++++-------------- .../repository/sdkman2/AdtUpdateDialog.java | 6 +- .../internal/repository/sdkman2/PackageLoader.java | 138 ++++++++++++++++++--- .../repository/sdkman2/PackagesDiffLogic.java | 6 - .../internal/repository/sdkman2/PackagesPage.java | 58 +++++++-- .../sdkuilib/internal/tasks/ProgressView.java | 7 ++ 8 files changed, 245 insertions(+), 131 deletions(-) (limited to 'sdkmanager') diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java index c29e988..591e447 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java @@ -134,6 +134,12 @@ public class DownloadCache { public enum Strategy { /** + * Exclusively serves data from the cache. If files are available in the + * cache, serve them as is (without trying to refresh them). If files are + * not available, they are not fetched at all. + */ + ONLY_CACHE, + /** * If the files are available in the cache, serve them as-is, otherwise * download them and return the cached version. No expiration or refresh * is attempted if a file is in the cache. @@ -237,6 +243,7 @@ public class DownloadCache { * @param monitor {@link ITaskMonitor} which is related to this URL * fetching. * @return Returns an {@link InputStream} holding the URL content. + * Returns null if the document is not cached and strategy is {@link Strategy#ONLY_CACHE}. * @throws IOException Exception thrown when there are problems retrieving * the URL or its content. * @throws CanceledByUserException Exception thrown if the user cancels the @@ -414,6 +421,14 @@ public class DownloadCache { } catch (IOException ignore) {} } + if (!useCached && mStrategy == Strategy.ONLY_CACHE) { + // We don't have a document to serve from the cache. + if (DEBUG) { + System.out.println(String.format("%s : file not in cache", urlString)); //$NON-NLS-1$ + } + return null; + } + // If we're not using the cache, try to remove the cache and download again. try { cached.delete(); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java index fb59b42..dfd197d 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java @@ -16,6 +16,7 @@ package com.android.sdklib.internal.repository; + /** * A factory that can start and run new {@link ITask}s. */ @@ -23,6 +24,12 @@ public interface ITaskFactory { /** * Starts a new task with a new {@link ITaskMonitor}. + *

+ * The task will execute in a thread and runs it own UI loop. + * This means the task can perform UI operations using + * {@code Display#asyncExec(Runnable)}. + *

+ * In either case, the method only returns when the task has finished. * * @param title The title of the task, displayed in the monitor if any. * @param task The task to run. @@ -36,6 +43,13 @@ public interface ITaskFactory { * and give the sub-monitor to the new task with the number of work units you want * it to fill. The {@link #start} method will make sure to fill the progress * when the task is completed, in case the actual task did not. + *

+ * When a task is started from within a monitor, it reuses the thread + * from the parent. Otherwise it starts a new thread and runs it own + * UI loop. This means the task can perform UI operations using + * {@code Display#asyncExec(Runnable)}. + *

+ * In either case, the method only returns when the task has finished. * * @param title The title of the task, displayed in the monitor if any. * @param parentMonitor The parent monitor. Can be null. diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java index a58e0f0..af40d78 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java @@ -24,31 +24,28 @@ import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.repository.AdbWrapper; -import com.android.sdklib.internal.repository.AddonsListFetcher; -import com.android.sdklib.internal.repository.AddonsListFetcher.Site; +import com.android.sdklib.internal.repository.DownloadCache; +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskFactory; +import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdklib.internal.repository.LocalSdkParser; +import com.android.sdklib.internal.repository.NullTaskMonitor; import com.android.sdklib.internal.repository.archives.Archive; import com.android.sdklib.internal.repository.archives.ArchiveInstaller; import com.android.sdklib.internal.repository.packages.AddonPackage; import com.android.sdklib.internal.repository.packages.Package; import com.android.sdklib.internal.repository.packages.PlatformToolPackage; import com.android.sdklib.internal.repository.packages.ToolPackage; -import com.android.sdklib.internal.repository.sources.SdkAddonSource; import com.android.sdklib.internal.repository.sources.SdkRepoSource; import com.android.sdklib.internal.repository.sources.SdkSource; import com.android.sdklib.internal.repository.sources.SdkSourceCategory; import com.android.sdklib.internal.repository.sources.SdkSources; -import com.android.sdklib.internal.repository.DownloadCache; -import com.android.sdklib.internal.repository.ITask; -import com.android.sdklib.internal.repository.ITaskFactory; -import com.android.sdklib.internal.repository.ITaskMonitor; -import com.android.sdklib.internal.repository.LocalSdkParser; -import com.android.sdklib.internal.repository.NullTaskMonitor; import com.android.sdklib.repository.SdkAddonConstants; -import com.android.sdklib.repository.SdkAddonsListConstants; import com.android.sdklib.repository.SdkRepoConstants; import com.android.sdklib.util.LineUtil; import com.android.sdklib.util.SparseIntArray; import com.android.sdkuilib.internal.repository.icons.ImageFactory; +import com.android.sdkuilib.internal.repository.sdkman2.PackageLoader; import com.android.sdkuilib.internal.repository.sdkman2.SdkUpdaterWindowImpl2; import com.android.sdkuilib.repository.ISdkChangeListener; @@ -79,27 +76,32 @@ public class UpdaterData implements IUpdaterData { private String mOsSdkRoot; - private final ISdkLog mSdkLog; - private ITaskFactory mTaskFactory; - - private SdkManager mSdkManager; - private AvdManager mAvdManager; - private DownloadCache mDownloadCache; // lazily created in getDownloadCache private final LocalSdkParser mLocalSdkParser = new LocalSdkParser(); private final SdkSources mSources = new SdkSources(); - private ImageFactory mImageFactory; private final SettingsController mSettingsController; private final ArrayList mListeners = new ArrayList(); + private final ISdkLog mSdkLog; + private ITaskFactory mTaskFactory; private Shell mWindowShell; - private AndroidLocationException mAvdManagerInitError; - + private SdkManager mSdkManager; + private AvdManager mAvdManager; /** - * 0 = need to fetch remote addons list once.. - * 1 = fetch succeeded, don't need to do it any more. - * -1= fetch failed, do it again only if the user requests a refresh - * or changes the force-http setting. + * The current {@link PackageLoader} to use. + * Lazily created in {@link #getPackageLoader()}. */ - private int mStateFetchRemoteAddonsList; + private PackageLoader mPackageLoader; + /** + * The current {@link DownloadCache} to use. + * Lazily created in {@link #getDownloadCache()}. + */ + private DownloadCache mDownloadCache; + /** + * The current {@link ImageFactory}. + * Set via {@link #setImageFactory(ImageFactory)} by the window implementation. + * It is null when invoked using the command-line interface. + */ + private ImageFactory mImageFactory; + private AndroidLocationException mAvdManagerInitError; /** * Creates a new updater data. @@ -111,7 +113,6 @@ public class UpdaterData implements IUpdaterData { mOsSdkRoot = osSdkRoot; mSdkLog = sdkLog; - mDownloadCache = getDownloadCache(); mSettingsController = new SettingsController(this); initSdk(); @@ -198,6 +199,14 @@ public class UpdaterData implements IUpdaterData { return mWindowShell; } + public PackageLoader getPackageLoader() { + // The package loader is lazily initialized here. + if (mPackageLoader == null) { + mPackageLoader = new PackageLoader(this); + } + return mPackageLoader; + } + /** * Check if any error occurred during initialization. * If it did, display an error message. @@ -446,7 +455,7 @@ public class UpdaterData implements IUpdaterData { mOsSdkRoot, forceHttp, mSdkManager, - mDownloadCache, + getDownloadCache(), monitor)) { // We installed this archive. newlyInstalledArchives.add(archive); @@ -698,7 +707,7 @@ public class UpdaterData implements IUpdaterData { includeObsoletes); if (selectedArchives == null) { - loadRemoteAddonsList(new NullTaskMonitor(getSdkLog())); + getPackageLoader().loadRemoteAddonsList(new NullTaskMonitor(getSdkLog())); ul.addNewPlatforms( archives, getSources(), @@ -733,7 +742,7 @@ public class UpdaterData implements IUpdaterData { */ private List getRemoteArchives_NoGUI(boolean includeAll) { refreshSources(true); - loadRemoteAddonsList(new NullTaskMonitor(getSdkLog())); + getPackageLoader().loadRemoteAddonsList(new NullTaskMonitor(getSdkLog())); List archives; SdkUpdaterLogic ul = new SdkUpdaterLogic(this); @@ -998,9 +1007,7 @@ public class UpdaterData implements IUpdaterData { @Override public void run(ITaskMonitor monitor) { - if (mStateFetchRemoteAddonsList <= 0) { - loadRemoteAddonsListInTask(monitor); - } + getPackageLoader().loadRemoteAddonsList(monitor); SdkSource[] sources = mSources.getAllSources(); monitor.setDescription("Refresh Sources"); @@ -1009,7 +1016,7 @@ public class UpdaterData implements IUpdaterData { if (forceFetching || source.getPackages() != null || source.getFetchError() != null) { - source.load(mDownloadCache, monitor.createSubMonitor(1), forceHttp); + source.load(getDownloadCache(), monitor.createSubMonitor(1), forceHttp); } monitor.incProgress(1); } @@ -1018,67 +1025,6 @@ public class UpdaterData implements IUpdaterData { } /** - * Loads the remote add-ons list. - */ - public void loadRemoteAddonsList(ITaskMonitor monitor) { - - if (mStateFetchRemoteAddonsList != 0) { - return; - } - - mTaskFactory.start("Load Add-ons List", monitor, new ITask() { - @Override - public void run(ITaskMonitor subMonitor) { - loadRemoteAddonsListInTask(subMonitor); - } - }); - } - - private void loadRemoteAddonsListInTask(ITaskMonitor monitor) { - mStateFetchRemoteAddonsList = -1; - - String url = SdkAddonsListConstants.URL_ADDON_LIST; - - // We override SdkRepoConstants.URL_GOOGLE_SDK_SITE if this is defined - String baseUrl = System.getenv("SDK_TEST_BASE_URL"); //$NON-NLS-1$ - if (baseUrl != null) { - if (baseUrl.length() > 0 && baseUrl.endsWith("/")) { //$NON-NLS-1$ - if (url.startsWith(SdkRepoConstants.URL_GOOGLE_SDK_SITE)) { - url = baseUrl + url.substring(SdkRepoConstants.URL_GOOGLE_SDK_SITE.length()); - } - } else { - monitor.logError("Ignoring invalid SDK_TEST_BASE_URL: %1$s", baseUrl); //$NON-NLS-1$ - } - } - - if (getSettingsController().getForceHttp()) { - url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$ - } - - // Hook to bypass loading 3rd party addons lists. - boolean fetch3rdParties = System.getenv("SDK_SKIP_3RD_PARTIES") == null; - - AddonsListFetcher fetcher = new AddonsListFetcher(); - Site[] sites = fetcher.fetch(url, mDownloadCache, monitor); - if (sites != null) { - mSources.removeAll(SdkSourceCategory.ADDONS_3RD_PARTY); - - if (fetch3rdParties) { - for (Site s : sites) { - mSources.add(SdkSourceCategory.ADDONS_3RD_PARTY, - new SdkAddonSource(s.getUrl(), s.getUiName())); - } - } - - mSources.notifyChangeListeners(); - - mStateFetchRemoteAddonsList = 1; - } - - monitor.setDescription("Fetched Add-ons List successfully"); - } - - /** * Safely invoke all the registered {@link ISdkChangeListener#onSdkLoaded()}. * This can be called from any thread. */ diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java index f481b0a..ba9bce7 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java @@ -76,7 +76,7 @@ public class AdtUpdateDialog extends SwtBaseDialog { private Map mResultPaths = null; private SettingsController mSettingsController; private PackageFilter mPackageFilter; - private PackageLoader mPackageMananger; + private PackageLoader mPackageLoader; private ProgressBar mProgressBar; private Label mStatusText; @@ -221,12 +221,12 @@ public class AdtUpdateDialog extends SwtBaseDialog { mUpdaterData.broadcastOnSdkLoaded(); - mPackageMananger = new PackageLoader(mUpdaterData); + mPackageLoader = new PackageLoader(mUpdaterData); } @Override protected void eventLoop() { - mPackageMananger.loadPackagesWithInstallTask( + mPackageLoader.loadPackagesWithInstallTask( mPackageFilter.installFlags(), new IAutoInstallTask() { @Override diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java index 4ac03e9..ecbefb7 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java @@ -16,6 +16,8 @@ package com.android.sdkuilib.internal.repository.sdkman2; +import com.android.sdklib.internal.repository.AddonsListFetcher; +import com.android.sdklib.internal.repository.AddonsListFetcher.Site; import com.android.sdklib.internal.repository.DownloadCache; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; @@ -23,7 +25,12 @@ import com.android.sdklib.internal.repository.NullTaskMonitor; import com.android.sdklib.internal.repository.archives.Archive; import com.android.sdklib.internal.repository.packages.Package; import com.android.sdklib.internal.repository.packages.Package.UpdateInfo; +import com.android.sdklib.internal.repository.sources.SdkAddonSource; import com.android.sdklib.internal.repository.sources.SdkSource; +import com.android.sdklib.internal.repository.sources.SdkSourceCategory; +import com.android.sdklib.internal.repository.sources.SdkSources; +import com.android.sdklib.repository.SdkAddonsListConstants; +import com.android.sdklib.repository.SdkRepoConstants; import com.android.sdkuilib.internal.repository.UpdaterData; import org.eclipse.swt.widgets.Display; @@ -39,13 +46,30 @@ import java.util.Map; * Loads packages fetched from the remote SDK Repository and keeps track * of their state compared with the current local SDK installation. */ -class PackageLoader { +public class PackageLoader { + /** The update data context. Never null. */ private final UpdaterData mUpdaterData; /** + * The {@link DownloadCache} override. Can be null, in which case the one from + * {@link UpdaterData} is used instead. + * @see #getDownloadCache() + */ + private final DownloadCache mOverrideCache; + + /** + * 0 = need to fetch remote addons list once.. + * 1 = fetch succeeded, don't need to do it any more. + * -1= fetch failed, do it again only if the user requests a refresh + * or changes the force-http setting. + */ + private int mStateFetchRemoteAddonsList; + + + /** * Interface for the callback called by - * {@link PackageLoader#loadPackages(DownloadCache, ISourceLoadedCallback)}. + * {@link PackageLoader#loadPackages(ISourceLoadedCallback)}. *

* After processing each source, the package loader calls {@link #onUpdateSource} * with the list of packages found in that source. @@ -128,12 +152,27 @@ class PackageLoader { } /** - * Creates a new PackageManager associated with the given {@link UpdaterData}. + * Creates a new PackageManager associated with the given {@link UpdaterData} + * and using the {@link UpdaterData}'s default {@link DownloadCache}. * * @param updaterData The {@link UpdaterData}. Must not be null. */ public PackageLoader(UpdaterData updaterData) { mUpdaterData = updaterData; + mOverrideCache = null; + } + + /** + * Creates a new PackageManager associated with the given {@link UpdaterData} + * but using the specified {@link DownloadCache} instead of the one from + * {@link UpdaterData}. + * + * @param updaterData The {@link UpdaterData}. Must not be null. + * @param cache The {@link DownloadCache} to use instead of the one from {@link UpdaterData}. + */ + public PackageLoader(UpdaterData updaterData, DownloadCache cache) { + mUpdaterData = updaterData; + mOverrideCache = cache; } /** @@ -145,20 +184,12 @@ class PackageLoader { * after each source is finished loaded. In return the callback tells the loader * whether to continue loading sources. */ - public void loadPackages( - DownloadCache downloadCache, - final ISourceLoadedCallback sourceLoadedCallback) { + public void loadPackages(final ISourceLoadedCallback sourceLoadedCallback) { try { if (mUpdaterData == null) { return; } - if (downloadCache == null) { - downloadCache = mUpdaterData.getDownloadCache(); - } - - final DownloadCache downloadCache2 = downloadCache; - mUpdaterData.getTaskFactory().start("Loading Sources", new ITask() { @Override public void run(ITaskMonitor monitor) { @@ -176,7 +207,7 @@ class PackageLoader { // get remote packages boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp(); - mUpdaterData.loadRemoteAddonsList(monitor.createSubMonitor(1)); + loadRemoteAddonsList(monitor.createSubMonitor(1)); SdkSource[] sources = mUpdaterData.getSources().getAllSources(); try { @@ -186,7 +217,7 @@ class PackageLoader { for (SdkSource source : sources) { Package[] pkgs = source.getPackages(); if (pkgs == null) { - source.load(downloadCache2, + source.load(getDownloadCache(), subMonitor.createSubMonitor(1), forceHttp); pkgs = source.getPackages(); @@ -215,8 +246,7 @@ class PackageLoader { } /** - * Load packages, source by source using - * {@link #loadPackages(DownloadCache, ISourceLoadedCallback)}, + * Load packages, source by source using {@link #loadPackages(ISourceLoadedCallback)}, * and executes the given {@link IAutoInstallTask} on the current package list. * That is for each package known, the install task is queried to find if * the package is the one to be installed or updated. @@ -248,8 +278,7 @@ class PackageLoader { final int installFlags, final IAutoInstallTask installTask) { - loadPackages(mUpdaterData.getDownloadCache(), - new ISourceLoadedCallback() { + loadPackages(new ISourceLoadedCallback() { List mArchivesToInstall = new ArrayList(); Map mInstallPaths = new HashMap(); @@ -367,4 +396,77 @@ class PackageLoader { } }); } + + + /** + * Loads the remote add-ons list. + */ + public void loadRemoteAddonsList(ITaskMonitor monitor) { + + if (mStateFetchRemoteAddonsList != 0) { + return; + } + + mUpdaterData.getTaskFactory().start("Load Add-ons List", monitor, new ITask() { + @Override + public void run(ITaskMonitor subMonitor) { + loadRemoteAddonsListInTask(subMonitor); + } + }); + } + + private void loadRemoteAddonsListInTask(ITaskMonitor monitor) { + mStateFetchRemoteAddonsList = -1; + + String url = SdkAddonsListConstants.URL_ADDON_LIST; + + // We override SdkRepoConstants.URL_GOOGLE_SDK_SITE if this is defined + String baseUrl = System.getenv("SDK_TEST_BASE_URL"); //$NON-NLS-1$ + if (baseUrl != null) { + if (baseUrl.length() > 0 && baseUrl.endsWith("/")) { //$NON-NLS-1$ + if (url.startsWith(SdkRepoConstants.URL_GOOGLE_SDK_SITE)) { + url = baseUrl + url.substring(SdkRepoConstants.URL_GOOGLE_SDK_SITE.length()); + } + } else { + monitor.logError("Ignoring invalid SDK_TEST_BASE_URL: %1$s", baseUrl); //$NON-NLS-1$ + } + } + + if (mUpdaterData.getSettingsController().getForceHttp()) { + url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + // Hook to bypass loading 3rd party addons lists. + boolean fetch3rdParties = System.getenv("SDK_SKIP_3RD_PARTIES") == null; + + AddonsListFetcher fetcher = new AddonsListFetcher(); + Site[] sites = fetcher.fetch(url, getDownloadCache(), monitor); + if (sites != null) { + SdkSources sources = mUpdaterData.getSources(); + sources.removeAll(SdkSourceCategory.ADDONS_3RD_PARTY); + + if (fetch3rdParties) { + for (Site s : sites) { + sources.add(SdkSourceCategory.ADDONS_3RD_PARTY, + new SdkAddonSource(s.getUrl(), s.getUiName())); + } + } + + sources.notifyChangeListeners(); + + mStateFetchRemoteAddonsList = 1; + } + + monitor.setDescription("Fetched Add-ons List successfully"); + } + + /** + * Returns the {@link DownloadCache} to use. + * + * @return Returns {@link #mOverrideCache} if not null; otherwise returns the + * one from {@link UpdaterData} is used instead. + */ + private DownloadCache getDownloadCache() { + return mOverrideCache != null ? mOverrideCache : mUpdaterData.getDownloadCache(); + } } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java index 8e1cbb3..9e45748 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java @@ -48,17 +48,11 @@ import java.util.Set; * so that we can test it using head-less unit tests. */ class PackagesDiffLogic { - private final PackageLoader mPackageLoader; private final UpdaterData mUpdaterData; private boolean mFirstLoadComplete = true; public PackagesDiffLogic(UpdaterData updaterData) { mUpdaterData = updaterData; - mPackageLoader = new PackageLoader(updaterData); - } - - public PackageLoader getPackageLoader() { - return mPackageLoader; } /** diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java index 51543e8..40a3538 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java @@ -17,6 +17,8 @@ package com.android.sdkuilib.internal.repository.sdkman2; import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.repository.DownloadCache; +import com.android.sdklib.internal.repository.DownloadCache.Strategy; import com.android.sdklib.internal.repository.IDescription; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; @@ -129,6 +131,7 @@ public class PackagesPage extends UpdaterPage implements ISdkChangeListener { private final SdkInvocationContext mContext; private final UpdaterData mUpdaterData; private final PackagesDiffLogic mDiffLogic; + private boolean mDisplayArchives = false; private boolean mOperationPending; @@ -168,8 +171,15 @@ public class PackagesPage extends UpdaterPage implements ISdkChangeListener { } public void performFirstLoad() { - // Initialize the package list the first time the page is shown. - loadPackages(true /*isFirstLoad*/); + // First a package loader is created that only checks + // the local cache xml files. It populates the package + // list based on what the client got last, essentially. + loadPackages(true /*useLocalCache*/); + + // Next a regular package loader is created that will + // respect the expiration and refresh parameters of the + // download cache. + loadPackages(false /*useLocalCache*/); } @SuppressWarnings("unused") @@ -576,11 +586,24 @@ public class PackagesPage extends UpdaterPage implements ISdkChangeListener { loadPackages(); } + /** + * Performs a "normal" reload of the package information, use the default download + * cache and refreshing strategy as needed. + */ private void loadPackages() { - loadPackages(false /*isFirstLoad*/); + loadPackages(false /*useLocalCache*/); } - private void loadPackages(final boolean isFirstLoad) { + /** + * Performs a reload of the package information. + * + * @param useLocalCache When true, the {@link PackageLoader} is switched to use + * a specific {@link DownloadCache} using the {@link Strategy#ONLY_CACHE}, meaning + * it will only use data from the local cache. It will not try to fetch or refresh + * manifests. This is used once the very first time the sdk manager window opens + * and is typically followed by a regular load with refresh. + */ + private void loadPackages(final boolean useLocalCache) { if (mUpdaterData == null) { return; } @@ -593,15 +616,26 @@ public class PackagesPage extends UpdaterPage implements ISdkChangeListener { final boolean displaySortByApi = isSortByApi(); - if (!mTreeColumnName.isDisposed()) { - mTreeColumnName.setImage( - getImage(displaySortByApi ? ICON_SORT_BY_API : ICON_SORT_BY_SOURCE)); + if (mTreeColumnName.isDisposed()) { + // If the UI got disposed, don't try to load anything since we won't be + // able to display it anyway. + return; + } + + mTreeColumnName.setImage(getImage(displaySortByApi ? ICON_SORT_BY_API + : ICON_SORT_BY_SOURCE)); + + PackageLoader packageLoader = null; + if (useLocalCache) { + packageLoader = + new PackageLoader(mUpdaterData, new DownloadCache(Strategy.ONLY_CACHE)); + } else { + packageLoader = mUpdaterData.getPackageLoader(); } + assert packageLoader != null; mDiffLogic.updateStart(); - mDiffLogic.getPackageLoader().loadPackages( - mUpdaterData.getDownloadCache(), // TODO do a first pass with Cache=SERVE_CACHE - new ISourceLoadedCallback() { + packageLoader.loadPackages(new ISourceLoadedCallback() { @Override public boolean onUpdateSource(SdkSource source, Package[] newPackages) { // This runs in a thread and must not access UI directly. @@ -640,7 +674,9 @@ public class PackagesPage extends UpdaterPage implements ISdkChangeListener { refreshViewerInput(); } - if (mDiffLogic.isFirstLoadComplete() && !mGroupPackages.isDisposed()) { + if (!useLocalCache && + mDiffLogic.isFirstLoadComplete() && + !mGroupPackages.isDisposed()) { // At the end of the first load, if nothing is selected then // automatically select all new and update packages. Object[] checked = mTreeViewer.getCheckedElements(); diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java index 8444f9f..3448852 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java @@ -100,6 +100,13 @@ public final class ProgressView implements IProgressUiProvider { /** * Starts the task and block till it's either finished or canceled. * This can be called from a non-UI thread safely. + *

+ * When a task is started from within a monitor, it reuses the thread + * from the parent. Otherwise it starts a new thread and runs it own + * UI loop. This means the task can perform UI operations using + * {@link Display#asyncExec(Runnable)}. + *

+ * In either case, the method only returns when the task has finished. */ public void startTask( final String title, -- cgit v1.1