Skip to content

Commit 7aa666d

Browse files
Merge pull request #4020 from NativeScript/vladimirov/prepare-perf-improvements-2
feat: speed-up livesync with HMR
2 parents 64b43df + 2729360 commit 7aa666d

File tree

5 files changed

+87
-54
lines changed

5 files changed

+87
-54
lines changed

lib/common/definitions/mobile.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ declare module Mobile {
149149
muted?: boolean;
150150
}
151151

152-
interface IDeviceAppData extends IPlatform {
152+
interface IDeviceAppData extends IPlatform, IConnectTimeoutOption {
153153
appIdentifier: string;
154154
device: Mobile.IDevice;
155155
getDeviceProjectRootPath(): Promise<string>;

lib/definitions/livesync.d.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,14 @@ interface IShouldSkipEmitLiveSyncNotification {
343343
interface IAttachDebuggerOptions extends IDebuggingAdditionalOptions, IEnableDebuggingDeviceOptions, IIsEmulator, IPlatform, IOptionalOutputPath {
344344
}
345345

346-
interface ILiveSyncWatchInfo extends IProjectDataComposition, IHasUseHotModuleReloadOption {
346+
interface IConnectTimeoutOption {
347+
/**
348+
* Time to wait for successful connection. Defaults to 30000 miliseconds.
349+
*/
350+
connectTimeout?: number;
351+
}
352+
353+
interface ILiveSyncWatchInfo extends IProjectDataComposition, IHasUseHotModuleReloadOption, IConnectTimeoutOption {
347354
filesToRemove: string[];
348355
filesToSync: string[];
349356
isReinstalled: boolean;
@@ -362,7 +369,7 @@ interface ILiveSyncResultInfo extends IHasUseHotModuleReloadOption {
362369

363370
interface IAndroidLiveSyncResultInfo extends ILiveSyncResultInfo, IAndroidLivesyncSyncOperationResult { }
364371

365-
interface IFullSyncInfo extends IProjectDataComposition, IHasUseHotModuleReloadOption {
372+
interface IFullSyncInfo extends IProjectDataComposition, IHasUseHotModuleReloadOption, IConnectTimeoutOption {
366373
device: Mobile.IDevice;
367374
watch: boolean;
368375
syncAllFiles: boolean;
@@ -510,7 +517,7 @@ interface IDoSyncOperationOptions {
510517
operationId?: string
511518
}
512519

513-
interface IAndroidLivesyncToolConfiguration {
520+
interface IAndroidLivesyncToolConfiguration extends IConnectTimeoutOption {
514521
/**
515522
* The application identifier.
516523
*/
@@ -531,10 +538,6 @@ interface IAndroidLivesyncToolConfiguration {
531538
* If provider will call it when an error occurs.
532539
*/
533540
errorHandler?: any;
534-
/**
535-
* Time to wait for successful connection. Defaults to 30000 miliseconds.
536-
*/
537-
connectTimeout?: number;
538541
}
539542

540543
interface IAndroidLivesyncSyncOperationResult {

lib/services/livesync/android-device-livesync-sockets-service.ts

+18-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,23 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe
2323
private $fs: IFileSystem,
2424
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
2525
$filesHashService: IFilesHashService) {
26-
super($injector, $platformsData, $filesHashService, $logger, device);
27-
this.livesyncTool = this.$injector.resolve(AndroidLivesyncTool);
26+
super($injector, $platformsData, $filesHashService, $logger, device);
27+
this.livesyncTool = this.$injector.resolve(AndroidLivesyncTool);
2828
}
2929

3030
public async beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
31-
const pathToLiveSyncFile = temp.path({ prefix: "livesync" });
32-
this.$fs.writeFile(pathToLiveSyncFile, "");
33-
await this.device.fileSystem.putFile(pathToLiveSyncFile, this.getPathToLiveSyncFileOnDevice(deviceAppData.appIdentifier), deviceAppData.appIdentifier);
34-
await this.device.applicationManager.startApplication({ appId: deviceAppData.appIdentifier, projectName: this.data.projectName, justLaunch: true });
35-
await this.connectLivesyncTool(this.data.projectIdentifiers.android);
31+
if (!this.livesyncTool.hasConnection()) {
32+
try {
33+
const pathToLiveSyncFile = temp.path({ prefix: "livesync" });
34+
this.$fs.writeFile(pathToLiveSyncFile, "");
35+
await this.device.fileSystem.putFile(pathToLiveSyncFile, this.getPathToLiveSyncFileOnDevice(deviceAppData.appIdentifier), deviceAppData.appIdentifier);
36+
await this.device.applicationManager.startApplication({ appId: deviceAppData.appIdentifier, projectName: this.data.projectName, justLaunch: true });
37+
await this.connectLivesyncTool(this.data.projectIdentifiers.android, deviceAppData.connectTimeout);
38+
} catch (err) {
39+
await this.device.fileSystem.deleteFile(this.getPathToLiveSyncFileOnDevice(deviceAppData.appIdentifier), deviceAppData.appIdentifier);
40+
throw err;
41+
}
42+
}
3643
}
3744

3845
private getPathToLiveSyncFileOnDevice(appIdentifier: string): string {
@@ -59,7 +66,7 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe
5966

6067
if (liveSyncInfo.modifiedFilesData.length) {
6168
const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo, liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform);
62-
const doSyncPromise = this.livesyncTool.sendDoSyncOperation({ doRefresh: canExecuteFastSync, operationId});
69+
const doSyncPromise = this.livesyncTool.sendDoSyncOperation({ doRefresh: canExecuteFastSync, operationId });
6370

6471
const syncInterval: NodeJS.Timer = setInterval(() => {
6572
if (this.livesyncTool.isOperationInProgress(operationId)) {
@@ -114,14 +121,15 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe
114121
await this.livesyncTool.sendDirectory(projectFilesPath);
115122
}
116123

117-
private async connectLivesyncTool(appIdentifier: string) {
124+
private async connectLivesyncTool(appIdentifier: string, connectTimeout?: number) {
118125
const platformData = this.$platformsData.getPlatformData(this.$devicePlatformsConstants.Android, this.data);
119126
const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME);
120127
if (!this.livesyncTool.hasConnection()) {
121128
await this.livesyncTool.connect({
122129
appIdentifier,
123130
deviceIdentifier: this.device.deviceInfo.identifier,
124-
appPlatformsPath: projectFilesPath
131+
appPlatformsPath: projectFilesPath,
132+
connectTimeout
125133
});
126134
}
127135
}

lib/services/livesync/livesync-service.ts

+56-35
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
594594
let filesToRemove: string[] = [];
595595
let timeoutTimer: NodeJS.Timer;
596596

597-
const startSyncFilesTimeout = (platform?: string) => {
597+
const startSyncFilesTimeout = (platform?: string, opts?: { calledFromHook: boolean }) => {
598598
timeoutTimer = setTimeout(async () => {
599599
if (platform && liveSyncData.bundle) {
600600
filesToSync = filesToSyncMap[platform];
@@ -636,6 +636,52 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
636636
const liveSyncProcessInfo = this.liveSyncProcessesInfo[projectData.projectDir];
637637
const deviceBuildInfoDescriptor = _.find(liveSyncProcessInfo.deviceDescriptors, dd => dd.identifier === device.deviceInfo.identifier);
638638

639+
const settings: ILiveSyncWatchInfo = {
640+
liveSyncDeviceInfo: deviceBuildInfoDescriptor,
641+
projectData,
642+
filesToRemove: currentFilesToRemove,
643+
filesToSync: currentFilesToSync,
644+
isReinstalled: false,
645+
syncAllFiles: liveSyncData.watchAllFiles,
646+
hmrData: currentHmrData,
647+
useHotModuleReload: liveSyncData.useHotModuleReload,
648+
force: liveSyncData.force,
649+
connectTimeout: 1000
650+
};
651+
652+
const service = this.getLiveSyncService(device.deviceInfo.platform);
653+
654+
const watchAction = async (watchInfo: ILiveSyncWatchInfo): Promise<void> => {
655+
let liveSyncResultInfo = await service.liveSyncWatchAction(device, watchInfo);
656+
657+
await this.refreshApplication(projectData, liveSyncResultInfo, deviceBuildInfoDescriptor.debugOptions, deviceBuildInfoDescriptor.outputPath);
658+
659+
// If didRecover is true, this means we were in ErrorActivity and fallback files were already transfered and app will be restarted.
660+
if (!liveSyncResultInfo.didRecover && liveSyncData.useHotModuleReload && currentHmrData.hash) {
661+
const status = await this.$hmrStatusService.getHmrStatus(device.deviceInfo.identifier, currentHmrData.hash);
662+
if (status === HmrConstants.HMR_ERROR_STATUS) {
663+
watchInfo.filesToSync = currentHmrData.fallbackFiles[device.deviceInfo.platform];
664+
liveSyncResultInfo = await service.liveSyncWatchAction(device, watchInfo);
665+
// We want to force a restart of the application.
666+
liveSyncResultInfo.isFullSync = true;
667+
await this.refreshApplication(projectData, liveSyncResultInfo, deviceBuildInfoDescriptor.debugOptions, deviceBuildInfoDescriptor.outputPath);
668+
}
669+
}
670+
671+
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
672+
};
673+
674+
if (liveSyncData.useHotModuleReload && opts && opts.calledFromHook) {
675+
try {
676+
this.$logger.trace("Try executing watch action without any preparation of files.");
677+
await watchAction(settings);
678+
this.$logger.trace("Successfully executed watch action without any preparation of files.");
679+
return;
680+
} catch (err) {
681+
this.$logger.trace(`Error while trying to execute fast sync. Now we'll check the state of the app and we'll try to resurrect from the error. The error is: ${err}`);
682+
}
683+
}
684+
639685
const appInstalledOnDeviceResult = await this.ensureLatestAppPackageIsInstalledOnDevice({
640686
device,
641687
preparedPlatforms,
@@ -653,41 +699,15 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
653699
skipModulesNativeCheck: !liveSyncData.watchAllFiles
654700
}, { skipNativePrepare: deviceBuildInfoDescriptor.skipNativePrepare });
655701

702+
settings.isReinstalled = appInstalledOnDeviceResult.appInstalled;
703+
settings.connectTimeout = null;
704+
656705
if (liveSyncData.useHotModuleReload && appInstalledOnDeviceResult.appInstalled) {
657706
const additionalFilesToSync = currentHmrData && currentHmrData.fallbackFiles && currentHmrData.fallbackFiles[device.deviceInfo.platform];
658707
_.each(additionalFilesToSync, fileToSync => currentFilesToSync.push(fileToSync));
659708
}
660709

661-
const service = this.getLiveSyncService(device.deviceInfo.platform);
662-
const settings: ILiveSyncWatchInfo = {
663-
liveSyncDeviceInfo: deviceBuildInfoDescriptor,
664-
projectData,
665-
filesToRemove: currentFilesToRemove,
666-
filesToSync: currentFilesToSync,
667-
isReinstalled: appInstalledOnDeviceResult.appInstalled,
668-
syncAllFiles: liveSyncData.watchAllFiles,
669-
hmrData: currentHmrData,
670-
useHotModuleReload: liveSyncData.useHotModuleReload,
671-
force: liveSyncData.force
672-
};
673-
674-
let liveSyncResultInfo = await service.liveSyncWatchAction(device, settings);
675-
676-
await this.refreshApplication(projectData, liveSyncResultInfo, deviceBuildInfoDescriptor.debugOptions, deviceBuildInfoDescriptor.outputPath);
677-
678-
//If didRecover is true, this means we were in ErrorActivity and fallback files were already transfered and app will be restarted.
679-
if (!liveSyncResultInfo.didRecover && liveSyncData.useHotModuleReload && currentHmrData.hash) {
680-
const status = await this.$hmrStatusService.getHmrStatus(device.deviceInfo.identifier, currentHmrData.hash);
681-
if (status === HmrConstants.HMR_ERROR_STATUS) {
682-
settings.filesToSync = currentHmrData.fallbackFiles[device.deviceInfo.platform];
683-
liveSyncResultInfo = await service.liveSyncWatchAction(device, settings);
684-
//We want to force a restart of the application.
685-
liveSyncResultInfo.isFullSync = true;
686-
await this.refreshApplication(projectData, liveSyncResultInfo, deviceBuildInfoDescriptor.debugOptions, deviceBuildInfoDescriptor.outputPath);
687-
}
688-
}
689-
690-
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
710+
await watchAction(settings);
691711
},
692712
(device: Mobile.IDevice) => {
693713
const liveSyncProcessInfo = this.liveSyncProcessesInfo[projectData.projectDir];
@@ -715,7 +735,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
715735
});
716736
}
717737
}
718-
}, 250);
738+
}, liveSyncData.useHotModuleReload ? 0 : 250);
719739

720740
this.liveSyncProcessesInfo[liveSyncData.projectDir].timer = timeoutTimer;
721741
};
@@ -738,11 +758,12 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
738758
hmrData,
739759
filesToRemove,
740760
startSyncFilesTimeout: async (platform: string) => {
761+
const opts = { calledFromHook: true };
741762
if (platform) {
742-
await startSyncFilesTimeout(platform);
763+
await startSyncFilesTimeout(platform, opts);
743764
} else {
744765
// This code is added for backwards compatibility with old versions of nativescript-dev-webpack plugin.
745-
await startSyncFilesTimeout();
766+
await startSyncFilesTimeout(null, opts);
746767
}
747768
}
748769
}
@@ -823,7 +844,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
823844
}
824845
}
825846

826-
public emitLivesyncEvent (event: string, livesyncData: ILiveSyncEventData): boolean {
847+
public emitLivesyncEvent(event: string, livesyncData: ILiveSyncEventData): boolean {
827848
this.$logger.trace(`Will emit event ${event} with data`, livesyncData);
828849
return this.emit(event, livesyncData);
829850
}

lib/services/livesync/platform-livesync-service-base.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ export abstract class PlatformLiveSyncServiceBase {
135135
platform: syncInfo.device.deviceInfo.platform,
136136
getDeviceProjectRootPath: () => this.$devicePathProvider.getDeviceProjectRootPath(syncInfo.device, deviceProjectRootOptions),
137137
deviceSyncZipPath: this.$devicePathProvider.getDeviceSyncZipPath(syncInfo.device),
138-
isLiveSyncSupported: async () => true
138+
isLiveSyncSupported: async () => true,
139+
connectTimeout: syncInfo.connectTimeout
139140
};
140141
}
141142

0 commit comments

Comments
 (0)