adb_kit/
parallel.rs

1use crate::device::ADB;
2use crate::error::{ADBError, ADBResult};
3use crate::app::PackageInfo;
4use log::{debug, warn};
5use rayon::prelude::*;
6use std::collections::HashMap;
7
8impl ADB {
9    /// 在多个设备上并行执行 shell 命令
10    ///
11    /// # 参数
12    ///
13    /// * `device_ids` - 设备 ID 列表
14    /// * `command` - 要执行的 shell 命令
15    ///
16    /// # 返回值
17    ///
18    /// 返回一个 HashMap,键为设备 ID,值为命令执行结果
19    pub fn parallel_shell(&self, device_ids: &[&str], command: &str) -> HashMap<String, ADBResult<String>> {
20        device_ids
21            .par_iter() // 使用 rayon 的并行迭代器
22            .map(|&id| {
23                (id.to_string(), self.shell(id, command))
24            })
25            .collect()
26    }
27
28    /// 在多个设备上并行安装应用
29    ///
30    /// # 参数
31    ///
32    /// * `device_ids` - 设备 ID 列表
33    /// * `apk_path` - APK 文件路径
34    ///
35    /// # 返回值
36    ///
37    /// 返回一个 HashMap,键为设备 ID,值为安装结果
38    pub fn parallel_install_app(&self, device_ids: &[&str], apk_path: &str) -> HashMap<String, ADBResult<()>> {
39        device_ids
40            .par_iter()
41            .map(|&id| {
42                (id.to_string(), self.install_app(id, apk_path))
43            })
44            .collect()
45    }
46
47    /// 在多个设备上并行卸载应用
48    ///
49    /// # 参数
50    ///
51    /// * `device_ids` - 设备 ID 列表
52    /// * `package_name` - 包名
53    ///
54    /// # 返回值
55    ///
56    /// 返回一个 HashMap,键为设备 ID,值为卸载结果
57    pub fn parallel_uninstall_app(&self, device_ids: &[&str], package_name: &str) -> HashMap<String, ADBResult<()>> {
58        device_ids
59            .par_iter()
60            .map(|&id| {
61                (id.to_string(), self.uninstall_app(id, package_name))
62            })
63            .collect()
64    }
65
66    /// 在多个设备上并行启动应用
67    ///
68    /// # 参数
69    ///
70    /// * `device_ids` - 设备 ID 列表
71    /// * `package_name` - 包名
72    /// * `activity` - 可选的 Activity 名称
73    ///
74    /// # 返回值
75    ///
76    /// 返回一个 HashMap,键为设备 ID,值为启动结果
77    pub fn parallel_start_app(
78        &self,
79        device_ids: &[&str],
80        package_name: &str,
81        activity: Option<&str>,
82    ) -> HashMap<String, ADBResult<bool>> {
83        device_ids
84            .par_iter()
85            .map(|&id| {
86                (id.to_string(), self.start_app(id, package_name, activity))
87            })
88            .collect()
89    }
90
91    /// 在多个设备上并行停止应用
92    ///
93    /// # 参数
94    ///
95    /// * `device_ids` - 设备 ID 列表
96    /// * `package_name` - 包名
97    ///
98    /// # 返回值
99    ///
100    /// 返回一个 HashMap,键为设备 ID,值为停止结果
101    pub fn parallel_stop_app(&self, device_ids: &[&str], package_name: &str) -> HashMap<String, ADBResult<()>> {
102        device_ids
103            .par_iter()
104            .map(|&id| {
105                (id.to_string(), self.stop_app(id, package_name))
106            })
107            .collect()
108    }
109
110    /// 在多个设备上并行获取包信息
111    ///
112    /// # 参数
113    ///
114    /// * `device_ids` - 设备 ID 列表
115    /// * `package_name` - 包名
116    ///
117    /// # 返回值
118    ///
119    /// 返回一个 HashMap,键为设备 ID,值为包信息
120    pub fn parallel_get_package_info(
121        &self,
122        device_ids: &[&str],
123        package_name: &str,
124    ) -> HashMap<String, ADBResult<PackageInfo>> {
125        device_ids
126            .par_iter()
127            .map(|&id| {
128                (id.to_string(), self.get_package_info_enhanced(id, package_name))
129            })
130            .collect()
131    }
132
133    /// 在多个设备上并行执行推送文件操作
134    ///
135    /// # 参数
136    ///
137    /// * `device_ids` - 设备 ID 列表
138    /// * `local_path` - 本地文件路径
139    /// * `device_path` - 设备上的目标路径
140    ///
141    /// # 返回值
142    ///
143    /// 返回一个 HashMap,键为设备 ID,值为推送结果
144    pub fn parallel_push(
145        &self,
146        device_ids: &[&str],
147        local_path: &str,
148        device_path: &str,
149    ) -> HashMap<String, ADBResult<()>> {
150        device_ids
151            .par_iter()
152            .map(|&id| {
153                (id.to_string(), self.push(id, local_path, device_path, None))
154            })
155            .collect()
156    }
157
158    /// 在多个设备上并行执行拉取文件操作
159    ///
160    /// # 参数
161    ///
162    /// * `operations` - 设备 ID 和文件路径的组合列表,每项包含(设备 ID, 设备文件路径, 本地目标路径)
163    ///
164    /// # 返回值
165    ///
166    /// 返回一个 HashMap,键为设备 ID,值为拉取结果
167    pub fn parallel_pull(
168        &self,
169        operations: &[(String, String, String)],
170    ) -> HashMap<String, ADBResult<()>> {
171        operations
172            .par_iter()
173            .map(|(device_id, device_path, local_path)| {
174                (device_id.clone(), self.pull(device_id, device_path, local_path, None))
175            })
176            .collect()
177    }
178
179    /// 检查多个设备是否在线
180    ///
181    /// # 参数
182    ///
183    /// * `device_ids` - 设备 ID 列表
184    ///
185    /// # 返回值
186    ///
187    /// 返回在线设备的列表
188    pub fn filter_online_devices(&self, device_ids: &[&str]) -> ADBResult<Vec<String>> {
189        let results = device_ids
190            .par_iter()
191            .map(|&id| {
192                (id.to_string(), self.is_device_online(id))
193            })
194            .collect::<HashMap<String, ADBResult<bool>>>();
195
196        let mut online_devices = Vec::new();
197        for (id, result) in results {
198            match result {
199                Ok(true) => online_devices.push(id),
200                Ok(false) => debug!("设备 {} 不在线", id),
201                Err(e) => warn!("检查设备 {} 状态时出错: {}", id, e),
202            }
203        }
204
205        Ok(online_devices)
206    }
207
208    /// 在所有在线设备上执行操作
209    ///
210    /// # 参数
211    ///
212    /// * `operation` - 要执行的操作闭包
213    ///
214    /// # 返回值
215    ///
216    /// 返回在线设备的操作结果
217    pub fn on_all_online_devices<F, T>(&self, operation: F) -> ADBResult<HashMap<String, ADBResult<T>>>
218    where
219        F: Fn(&str) -> ADBResult<T> + Send + Sync,
220        T: Send,
221    {
222        // 获取所有设备
223        let devices = self.list_devices()?;
224
225        // 筛选在线设备
226        let online_devices: Vec<String> = devices
227            .iter()
228            .filter(|d| d.is_online())
229            .map(|d| d.id.clone())
230            .collect();
231
232        if online_devices.is_empty() {
233            return Err(ADBError::DeviceError("没有在线设备".to_string()));
234        }
235
236        // 并行执行操作
237        let results = online_devices
238            .par_iter()
239            .map(|id| {
240                (id.clone(), operation(id))
241            })
242            .collect();
243
244        Ok(results)
245    }
246
247    /// 在所有指定设备上并行执行多个命令
248    pub fn parallel_commands(
249        &self,
250        device_ids: &[&str],
251        commands: &[&str],
252    ) -> HashMap<String, Vec<ADBResult<String>>> {
253        device_ids
254            .par_iter()
255            .map(|&id| {
256                let results = commands
257                    .iter()
258                    .map(|&cmd| self.shell(id, cmd))
259                    .collect();
260
261                (id.to_string(), results)
262            })
263            .collect()
264    }
265
266    /// 在所有在线设备上启动同一应用
267    pub fn start_app_on_all_devices(
268        &self,
269        package_name: &str,
270        activity: Option<&str>,
271    ) -> ADBResult<HashMap<String, ADBResult<bool>>> {
272        self.on_all_online_devices(|device_id| {
273            self.start_app(device_id, package_name, activity)
274        })
275    }
276
277    /// 在所有在线设备上停止同一应用
278    pub fn stop_app_on_all_devices(
279        &self,
280        package_name: &str,
281    ) -> ADBResult<HashMap<String, ADBResult<()>>> {
282        self.on_all_online_devices(|device_id| {
283            self.stop_app(device_id, package_name)
284        })
285    }
286}