使用UiautomatorViewer工具快照屏幕时,感觉速度有点慢,所以这边就想着修改下源码来提升下速度,准确来说,应该算是换一种方式来快照屏幕。
主要的想法:新增一个按钮,添加一些按钮事件(保留了原本的快照功能)
按钮事件思路:
-
1、创建两个线程,线程A进行dump当前界面的层级结构数据,线程B进行屏幕截图
-
2、从手机内导出线程生成的两个文件到电脑端
-
3、调用工程内UiAutomatorViewer类的.setModel(model,new File(saveDirString+mobileXml), img);方法传入两个文件参数,随后在PC端生成快照。
-
ps:每次打开uiautomatorViewer工具时,会去清空PC端指定目录下之前生成的数据。
OK,接下来,差不多到了上代码的环节。有部分代码是建立在UiautomatorViewer源码(一):浅析 和UiautomatorViewer源码(二):持久化 这两篇文章上的。
第一步:新增一个类,EasyScreenshotAction。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231package com.android.uiautomator.actions; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.text.SimpleDateFormat; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import com.android.ddmlib.IDevice; import com.android.uiautomator.DebugBridge; import com.android.uiautomator.UiAutomatorModel; import com.android.uiautomator.UiAutomatorViewer; public class EasyScreenshotAction extends Action { UiAutomatorViewer mViewer; public EasyScreenshotAction(UiAutomatorViewer viewer) { super("&SYNC"); this.mViewer = viewer; } public ImageDescriptor getImageDescriptor() { return ImageHelper.loadImageDescriptorFromResource("images/screenshot.png"); } public static String getCurrentTime(){ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); return df.format(System.currentTimeMillis()); } public void run() { if(!DebugBridge.isInitialized()) { MessageDialog.openError(this.mViewer.getShell(), "Error obtaining Device Screenshot", "Unable to connect to adb. Check if adb is installed correctly."); } else { File saveDir=new File("C:\TempForUiautomatorView") ; if (!saveDir.exists()) { saveDir.mkdirs(); } final String saveDirString = saveDir.getAbsolutePath()+File.separator; final IDevice device = this.pickDevice(); if(device != null) { ProgressMonitorDialog dialog = new ProgressMonitorDialog(this.mViewer.getShell()); try { dialog.run(true, false, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { String adbHead = "adb "; try { if (EasyScreenshotAction.DevicePickerDialog.serial ==null || EasyScreenshotAction.DevicePickerDialog.serial.length()<1) { }else { adbHead="adb -s "+EasyScreenshotAction.DevicePickerDialog.serial+" "; } String mobileScreenshot=getCurrentTime()+"_screenshot.png"; String mobileXml=getCurrentTime()+"_xml.uix"; //使用多线程,来达到更快的同步速度 MyThread screenThread=new MyThread(adbHead+"shell "/system/bin/screencap -p /data/local/tmp/"+mobileScreenshot+"""); MyThread xmlThread=new MyThread(adbHead+"shell "uiautomator dump /data/local/tmp/"+mobileXml+"""); monitor.setTaskName("Obtaining device screenshot"); monitor.subTask("Taking UI XML snapshot..."); ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); executor.execute(screenThread); executor.execute(xmlThread); //等待线程池中所有线程执行完毕后,再开始pull操作 executor.shutdown(); try { boolean loop = true; do { //等待所有任务完成 loop = !executor.awaitTermination(200, TimeUnit.MILLISECONDS); //阻塞,直到线程池里所有任务结束 } while(loop); } catch (InterruptedException e) { e.printStackTrace(); } Runtime.getRuntime().exec(adbHead+"pull /data/local/tmp/"+mobileScreenshot+" "+saveDirString+mobileScreenshot).waitFor(); monitor.subTask("Obtaining UI hierarchy"); Runtime.getRuntime().exec(adbHead+"pull /data/local/tmp/"+mobileXml+" "+saveDirString+mobileXml).waitFor(); UiAutomatorModel model = new UiAutomatorModel(new File(saveDirString+mobileXml)); Image img = null; File screenshot = new File(saveDirString+mobileScreenshot); if(screenshot != null) { ImageData[] e = (new ImageLoader()).load(screenshot.getAbsolutePath()); if(e.length < 1) { throw new RuntimeException("Unable to load image: " + screenshot.getAbsolutePath()); } img = new Image(Display.getDefault(), e[0]); } mViewer.setModel(model,new File(saveDirString+mobileXml), img); //下次工具重启后删除目录下之前生成的所有文件 // final String finalMobileScreenshot = mobileScreenshot; // final String finalMobileXml = mobileXml; // new Thread(new Runnable() { // public void run() { // if(new File(saveDirString+finalMobileScreenshot).exists()){ // new File(saveDirString+finalMobileScreenshot).delete(); // } // if(new File(saveDirString+finalMobileXml).exists()){ // new File(saveDirString+finalMobileXml).delete(); // } // } // }).start(); } catch (Exception var4) { monitor.done(); EasyScreenshotAction.this.showError(var4.getMessage(), var4); return; } monitor.done(); } }); } catch (Exception var4) { this.showError("Unexpected error while obtaining UI hierarchy", var4); } } } } private void showError(final String msg, final Throwable t) { this.mViewer.getShell().getDisplay().syncExec(new Runnable() { public void run() { Status s = new Status(4, "Screenshot", msg, t); ErrorDialog.openError(EasyScreenshotAction.this.mViewer.getShell(), "Error", "Error obtaining UI hierarchy", s); } }); } private IDevice pickDevice() { List<?> devices = DebugBridge.getDevices(); if(devices.size() == 0) { MessageDialog.openError(this.mViewer.getShell(), "Error obtaining Device Screenshot", "No Android devices were detected by adb."); return null; } else if(devices.size() == 1) { return (IDevice)devices.get(0); } else { EasyScreenshotAction.DevicePickerDialog dlg = new EasyScreenshotAction.DevicePickerDialog(this.mViewer.getShell(), devices); return dlg.open() != 0?null:dlg.getSelectedDevice(); } } private static class DevicePickerDialog extends Dialog { private final List<?> mDevices; private final String[] mDeviceNames; private static int sSelectedDeviceIndex; private static String serial=""; public DevicePickerDialog(Shell parentShell, List<?> devices) { super(parentShell); this.mDevices = devices; this.mDeviceNames = new String[this.mDevices.size()]; for(int i = 0; i < devices.size(); ++i) { this.mDeviceNames[i] = ((IDevice)devices.get(i)).getName(); } } protected Control createDialogArea(Composite parentShell) { Composite parent = (Composite)super.createDialogArea(parentShell); Composite c = new Composite(parent, 0); c.setLayout(new GridLayout(2, false)); Label l = new Label(c, 0); l.setText("Select device: "); final Combo combo = new Combo(c, 2056); combo.setItems(this.mDeviceNames); int defaultSelection = sSelectedDeviceIndex < this.mDevices.size()?sSelectedDeviceIndex:0; combo.select(defaultSelection); sSelectedDeviceIndex = defaultSelection; serial = combo.getItem(combo.getSelectionIndex()); combo.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent arg0) { EasyScreenshotAction.DevicePickerDialog.sSelectedDeviceIndex = combo.getSelectionIndex(); serial = combo.getItem(combo.getSelectionIndex()); // System.out.println(serial); } }); // System.out.println(serial); return parent; } public IDevice getSelectedDevice() { return (IDevice)this.mDevices.get(sSelectedDeviceIndex); } } } class MyThread extends Thread { public String cmd=""; public MyThread(String cmdString) { this.cmd=cmdString; } public void run() { try { Runtime.getRuntime().exec(cmd); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
第二步:在UiAutomatorViewer类的createContents()方法中添加按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21protected Control createContents(Composite parent) { Composite c = new Composite(parent, 2048); GridLayout gridLayout = new GridLayout(1, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.horizontalSpacing = 0; gridLayout.verticalSpacing = 0; c.setLayout(gridLayout); GridData gd = new GridData(768); c.setLayoutData(gd); ToolBarManager toolBarManager = new ToolBarManager(8388608); toolBarManager.add(new OpenFilesAction(this)); toolBarManager.add(new ScreenshotAction(this)); //添加一个按钮 toolBarManager.add(new EasyScreenshotAction(this)); ToolBar tb = toolBarManager.createControl(c); tb.setLayoutData(new GridData(768)); this.mUiAutomatorView = new UiAutomatorView(c, 2048); this.mUiAutomatorView.setLayoutData(new GridData(1808)); return parent; }
第三步:编写按钮事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72public void run() { if(!DebugBridge.isInitialized()) { MessageDialog.openError(this.mViewer.getShell(), "Error obtaining Device Screenshot", "Unable to connect to adb. Check if adb is installed correctly."); } else { File saveDir=new File("C:\TempForUiautomatorView") ; if (!saveDir.exists()) { saveDir.mkdirs(); } final String saveDirString = saveDir.getAbsolutePath()+File.separator; final IDevice device = this.pickDevice(); if(device != null) { ProgressMonitorDialog dialog = new ProgressMonitorDialog(this.mViewer.getShell()); try { dialog.run(true, false, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { String adbHead = "adb "; try { if (EasyScreenshotAction.DevicePickerDialog.serial ==null || EasyScreenshotAction.DevicePickerDialog.serial.length()<1) { }else { adbHead="adb -s "+EasyScreenshotAction.DevicePickerDialog.serial+" "; } String mobileScreenshot=getCurrentTime()+"_screenshot.png"; String mobileXml=getCurrentTime()+"_xml.uix"; //使用多线程,来达到更快的同步速度 MyThread screenThread=new MyThread(adbHead+"shell "/system/bin/screencap -p /data/local/tmp/"+mobileScreenshot+"""); MyThread xmlThread=new MyThread(adbHead+"shell "uiautomator dump /data/local/tmp/"+mobileXml+"""); monitor.setTaskName("Obtaining device screenshot"); monitor.subTask("Taking UI XML snapshot..."); ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); executor.execute(screenThread); executor.execute(xmlThread); //等待线程池中所有线程执行完毕后,再开始pull操作 executor.shutdown(); try { boolean loop = true; do { //等待所有任务完成 loop = !executor.awaitTermination(200, TimeUnit.MILLISECONDS); //阻塞,直到线程池里所有任务结束 } while(loop); } catch (InterruptedException e) { e.printStackTrace(); } Runtime.getRuntime().exec(adbHead+"pull /data/local/tmp/"+mobileScreenshot+" "+saveDirString+mobileScreenshot).waitFor(); monitor.subTask("Obtaining UI hierarchy"); Runtime.getRuntime().exec(adbHead+"pull /data/local/tmp/"+mobileXml+" "+saveDirString+mobileXml).waitFor(); UiAutomatorModel model = new UiAutomatorModel(new File(saveDirString+mobileXml)); Image img = null; File screenshot = new File(saveDirString+mobileScreenshot); if(screenshot != null) { ImageData[] e = (new ImageLoader()).load(screenshot.getAbsolutePath()); if(e.length < 1) { throw new RuntimeException("Unable to load image: " + screenshot.getAbsolutePath()); } img = new Image(Display.getDefault(), e[0]); } //调用setModel方法,在工具中生成快照 mViewer.setModel(model,new File(saveDirString+mobileXml), img); } catch (Exception var4) { monitor.done(); EasyScreenshotAction.this.showError(var4.getMessage(), var4); return; } monitor.done(); } }); } catch (Exception var4) { this.showError("Unexpected error while obtaining UI hierarchy", var4); } } } }
第四步:每次启动时删除指定文件夹下的旧文件,在UiAutomatorViewer类的Main方法中添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public static void main(String[] args) { DebugBridge.init(); //每次启动时,都要去检查是否有旧文件如果有的话,就删除旧文件 File saveDir=new File("C:\TempForUiautomatorView") ; if (saveDir.exists()) { File TrxFiles[] = saveDir.listFiles(); for(File curFile:TrxFiles ){ curFile.delete(); } } try { UiAutomatorViewer e = new UiAutomatorViewer(); e.setBlockOnOpen(true); e.open(); } catch (Exception var5) { var5.printStackTrace(); } finally { DebugBridge.terminate(); } }
最终效果:
有益效果:
1、快照速度还是有提升的。
2、有部分Android版本这个工具会报java.lang.reflect.InvocationTargetException,使用这种方法后,可以完美规避。
最后
以上就是震动鸵鸟最近收集整理的关于UiautomatorViewer源码(三):提升快照速度的全部内容,更多相关UiautomatorViewer源码(三)内容请搜索靠谱客的其他文章。
发表评论 取消回复