缩短数据读取时间
在我遇到的场景中,OS对正在开发的App进行特定授权后,App可以在系统内使用screencap
进行截图操作(另外一种截图的方式需要依赖当前Activity
,不适合当前的场景),因此目标在于优化此命令的执行及数据输出上。
目标:缩短screencap
输出数据的读取时间——绕开磁盘IO进行数据处理。
screencap
命令
screencap
格式:
screencap: invalid option -- -
usage: screencap [-hp] [-d display-id] [FILENAME]
-h: this message
-p: save the file as a png.
-d: specify the display id to capture, default 0.
If FILENAME ends with .png it will be saved as a png.
If FILENAME is not given, the results will be printed to stdout.
- -h 当前case暂且不适用;
- -p 将截图保存为PNG格式;
- -d 设置当前截图操作的id值,当前case暂且不适用;
原来方案
在原来方案中使用系统shell命令screencap
进行截图,并且直接使用命令参数[FILENAME]
将截图数据从内存写入到磁盘文件。
原有方案的代码(省略部分无关代码,添加部分注释):
public static String takeScreenshot(long timestamp, double size, int quality) {
try {
// image naming and path to include download cache appending name you choose for file
String dirPath = /* 截图存放路径 */
// 省略...
String imagePath = /* 生成截图文件名 */
Process sh = Runtime.getRuntime().exec("/system/bin/screencap -p " + imagePath, null, null);
sh.waitFor();
// 此处省略....
return outputPath; // 最后截图生成路径
} catch (Exception e) {
// exception
}
return null;
}
上面代码逻辑:使用screencap
命令截图,参数-p
表明截图格式为PNG,并将最终图片写入磁盘中;
主要的耗时就在此时将截图数据写入到磁盘中。
运行时间
代码中执行最耗时位置在java.lang.Process
的waitFor
方法,执行时间如下:
从上图看出,整个方法执行时间2.52s,其中waitFor()
方法执行了2.44s,这是screencap
命令正在执行操作:
- 进行屏幕截图操作;
- 将数据写入到磁盘中;
因此需要优化的关键在于如何缩短将数据读取到内存处理时间。
修改方案
重新定义了takeScreenshot()
方法,其中原有的参数处理在其他位置进行处理,因为依据原有方案截图输出图片名是可知的。
public static Bitmap takeScreenshot() {
try {
Logcat.i("takeScreenshot", "START TO TAKE A SCREENSHOT...");
long startTimeMillis = System.currentTimeMillis();
Process sh = Runtime.getRuntime().exec("/system/bin/screencap -p" );
Bitmap bitmap;
byte[] byteArray = new byte[360 * 640 * 4]; // 此处的数组大小依据最终的图片大小而定,此处定义为360x640,测试设备的一般大小
try (BufferedInputStream bis = new BufferedInputStream(sh.getInputStream())) {
int length = bis.read(byteArray);
bitmap = BitmapFactory.decodeByteArray(byteArray, 0, length);
long endTimeMillis = System.currentTimeMillis();
Logcat.d("takeScreenshot", "截图时间:" + (endTimeMillis - startTimeMillis) + "ms");
}
Logcat.i("takeScreenshot", "END TAKING A SCREENSHOT...");
sh.destroy();
return bitmap;
} catch (Exception e) {
// Several error may come out with file handling or DOM
Logcat.e(TAG, "takeScreenshot: EXCEPTION " + e.getMessage());
}
return null;
}
上述代码只做了一件事情,即重定义了takeScreenshot()
方法,
- 使用
screencap
命令截图,但不设置参数[FILENAME]
输出路径——即将截图数据直接打印到stdout
标准输出上; - 从
stdout
对象上直接获取数据,即获取输入流对象sh.getInputStream()
,从中读取图片字节数据。 - 读取完成后主动destroy进程,即
java.lang.Process
对象。
不使用waitFor()
方法,因为waitFor()
方法阻塞当前线程,一直等待到Process
表示的进程自动终止。
public boolean waitFor(long timeout,
TimeUnit unit)
throws InterruptedException
Causes the current thread to wait, if necessary, until the subprocess represented by this Process object has terminated, or the specified waiting time elapses.
If the subprocess has already terminated then this method returns immediately with the value true. If the process has not terminated and the timeout value is less than, or equal to, zero, then this method returns immediately with the value false.
The default implementation of this methods polls the exitValue to check if the process has terminated. Concrete implementations of this class are strongly encouraged to override this method with a more efficient implementation.
运行时间
重新定义的方案执行时间记录。
从修改方案执行执行图中可以看到的是,takeScreenshot()
方法执行时间622ms,比原有方案缩短了大约1.9s。
其后将截图图片的压缩,异步写入到磁盘,可在其他逻辑中进行处理,不影响整体执行结果。