@aiot-toolkit/emulator 2.0.2-beta.1 → 2.0.2-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,2 +1,15 @@
1
+ ## emulator
1
2
 
2
- emulator tool
3
+ QEMU模拟器
4
+
5
+ 模拟器的介绍可参考[开发帮助文档](https://xiaomi.f.mioffice.cn/docx/doxk4Rk6x67GanHlzQ8bEOrxtEe)里的「模拟器」章节
6
+
7
+ ## 目录结构
8
+
9
+ | 目录 | 描述 |
10
+ | -------- | ------------------------------------------------------------------------------- |
11
+ | avd | 模拟器的AVD,配置统一放置$HOME/.android/avd目录下 |
12
+ | instance | 模拟器实例,不同的Vela镜像版本会使用不同的instance,通过findInstance确定 |
13
+ | static | 创建AVD时需要用到的静态资源,常量配置文件 |
14
+ | typing | 接口定义 |
15
+ | utils | 工具函数 |
@@ -3,10 +3,22 @@ declare class VelaAvdCls {
3
3
  private avdHome;
4
4
  private sdkHome;
5
5
  constructor(avdResourcePaths: IAvdResourcePaths);
6
+ /**
7
+ * 创建Vela端的AVD,统一保存在.android目录下
8
+ * 1. 创建.android/advancedFeatures.ini文件
9
+ * 2. 创建.android/${avdName}.ini文件
10
+ * 3. 创建.android/${avdName}.avd/config.ini文件
11
+ * @param avdParams AVD参数,宽高、绑定的镜像路径等
12
+ * @returns
13
+ */
6
14
  createVelaAvd(avdParams: IAvdParams): boolean;
15
+ /** 根据AVD名字获取模拟器的详细信息 */
7
16
  getVelaAvdInfo(avdName: string): IAvdParams;
17
+ /** 根据名字删除AVD */
8
18
  deleteVelaAvd(avdName: string): boolean;
19
+ /** 获取已经创建的模拟器列表 */
9
20
  getVelaAvdList(): IAvdParams[];
21
+ /** 获取模拟器皮肤列表 */
10
22
  getVelaSkinList(): string[];
11
23
  }
12
24
  export default VelaAvdCls;
package/lib/avd/index.js CHANGED
@@ -3,17 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
6
+ const shared_utils_1 = require("@aiot-toolkit/shared-utils");
7
7
  const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
8
9
  const path_1 = __importDefault(require("path"));
9
10
  const constants_1 = require("../static/constants");
10
11
  const Avd_1 = require("../typing/Avd");
11
12
  const EAvdParamsToIni = {
12
- 'arm': {
13
- abiType: 'armeabi-v7a',
13
+ arm: {
14
+ abiType: 'armeabi-v7a'
14
15
  },
15
- 'arm64': {
16
- abiType: 'arm64-v8a',
16
+ arm64: {
17
+ abiType: 'arm64-v8a'
17
18
  }
18
19
  };
19
20
  class VelaAvdCls {
@@ -25,7 +26,21 @@ class VelaAvdCls {
25
26
  fs_1.default.mkdirSync(this.avdHome, { recursive: true });
26
27
  }
27
28
  }
29
+ /**
30
+ * 创建Vela端的AVD,统一保存在.android目录下
31
+ * 1. 创建.android/advancedFeatures.ini文件
32
+ * 2. 创建.android/${avdName}.ini文件
33
+ * 3. 创建.android/${avdName}.avd/config.ini文件
34
+ * @param avdParams AVD参数,宽高、绑定的镜像路径等
35
+ * @returns
36
+ */
28
37
  createVelaAvd(avdParams) {
38
+ // 在.android下创建advancedFeatures.ini文件
39
+ const advancedFeaturesIni = path_1.default.resolve(os_1.default.homedir(), '.android/advancedFeatures.ini');
40
+ if (!fs_1.default.existsSync(advancedFeaturesIni)) {
41
+ const iniSourcePath = path_1.default.join(__dirname, '../static/advancedFeatures.ini');
42
+ fs_1.default.copyFileSync(iniSourcePath, advancedFeaturesIni);
43
+ }
29
44
  const { avdName, avdArch, avdWidth, avdHeight, avdSkin, avdImagePath = constants_1.defaultImageHome } = avdParams;
30
45
  const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
31
46
  const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
@@ -66,9 +81,10 @@ class VelaAvdCls {
66
81
  return true;
67
82
  }
68
83
  catch (e) {
69
- throw (`createVelaAvd: ${e.message}`);
84
+ throw `createVelaAvd: ${e.message}`;
70
85
  }
71
86
  }
87
+ /** 根据AVD名字获取模拟器的详细信息 */
72
88
  getVelaAvdInfo(avdName) {
73
89
  const avdInfo = {
74
90
  avdName,
@@ -101,10 +117,11 @@ class VelaAvdCls {
101
117
  return avdInfo;
102
118
  }
103
119
  catch (err) {
104
- ColorConsole_1.default.log(`getVelaAvdInfo: ${err.message}`);
120
+ shared_utils_1.ColorConsole.log(`getVelaAvdInfo: ${err.message}`);
105
121
  return avdInfo;
106
122
  }
107
123
  }
124
+ /** 根据名字删除AVD */
108
125
  deleteVelaAvd(avdName) {
109
126
  const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
110
127
  const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
@@ -117,6 +134,7 @@ class VelaAvdCls {
117
134
  return false;
118
135
  }
119
136
  }
137
+ /** 获取已经创建的模拟器列表 */
120
138
  getVelaAvdList() {
121
139
  const avdList = [];
122
140
  const files = fs_1.default.readdirSync(this.avdHome);
@@ -131,6 +149,7 @@ class VelaAvdCls {
131
149
  }
132
150
  return avdList;
133
151
  }
152
+ /** 获取模拟器皮肤列表 */
134
153
  getVelaSkinList() {
135
154
  try {
136
155
  const skinList = [];
package/lib/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { default as VelaAvdCls } from './avd';
2
+ import { getSystemArch } from './utils';
2
3
  export * from './instance';
3
4
  export * from './typing/Avd';
4
5
  export * from './typing/Instance';
5
- export { VelaAvdCls };
6
+ export * from './static/constants';
7
+ export { VelaAvdCls, getSystemArch };
package/lib/index.js CHANGED
@@ -17,9 +17,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.VelaAvdCls = void 0;
20
+ exports.getSystemArch = exports.VelaAvdCls = void 0;
21
21
  const avd_1 = __importDefault(require("./avd"));
22
22
  Object.defineProperty(exports, "VelaAvdCls", { enumerable: true, get: function () { return avd_1.default; } });
23
+ const utils_1 = require("./utils");
24
+ Object.defineProperty(exports, "getSystemArch", { enumerable: true, get: function () { return utils_1.getSystemArch; } });
23
25
  __exportStar(require("./instance"), exports);
24
26
  __exportStar(require("./typing/Avd"), exports);
25
27
  __exportStar(require("./typing/Instance"), exports);
28
+ __exportStar(require("./static/constants"), exports);
@@ -7,37 +7,41 @@ import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
7
7
  * CommonInstance
8
8
  */
9
9
  declare class CommonInstance {
10
+ quickappStartedFlag: RegExp;
10
11
  projectPath: string;
11
12
  sdkHome: string;
12
13
  avdHome: string;
13
- adbPort: number;
14
- debugPort: number;
15
14
  velaAvdCls: VelaAvdCls;
16
15
  goldfishProcess: ChildProcess | undefined;
17
16
  isFirstStart: boolean;
18
17
  startOptions: IStartOptions | undefined;
19
18
  projectInfo: IManifest;
20
19
  isRpk: boolean;
20
+ isDistributedApp: boolean;
21
+ sn?: string;
21
22
  constructor(params: INewGoldfishInstanceParams);
22
23
  /** 获取模拟器二进制文件所在位置 */
23
24
  getEmulatorBinPath(): string;
24
25
  /** 在goldfish模拟器中运行快应用 */
25
26
  start(options: IStartOptions): Promise<void>;
26
- /** 在goldfish中启动快应用 */
27
- startupQuickApp(options: IStartOptions): void;
28
- /** 启动goldfish模拟器 */
29
- startGoldfish(options: IStartOptions): Promise<void>;
30
- /** 通过adb连接模拟器 */
31
- connectGoldfish(): Promise<boolean>;
32
- /** 将打包后的文件推到挂载的快应用目录 */
33
- pushRpk(sourceRoot: string): Promise<void>;
27
+ /**
28
+ * 判断模拟器是否 ready
29
+ */
30
+ isConnected(): Promise<boolean>;
31
+ /**
32
+ * 通过adb连接模拟器。
33
+ * 时间限制为 @param timeout 秒,超时则表示连接失败
34
+ * @returns
35
+ */
36
+ connectGoldfish(timeout?: number): Promise<unknown>;
34
37
  /** 杀死进程 */
35
38
  killProcess(currProcess?: ChildProcess): void;
36
39
  /** 停止模拟器并释放相关资源 */
37
40
  stop(): Promise<void>;
38
41
  /** 重启模拟器 */
39
- restart(): void;
42
+ restart(): Promise<void>;
40
43
  /** 创建server */
41
44
  createWebsockeServer(): Promise<void>;
45
+ connectDevice(): Promise<string | void>;
42
46
  }
43
47
  export default CommonInstance;
@@ -36,12 +36,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  const UxFileUtils_1 = __importDefault(require("@aiot-toolkit/aiotpack/lib/utils/ux/UxFileUtils"));
39
- const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
39
+ const shared_utils_1 = require("@aiot-toolkit/shared-utils");
40
40
  const adbMiwt = __importStar(require("@miwt/adb"));
41
41
  const child_process_1 = require("child_process");
42
+ const fs_extra_1 = __importDefault(require("fs-extra"));
42
43
  const os_1 = __importDefault(require("os"));
43
44
  const path_1 = __importDefault(require("path"));
44
- const portfinder_1 = __importDefault(require("portfinder"));
45
45
  const ws_1 = require("ws");
46
46
  const avd_1 = __importDefault(require("../avd"));
47
47
  const constants_1 = require("../static/constants");
@@ -51,13 +51,19 @@ const utils_1 = require("../utils");
51
51
  */
52
52
  class CommonInstance {
53
53
  constructor(params) {
54
- this.adbPort = 5555;
55
- this.debugPort = 10055;
54
+ this.quickappStartedFlag = /__loadChunks/;
56
55
  this.isFirstStart = true;
56
+ this.isDistributedApp = false;
57
57
  this.projectPath = params.projectPath;
58
58
  this.sdkHome = params.sdkHome || constants_1.defaultSDKHome;
59
59
  this.avdHome = params.avdHome || constants_1.defaultAvdHome;
60
60
  this.isRpk = params.sourceRoot === './';
61
+ const projectJsonFile = path_1.default.resolve(this.projectPath, '.project.json');
62
+ const projectJsonExist = fs_extra_1.default.existsSync(projectJsonFile);
63
+ if (projectJsonExist) {
64
+ const projectJsonInfo = fs_extra_1.default.readJSONSync(projectJsonFile);
65
+ this.isDistributedApp = projectJsonInfo.projectType === 'distributed';
66
+ }
61
67
  this.velaAvdCls = new avd_1.default({
62
68
  sdkHome: this.sdkHome,
63
69
  avdHome: this.avdHome
@@ -74,154 +80,63 @@ class CommonInstance {
74
80
  }
75
81
  /** 在goldfish模拟器中运行快应用 */
76
82
  start(options) {
77
- return __awaiter(this, void 0, void 0, function* () {
78
- this.startOptions = options;
79
- // 启动模拟器
80
- yield this.startGoldfish(options);
81
- const connected = yield this.connectGoldfish();
82
- if (connected) {
83
- ColorConsole_1.default.log('### Emulator ### Goldfish emulator connected successfully');
84
- if (this.isFirstStart && this.startOptions.serverPort) {
85
- yield this.createWebsockeServer();
86
- }
87
- // adb push快应用到模拟器的/data/app目录
88
- const buildedFilesPath = this.isRpk ? this.projectPath : path_1.default.resolve(this.projectPath, './build');
89
- this.pushRpk(buildedFilesPath);
90
- // 在模拟器中启动快应用
91
- this.startupQuickApp(options);
92
- this.isFirstStart = false;
93
- }
94
- else {
95
- ColorConsole_1.default.throw('### Emulator ### Failed to connect emulator, please check whether the adb is normal');
96
- }
97
- });
98
- }
99
- /** 在goldfish中启动快应用 */
100
- startupQuickApp(options) {
101
- try {
102
- let vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp app/${this.projectInfo.package} &`;
103
- if (options.devtool) {
104
- vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp --jsdebugger=10.0.2.15:101 app/${this.projectInfo.package} &`;
105
- }
106
- ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${vappCmd}`);
107
- // vapp进程会一直pending,不会退出。这里必须加stdio: 'ignore',否则快应用无法运行成功
108
- adbMiwt.execAdbCmdAsync(vappCmd, { stdio: 'ignore', encoding: 'utf-8' });
109
- }
110
- catch (e) {
111
- ColorConsole_1.default.error(`### Emulator ### Failed to startup quickapp: ${e.message}`);
112
- }
83
+ return __awaiter(this, void 0, void 0, function* () { });
113
84
  }
114
- /** 启动goldfish模拟器 */
115
- startGoldfish(options) {
116
- var _a;
85
+ /**
86
+ * 判断模拟器是否 ready
87
+ */
88
+ isConnected() {
117
89
  return __awaiter(this, void 0, void 0, function* () {
118
- const { avdName, devtool } = options;
119
- const emulatorBin = this.getEmulatorBinPath();
120
- ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
121
- const avdInfo = this.velaAvdCls.getVelaAvdInfo(avdName);
122
- const { avdArch, avdImagePath } = avdInfo;
123
- this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
124
- ColorConsole_1.default.log(`### Emulator ### adb port: ${this.adbPort}`);
125
- if (!avdImagePath) {
126
- return ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
127
- }
128
- const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
129
- ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
130
- // 端口映射
131
- const adbPortMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
132
- let debugPortMappingStr = '';
133
- if (devtool) {
134
- debugPortMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
135
- }
136
- // 文件系统配置,第一次使用fatfs镜像挂载,后续使用adb push更新应用
137
- const systemImageBin = path_1.default.resolve(this.sdkHome, 'tools/image/system.img');
138
- const dataImageBin = path_1.default.resolve(this.sdkHome, 'tools/image/data.img');
139
- const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin} \
140
- -device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
141
- -drive index=1,id=userdata,if=none,format=raw,file=${dataImageBin} \
142
- -device virtio-blk-device,bus=virtio-mmio-bus.1,drive=userdata \
143
- -device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
144
- // vnc配置
145
- let windowStr = '';
146
- let vncStr = '';
147
- if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.vncPort) {
148
- windowStr = '-no-window';
149
- const portSuffix = this.startOptions.vncPort - constants_1.defaultVncPort;
150
- vncStr = `-vnc :${portSuffix}`;
151
- }
152
- const stdioType = options.disableNSH ? 'pipe' : 'inherit';
153
- // 启动goldfish的命令和参数
154
- const cmd = `${emulatorBin} -nuttx -avd ${avdName} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${adbPortMappingStr} ${debugPortMappingStr} -qemu ${windowStr} ${vncStr} ${imageMountStr}`;
155
- const spawnArgs = cmd.split(' ');
156
- const spawnBin = spawnArgs.shift();
157
- ColorConsole_1.default.log(`### Emulator ### Start CMD: ${cmd}`);
158
- return new Promise((resolve) => {
159
- var _a, _b;
160
- this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: stdioType, shell: true });
161
- if (options.disableNSH) {
162
- (_a = this.goldfishProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
163
- console.log(data.toString());
164
- if (data.toString().includes('(NSH)')) {
165
- ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
166
- resolve();
167
- }
168
- });
169
- (_b = this.goldfishProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
170
- console.log(data.toString());
171
- });
172
- }
173
- else {
174
- setTimeout(() => {
175
- ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
176
- resolve();
177
- }, 2000);
178
- }
179
- this.goldfishProcess.on('exit', (code) => {
180
- ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
181
- });
182
- });
90
+ return (0, utils_1.tryRun)(() => __awaiter(this, void 0, void 0, function* () {
91
+ const devices = yield adbMiwt.getAdbDevices();
92
+ shared_utils_1.ColorConsole.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
93
+ const curDev = devices.find((t) => t.sn === this.sn);
94
+ return (curDev === null || curDev === void 0 ? void 0 : curDev.status) === 'device';
95
+ }), 10, 500);
183
96
  });
184
97
  }
185
- /** 通过adb连接模拟器 */
186
- connectGoldfish() {
98
+ /**
99
+ * 通过adb连接模拟器。
100
+ * 时间限制为 @param timeout 秒,超时则表示连接失败
101
+ * @returns
102
+ */
103
+ connectGoldfish(timeout = 10) {
187
104
  return __awaiter(this, void 0, void 0, function* () {
188
- let adbConnected = false;
105
+ let adbConnected = false; // adb是否连接成功
106
+ let enableLoop = true; // 是否允许循环,用于终止adb的连接
107
+ let needKill = false;
189
108
  const connectFn = () => __awaiter(this, void 0, void 0, function* () {
190
- const sn = `127.0.0.1:${this.adbPort}`;
191
- while (!adbConnected) {
192
- const adbKillCmd = `adb kill-server`;
193
- ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
194
- adbMiwt.execAdbCmdSync(adbKillCmd);
195
- const adbConnectCmd = `adb connect ${sn}`;
196
- ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
197
- const str = adbMiwt.execAdbCmdSync(adbConnectCmd);
198
- ColorConsole_1.default.log(`### Emulator ### ${str}`);
109
+ while (enableLoop && !adbConnected) {
110
+ if (needKill) {
111
+ const adbKillCmd = `adb kill-server`;
112
+ shared_utils_1.ColorConsole.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
113
+ yield adbMiwt.execAdbCmdAsync(adbKillCmd);
114
+ }
115
+ const str = yield this.connectDevice();
116
+ shared_utils_1.ColorConsole.log(`### Emulator ### ${str}`);
117
+ // 查询模拟器的状态是否为“device”
199
118
  const devices = yield adbMiwt.getAdbDevices();
200
- ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
201
- adbConnected =
202
- devices.filter((item) => item.sn === sn && item.status === 'device').length > 0;
119
+ shared_utils_1.ColorConsole.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
120
+ const curDev = devices.find((t) => t.sn === this.sn);
121
+ if ((curDev === null || curDev === void 0 ? void 0 : curDev.status) === 'offline')
122
+ needKill = true;
123
+ adbConnected = devices.some((item) => item.sn === this.sn && item.status === 'device');
203
124
  }
204
- Promise.resolve(adbConnected);
125
+ return Promise.resolve(adbConnected);
205
126
  });
206
- yield Promise.race([
127
+ const res = yield Promise.race([
207
128
  connectFn(),
208
129
  new Promise((resolve) => {
209
- setTimeout(() => resolve(false), 600 * 1000);
130
+ setTimeout(() => {
131
+ if (!adbConnected) {
132
+ enableLoop = false;
133
+ }
134
+ // 超时则认为adb没有连接成功
135
+ resolve(false);
136
+ }, timeout * 1000);
210
137
  })
211
138
  ]);
212
- return adbConnected;
213
- });
214
- }
215
- /** 将打包后的文件推到挂载的快应用目录 */
216
- pushRpk(sourceRoot) {
217
- return __awaiter(this, void 0, void 0, function* () {
218
- const sn = `127.0.0.1:${this.adbPort}`;
219
- const { package: appPackageName } = UxFileUtils_1.default.getMainfestInfo(this.projectPath);
220
- const appRunDir = '/data/app';
221
- ColorConsole_1.default.log(`### Emulator ### Pushing ${appPackageName} to ${appRunDir}`);
222
- (0, child_process_1.execSync)(`adb -s ${sn} shell mkdir ${appRunDir}/${appPackageName}`, { stdio: 'ignore' });
223
- yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push ${sourceRoot}/* ${appRunDir}/${appPackageName}`);
224
- ColorConsole_1.default.log(`### Emulator ### Push ${appPackageName} to ${appRunDir} successfully`);
139
+ return res;
225
140
  });
226
141
  }
227
142
  /** 杀死进程 */
@@ -240,13 +155,13 @@ class CommonInstance {
240
155
  }
241
156
  }
242
157
  catch (err) {
243
- ColorConsole_1.default.log(`### Emulator ### kill process get error :\n${err.stack}`);
158
+ shared_utils_1.ColorConsole.log(`### Emulator ### kill process get error :\n${err.stack}`);
244
159
  }
245
160
  }
246
161
  }
247
162
  /** 停止模拟器并释放相关资源 */
248
163
  stop() {
249
- var _a;
164
+ var _a, _b;
250
165
  return __awaiter(this, void 0, void 0, function* () {
251
166
  if (this.goldfishProcess) {
252
167
  // Linux删除goldfishProcess后,子进程仍存在,导致模拟器窗口未关闭
@@ -255,31 +170,36 @@ class CommonInstance {
255
170
  // process.kill(this.goldfishProcess.pid + 1)
256
171
  // }
257
172
  this.killProcess(this.goldfishProcess);
258
- yield (0, utils_1.killProcessByCmd)((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.avdName);
173
+ if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.avdName) {
174
+ const emulatorProcessFlag = `-avd ${(_b = this.startOptions) === null || _b === void 0 ? void 0 : _b.avdName} -avd-arch`;
175
+ yield (0, utils_1.killProcessByCmd)(emulatorProcessFlag);
176
+ }
259
177
  this.goldfishProcess = undefined;
260
178
  }
261
179
  });
262
180
  }
263
181
  /** 重启模拟器 */
264
182
  restart() {
265
- this.stop();
266
- this.start(this.startOptions);
183
+ return __awaiter(this, void 0, void 0, function* () {
184
+ yield this.stop();
185
+ this.start(this.startOptions);
186
+ });
267
187
  }
268
188
  /** 创建server */
269
189
  createWebsockeServer() {
270
190
  var _a;
271
191
  return __awaiter(this, void 0, void 0, function* () {
272
192
  const wsServer = new ws_1.WebSocketServer({
273
- port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort,
193
+ port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort
274
194
  });
275
- wsServer.on('connection', socket => {
276
- ColorConsole_1.default.success(`### App Socket server ### Websocket connects to websocket server`);
277
- socket.on('error', err => {
278
- ColorConsole_1.default.error(`### App Socket server ### Websocket server error: ${err.message}`);
195
+ wsServer.on('connection', (socket) => {
196
+ shared_utils_1.ColorConsole.success(`### App Socket server ### Websocket connects to websocket server`);
197
+ socket.on('error', (err) => {
198
+ shared_utils_1.ColorConsole.error(`### App Socket server ### Websocket server error: ${err.message}`);
279
199
  });
280
- socket.on('message', data => {
200
+ socket.on('message', (data) => {
281
201
  const message = JSON.parse(data.toString());
282
- ColorConsole_1.default.log(`### App Socket server ### Websocket server get data: ${data}`);
202
+ shared_utils_1.ColorConsole.log(`### App Socket server ### Websocket server get data: ${data}`);
283
203
  if (message.type === 'restart') {
284
204
  this.restart();
285
205
  }
@@ -290,5 +210,32 @@ class CommonInstance {
290
210
  });
291
211
  });
292
212
  }
213
+ connectDevice() {
214
+ return __awaiter(this, void 0, void 0, function* () {
215
+ const adbConnectCmd = `adb connect ${this.sn}`;
216
+ shared_utils_1.ColorConsole.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
217
+ let pending = true;
218
+ const p1 = adbMiwt.execAdbCmdAsync(adbConnectCmd);
219
+ let timer;
220
+ // 超过一定时间还没有连接成功,则 kill-server 后再试
221
+ // 用于处理 adb connect 一直 pending 的情况
222
+ const p2 = new Promise((resolve, reject) => {
223
+ timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
224
+ if (pending) {
225
+ const adbKillCmd = `adb kill-server`;
226
+ shared_utils_1.ColorConsole.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
227
+ yield adbMiwt.execAdbCmdAsync(adbKillCmd);
228
+ }
229
+ resolve();
230
+ }), 10 * 1000);
231
+ });
232
+ p1.then((r) => {
233
+ pending = false;
234
+ clearTimeout(timer);
235
+ console.log(r);
236
+ });
237
+ return Promise.race([p1, p2]);
238
+ });
239
+ }
293
240
  }
294
241
  exports.default = CommonInstance;
@@ -1,15 +1,45 @@
1
1
  import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
2
2
  import CommonInstance from './common';
3
3
  declare class GoldfishInstance extends CommonInstance {
4
- private appRunDir;
4
+ appRunDir: string;
5
+ emulatorStartedFlag: string;
5
6
  constructor(params: INewGoldfishInstanceParams);
6
- /** 在goldfish模拟器中运行快应用 */
7
+ /**
8
+ * 1. 启动模拟器
9
+ * 2. 启动成功后,adb连接模拟器
10
+ * 3. 连接成功后,在模拟器中启动快应用
11
+ */
7
12
  start(options: IStartOptions): Promise<void>;
8
- /** 在goldfish中启动快应用 */
13
+ /**
14
+ * 在模拟器中启动快应用
15
+ * 通过vapp命令启动,调试时需额外配置--jsdebugger参数
16
+ * @param options
17
+ */
9
18
  startupQuickApp(options: IStartOptions): Promise<void>;
10
- /** 启动goldfish模拟器 */
19
+ /**
20
+ * 启动模拟器
21
+ * 1. 通过options生成模拟器的启动命令
22
+ * 2. 执行启动命令
23
+ * 3. 判断模拟器是否启动成功
24
+ * 3.1 若disableNSH=true,输出流中匹配到(NSH),认为模拟器启动成功了
25
+ * 3.2 若disableNSH=false,认为2s过后模拟器启动成功了
26
+ * @param options
27
+ * @returns
28
+ */
11
29
  startGoldfish(options: IStartOptions): Promise<void>;
12
- /** 将打包后的文件推到挂载的快应用目录 */
30
+ /**
31
+ * 将目录通过adb push到模拟器中
32
+ * @param sourceRoot
33
+ */
13
34
  pushRpk(sourceRoot: string): Promise<void>;
35
+ /**
36
+ * 重新推送,然后重启应用
37
+ */
38
+ pushAndReloadApp(): Promise<void>;
39
+ /**
40
+ * 重启应用
41
+ */
42
+ reloadApp(): Promise<void>;
43
+ reboot(): Promise<boolean>;
14
44
  }
15
45
  export default GoldfishInstance;