From 11221ae4172616b784d7b53ec1e03118e501c90c Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 22 Jan 2016 15:11:34 +0200 Subject: [PATCH] Speed up livesync on android device by using hash file Optimize the usage of `tns livesync android` command. In case your project is huge, every time you use it, the full project will be transferred to device. Persist all file hashes of project's files and next time when command is executed transfer only the changed ones. The json which contains hashes information should be on device, so when the device is attached to another PC, where the same project is developed, we'll still transfer only the changed files. Related: https://github.com/NativeScript/nativescript-cli/issues/921 --- lib/common | 2 +- lib/definitions/platform.d.ts | 1 + lib/providers/device-app-data-provider.ts | 27 ++++++++++++++++--- lib/providers/livesync-provider.ts | 2 +- lib/services/android-project-service.ts | 12 ++++++++- .../livesync/android-livesync-service.ts | 22 ++++++++++++++- lib/services/platform-service.ts | 4 +-- 7 files changed, 61 insertions(+), 9 deletions(-) diff --git a/lib/common b/lib/common index 7ba45ead4a..998e8e7eae 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 7ba45ead4ac61c0c4e6a6d97d5446dffae8db19c +Subproject commit 998e8e7eaec9df508350a671cce287f4e634b546 diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 4ac9f7e9ea..60a69bdd97 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -8,6 +8,7 @@ interface IPlatformService { runPlatform(platform: string, buildConfig?: IBuildConfig): IFuture; preparePlatform(platform: string): IFuture; buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture; + buildForDeploy(platform: string, buildConfig?: IBuildConfig): IFuture; installOnDevice(platform: string, buildConfig?: IBuildConfig): IFuture; deployOnDevice(platform: string, buildConfig?: IBuildConfig): IFuture; deployOnEmulator(platform: string, buildConfig?: IBuildConfig): IFuture; diff --git a/lib/providers/device-app-data-provider.ts b/lib/providers/device-app-data-provider.ts index 34f206e2b3..fdeb6e20ba 100644 --- a/lib/providers/device-app-data-provider.ts +++ b/lib/providers/device-app-data-provider.ts @@ -3,6 +3,11 @@ import * as deviceAppDataBaseLib from "../common/mobile/device-app-data/device-app-data-base"; import Future = require("fibers/future"); import * as path from "path"; +import {AndroidDeviceHashService} from "../common/mobile/android/android-device-hash-service"; +import {AndroidDebugBridge} from "../common/mobile/android/android-debug-bridge"; + +const SYNC_DIR_NAME = "sync"; +const FULLSYNC_DIR_NAME = "fullsync"; export class IOSAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase implements Mobile.IDeviceAppData { private static DEVICE_PROJECT_ROOT_PATH = "Library/Application Support/LiveSync/app"; @@ -37,18 +42,34 @@ export class AndroidAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase constructor(_appIdentifier: string, public device: Mobile.IDevice, public platform: string, - private $options: IOptions) { + private $options: IOptions, + private $injector: IInjector) { super(_appIdentifier); } + private _deviceProjectRootPath: string; + public get deviceProjectRootPath(): string { - let syncFolderName = this.$options.watch ? "sync" : "fullsync"; - return `/data/local/tmp/${this.appIdentifier}/${syncFolderName}`; + if(!this._deviceProjectRootPath) { + let syncFolderName = this.getSyncFolderName().wait(); + this._deviceProjectRootPath = `/data/local/tmp/${this.appIdentifier}/${syncFolderName}`; + } + + return this._deviceProjectRootPath; } public isLiveSyncSupported(): IFuture { return Future.fromResult(true); } + + private getSyncFolderName(): IFuture { + return ((): string =>{ + let adb = this.$injector.resolve(AndroidDebugBridge, { identifier: this.device.deviceInfo.identifier }); + let deviceHashService = this.$injector.resolve(AndroidDeviceHashService, {adb: adb, appIdentifier: this.appIdentifier}); + let hashFile = this.$options.force ? null : deviceHashService.doesShasumFileExistsOnDevice().wait(); + return this.$options.watch || hashFile ? SYNC_DIR_NAME : FULLSYNC_DIR_NAME; + }).future()(); + } } export class DeviceAppDataProvider implements Mobile.IDeviceAppDataProvider { diff --git a/lib/providers/livesync-provider.ts b/lib/providers/livesync-provider.ts index 9f4d508b8f..f0fe1e0fb2 100644 --- a/lib/providers/livesync-provider.ts +++ b/lib/providers/livesync-provider.ts @@ -25,7 +25,7 @@ export class LiveSyncProvider implements ILiveSyncProvider { public buildForDevice(device: Mobile.IDevice): IFuture { return (() => { - this.$platformService.buildPlatform(device.deviceInfo.platform, {buildForDevice: !device.isEmulator}).wait(); + this.$platformService.buildForDeploy(device.deviceInfo.platform, {buildForDevice: !device.isEmulator}).wait(); let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform); if (device.isEmulator) { return this.$platformService.getLatestApplicationPackageForEmulator(platformData).wait().packageName; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 447e4d7d3f..74c028335e 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -7,6 +7,7 @@ import * as constants from "../constants"; import * as semver from "semver"; import * as projectServiceBaseLib from "./platform-project-service-base"; import * as androidDebugBridgePath from "../common/mobile/android/android-debug-bridge"; +import {AndroidDeviceHashService} from "../common/mobile/android/android-device-hash-service"; export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { private static VALUES_DIRNAME = "values"; @@ -29,7 +30,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject private $sysInfo: ISysInfo, private $mobileHelper: Mobile.IMobileHelper, private $injector: IInjector, - private $pluginVariablesService: IPluginVariablesService) { + private $pluginVariablesService: IPluginVariablesService, + private $deviceAppDataFactory: Mobile.IDeviceAppDataFactory) { super($fs, $projectData, $projectDataService); this._androidProjectPropertiesManagers = Object.create(null); } @@ -330,6 +332,14 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject adb.executeShellCommand(["rm", "-rf", this.$mobileHelper.buildDevicePath(deviceRootPath, "fullsync"), this.$mobileHelper.buildDevicePath(deviceRootPath, "sync"), this.$mobileHelper.buildDevicePath(deviceRootPath, "removedsync")]).wait(); + + let projectFilesManager = this.$injector.resolve("projectFilesManager"); // We need to resolve projectFilesManager here due to cyclic dependency + let devicesService: Mobile.IDevicesService = this.$injector.resolve("devicesService"); + let device = _.find(devicesService.getDevicesForPlatform(this.platformData.normalizedPlatformName), d => d.deviceInfo.identifier === deviceIdentifier); + let deviceAppData = this.$deviceAppDataFactory.create(this.$projectData.projectId, this.platformData.normalizedPlatformName, device); + let localToDevicePaths = projectFilesManager.createLocalToDevicePaths(deviceAppData, path.join(this.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)); + let deviceHashService = this.$injector.resolve(AndroidDeviceHashService, { adb: adb, appIdentifier: this.$projectData.projectId }); + deviceHashService.uploadHashFileToDevice(localToDevicePaths).wait(); }).future()(); } diff --git a/lib/services/livesync/android-livesync-service.ts b/lib/services/livesync/android-livesync-service.ts index 91421212bb..27ca710432 100644 --- a/lib/services/livesync/android-livesync-service.ts +++ b/lib/services/livesync/android-livesync-service.ts @@ -1,6 +1,8 @@ /// "use strict"; +import {AndroidDebugBridge} from "../../common/mobile/android/android-debug-bridge"; +import {AndroidDeviceHashService} from "../../common/mobile/android/android-device-hash-service"; import Future = require("fibers/future"); import * as helpers from "../../common/helpers"; import liveSyncServiceBaseLib = require("./livesync-service-base"); @@ -13,7 +15,9 @@ class AndroidLiveSyncService extends liveSyncServiceBaseLib.LiveSyncServiceBase< constructor(_device: Mobile.IDevice, private $fs: IFileSystem, private $mobileHelper: Mobile.IMobileHelper, - private $options: IOptions) { + private $options: IOptions, + private $injector: IInjector, + private $projectData: IProjectData) { super(_device); } @@ -53,9 +57,15 @@ class AndroidLiveSyncService extends liveSyncServiceBaseLib.LiveSyncServiceBase< let deviceFilePath = this.$mobileHelper.buildDevicePath(deviceRootPath, "removedsync", relativeUnixPath); this.device.adb.executeShellCommand(["mkdir", "-p", path.dirname(deviceFilePath), "&&", "touch", deviceFilePath]).wait(); }); + + this.deviceHashService.removeHashes(localToDevicePaths).wait(); }).future()(); } + public afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { + return this.deviceHashService.uploadHashFileToDevice(localToDevicePaths); + } + private getDeviceRootPath(appIdentifier: string): string { return `/data/local/tmp/${appIdentifier}`; } @@ -77,5 +87,15 @@ class AndroidLiveSyncService extends liveSyncServiceBaseLib.LiveSyncServiceBase< return future; } + + private _deviceHashService: Mobile.IAndroidDeviceHashService; + private get deviceHashService(): Mobile.IAndroidDeviceHashService { + if (!this._deviceHashService) { + let adb = this.$injector.resolve(AndroidDebugBridge, { identifier: this.device.deviceInfo.identifier }); + this._deviceHashService = this.$injector.resolve(AndroidDeviceHashService, { adb: adb, appIdentifier: this.$projectData.projectId }); + } + + return this._deviceHashService; + } } $injector.register("androidLiveSyncServiceLocator", {factory: AndroidLiveSyncService}); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 10beed12ac..2bf14bd07a 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -311,7 +311,7 @@ export class PlatformService implements IPlatformService { }).future()(); } - public buildForDeploy(platform: string, buildConfig?: IBuildConfig): IFuture { + public buildForDeploy(platform: string, buildConfig?: IBuildConfig): IFuture { return (() => { platform = platform.toLowerCase(); if (!this.preparePlatform(platform).wait()) { @@ -321,7 +321,7 @@ export class PlatformService implements IPlatformService { let platformData = this.$platformsData.getPlatformData(platform); platformData.platformProjectService.buildForDeploy(platformData.projectRoot, buildConfig).wait(); this.$logger.out("Project successfully built"); - }).future()(); + }).future()(); } public copyLastOutput(platform: string, targetPath: string, settings: {isForDevice: boolean}): IFuture {