AI 143408: am: CL 143407 Prevent reinstalling APKs during launch if they have not been recompiled since the previous launch.
Original author: xav Merged from: //branches/cupcake/... Automated import of CL 143408
This commit is contained in:
committed by
The Android Open Source Project
parent
1e3355dd7d
commit
c33ae50cc3
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.build;
|
|||||||
|
|
||||||
import com.android.ide.eclipse.adt.AdtConstants;
|
import com.android.ide.eclipse.adt.AdtConstants;
|
||||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||||
|
import com.android.ide.eclipse.adt.project.ApkInstallManager;
|
||||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||||
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
|
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
|
||||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||||
@@ -551,6 +552,9 @@ public class ApkBuilder extends BaseBuilder {
|
|||||||
// and store it
|
// and store it
|
||||||
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
|
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
|
||||||
|
|
||||||
|
// reset the installation manager to force new installs of this project
|
||||||
|
ApkInstallManager.getInstance().resetInstallationFor(project);
|
||||||
|
|
||||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||||
"Build Success!");
|
"Build Success!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
|
|||||||
import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
|
import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
|
||||||
import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode;
|
import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode;
|
||||||
import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse;
|
import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse;
|
||||||
|
import com.android.ide.eclipse.adt.project.ApkInstallManager;
|
||||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||||
@@ -762,13 +763,46 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syncs the application on the device/emulator.
|
* If needed, syncs the application and all its dependencies on the device/emulator.
|
||||||
*
|
*
|
||||||
* @param launchInfo The Launch information object.
|
* @param launchInfo The Launch information object.
|
||||||
* @param device the device on which to sync the application
|
* @param device the device on which to sync the application
|
||||||
* @return true if the install succeeded.
|
* @return true if the install succeeded.
|
||||||
*/
|
*/
|
||||||
private boolean syncApp(DelayedLaunchInfo launchInfo, IDevice device) {
|
private boolean syncApp(DelayedLaunchInfo launchInfo, IDevice device) {
|
||||||
|
boolean alreadyInstalled = ApkInstallManager.getInstance().isApplicationInstalled(
|
||||||
|
launchInfo.getProject(), device);
|
||||||
|
|
||||||
|
if (alreadyInstalled) {
|
||||||
|
AdtPlugin.printToConsole(launchInfo.getProject(),
|
||||||
|
"Application already deployed. No need to reinstall.");
|
||||||
|
} else {
|
||||||
|
if (doSyncApp(launchInfo, device) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The app is now installed, now try the dependent projects
|
||||||
|
for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
|
||||||
|
String msg = String.format("Project dependency found, installing: %s",
|
||||||
|
dependentLaunchInfo.getProject().getName());
|
||||||
|
AdtPlugin.printToConsole(launchInfo.getProject(), msg);
|
||||||
|
if (syncApp(dependentLaunchInfo, device) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syncs the application on the device/emulator.
|
||||||
|
*
|
||||||
|
* @param launchInfo The Launch information object.
|
||||||
|
* @param device the device on which to sync the application
|
||||||
|
* @return true if the install succeeded.
|
||||||
|
*/
|
||||||
|
private boolean doSyncApp(DelayedLaunchInfo launchInfo, IDevice device) {
|
||||||
SyncService sync = device.getSyncService();
|
SyncService sync = device.getSyncService();
|
||||||
if (sync != null) {
|
if (sync != null) {
|
||||||
IPath path = launchInfo.getPackageFile().getLocation();
|
IPath path = launchInfo.getPackageFile().getLocation();
|
||||||
@@ -812,12 +846,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The app is now installed, now try the dependent projects
|
// if the installation succeeded, we register it.
|
||||||
for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
|
if (installResult) {
|
||||||
String msg = String.format("Project dependency found, syncing: %s",
|
ApkInstallManager.getInstance().registerInstallation(
|
||||||
dependentLaunchInfo.getProject().getName());
|
launchInfo.getProject(), device);
|
||||||
AdtPlugin.printToConsole(launchInfo.getProject(), msg);
|
|
||||||
syncApp(dependentLaunchInfo, device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return installResult;
|
return installResult;
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.eclipse.org/org/documents/epl-v10.php
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.ide.eclipse.adt.project;
|
||||||
|
|
||||||
|
import com.android.ddmlib.AndroidDebugBridge;
|
||||||
|
import com.android.ddmlib.Device;
|
||||||
|
import com.android.ddmlib.IDevice;
|
||||||
|
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
|
||||||
|
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
|
||||||
|
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
|
||||||
|
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IProject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers which apk was installed on which device.
|
||||||
|
* <p/>
|
||||||
|
* The goal of this class is to remember the installation of APKs on devices, and provide
|
||||||
|
* information about whether a new APK should be installed on a device prior to running the
|
||||||
|
* application from a launch configuration.
|
||||||
|
* <p/>
|
||||||
|
* The manager uses {@link IProject} and {@link IDevice} to identify the target device and the
|
||||||
|
* (project generating the) APK. This ensures that disconnected and reconnected devices will
|
||||||
|
* always receive new APKs (since the APK could be uninstalled manually).
|
||||||
|
* <p/>
|
||||||
|
* Manually uninstalling an APK from a connected device will still be a problem, but this should
|
||||||
|
* be a limited use case.
|
||||||
|
* <p/>
|
||||||
|
* This is a singleton. To get the instance, use {@link #getInstance()}
|
||||||
|
*/
|
||||||
|
public class ApkInstallManager implements IDeviceChangeListener, IDebugBridgeChangeListener,
|
||||||
|
IProjectListener {
|
||||||
|
|
||||||
|
private final static ApkInstallManager sThis = new ApkInstallManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal struct to associate a project and a device.
|
||||||
|
*/
|
||||||
|
private static class ApkInstall {
|
||||||
|
public ApkInstall(IProject project, IDevice device) {
|
||||||
|
this.project = project;
|
||||||
|
this.device = device;
|
||||||
|
}
|
||||||
|
IProject project;
|
||||||
|
IDevice device;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<ApkInstall> mInstallList = new ArrayList<ApkInstall>();
|
||||||
|
|
||||||
|
public static ApkInstallManager getInstance() {
|
||||||
|
return sThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an installation of <var>project</var> onto <var>device</var>
|
||||||
|
* @param project The project that was installed.
|
||||||
|
* @param device The device that received the installation.
|
||||||
|
*/
|
||||||
|
public void registerInstallation(IProject project, IDevice device) {
|
||||||
|
synchronized (mInstallList) {
|
||||||
|
mInstallList.add(new ApkInstall(project, device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a <var>project</var> was installed on the <var>device</var>.
|
||||||
|
* @param project the project that may have been installed.
|
||||||
|
* @param device the device that may have received the installation.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isApplicationInstalled(IProject project, IDevice device) {
|
||||||
|
synchronized (mInstallList) {
|
||||||
|
for (ApkInstall install : mInstallList) {
|
||||||
|
if (project == install.project && device == install.device) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets registered installations for a specific {@link IProject}.
|
||||||
|
* <p/>This ensures that {@link #isApplicationInstalled(IProject, IDevice)} will always return
|
||||||
|
* <code>null</code> for this specified project, for any device.
|
||||||
|
* @param project the project for which to reset all installations.
|
||||||
|
*/
|
||||||
|
public void resetInstallationFor(IProject project) {
|
||||||
|
synchronized (mInstallList) {
|
||||||
|
for (int i = 0 ; i < mInstallList.size() ;) {
|
||||||
|
ApkInstall install = mInstallList.get(i);
|
||||||
|
if (install.project == project) {
|
||||||
|
mInstallList.remove(i);
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApkInstallManager() {
|
||||||
|
AndroidDebugBridge.addDeviceChangeListener(this);
|
||||||
|
AndroidDebugBridge.addDebugBridgeChangeListener(this);
|
||||||
|
ResourceMonitor.getMonitor().addProjectListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Responds to a bridge change by clearing the full installation list.
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener#bridgeChanged(com.android.ddmlib.AndroidDebugBridge)
|
||||||
|
*/
|
||||||
|
public void bridgeChanged(AndroidDebugBridge bridge) {
|
||||||
|
// the bridge changed, there is no way to know which IDevice will be which.
|
||||||
|
// We reset everything
|
||||||
|
synchronized (mInstallList) {
|
||||||
|
mInstallList.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Responds to a device being disconnected by removing all installations related to this device.
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceDisconnected(com.android.ddmlib.Device)
|
||||||
|
*/
|
||||||
|
public void deviceDisconnected(Device device) {
|
||||||
|
synchronized (mInstallList) {
|
||||||
|
for (int i = 0 ; i < mInstallList.size() ;) {
|
||||||
|
ApkInstall install = mInstallList.get(i);
|
||||||
|
if (install.device == device) {
|
||||||
|
mInstallList.remove(i);
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Responds to a close project by resetting all its installation.
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectClosed(org.eclipse.core.resources.IProject)
|
||||||
|
*/
|
||||||
|
public void projectClosed(IProject project) {
|
||||||
|
resetInstallationFor(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Responds to a close project by resetting all its installation.
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectDeleted(org.eclipse.core.resources.IProject)
|
||||||
|
*/
|
||||||
|
public void projectDeleted(IProject project) {
|
||||||
|
resetInstallationFor(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Does nothing
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceChanged(com.android.ddmlib.Device, int)
|
||||||
|
*/
|
||||||
|
public void deviceChanged(Device device, int changeMask) {
|
||||||
|
// nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Does nothing
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceConnected(com.android.ddmlib.Device)
|
||||||
|
*/
|
||||||
|
public void deviceConnected(Device device) {
|
||||||
|
// nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Does nothing
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpened(org.eclipse.core.resources.IProject)
|
||||||
|
*/
|
||||||
|
public void projectOpened(IProject project) {
|
||||||
|
// nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Does nothing
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpenedWithWorkspace(org.eclipse.core.resources.IProject)
|
||||||
|
*/
|
||||||
|
public void projectOpenedWithWorkspace(IProject project) {
|
||||||
|
// nothing to do.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user