JNA库比机器人类的屏幕截图慢?

由于Robot.createScreenCaputure()方法很慢,我决定使用本机库。 我搜索并找到了这个论坛 ,找到了一个使用JNA Library的特定代码snipplet 。 这是一个旧版本,所以我重写了代码:

 import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.DirectColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import com.sun.jna.Native; import com.sun.jna.win32.W32APIOptions; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WinGDI; public class JNAScreenShot { public static BufferedImage getScreenshot(Rectangle bounds) { WinDef.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow()); WinDef.HBITMAP outputBitmap = GDI.CreateCompatibleBitmap(windowDC, bounds.width, bounds.height); try { WinDef.HDC blitDC = GDI.CreateCompatibleDC(windowDC); try { WinNT.HANDLE oldBitmap = GDI.SelectObject(blitDC, outputBitmap); try { GDI.BitBlt(blitDC, 0, 0, bounds.width, bounds.height, windowDC, bounds.x, bounds.y, GDI32.SRCCOPY); } finally { GDI.SelectObject(blitDC, oldBitmap); } WinGDI.BITMAPINFO bi = new WinGDI.BITMAPINFO(40); bi.bmiHeader.biSize = 40; boolean ok = GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height, (byte[]) null, bi, WinGDI.DIB_RGB_COLORS); if (ok) { WinGDI.BITMAPINFOHEADER bih = bi.bmiHeader; bih.biHeight = -Math.abs(bih.biHeight); bi.bmiHeader.biCompression = 0; return bufferedImageFromBitmap(blitDC, outputBitmap, bi); } else { return null; } } finally { GDI.DeleteObject(blitDC); } } finally { GDI.DeleteObject(outputBitmap); } } private static BufferedImage bufferedImageFromBitmap(WinDef.HDC blitDC, WinDef.HBITMAP outputBitmap, WinGDI.BITMAPINFO bi) { WinGDI.BITMAPINFOHEADER bih = bi.bmiHeader; int height = Math.abs(bih.biHeight); final ColorModel cm; final DataBuffer buffer; final WritableRaster raster; int strideBits = (bih.biWidth * bih.biBitCount); int strideBytesAligned = (((strideBits - 1) | 0x1F) + 1) >> 3; final int strideElementsAligned; switch (bih.biBitCount) { case 16: strideElementsAligned = strideBytesAligned / 2; cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F); buffer = new DataBufferUShort(strideElementsAligned * height); raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned, ((DirectColorModel) cm).getMasks(), null); break; case 32: strideElementsAligned = strideBytesAligned / 4; cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF); buffer = new DataBufferInt(strideElementsAligned * height); raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned, ((DirectColorModel) cm).getMasks(), null); break; default: throw new IllegalArgumentException("Unsupported bit count: " + bih.biBitCount); } final boolean ok; switch (buffer.getDataType()) { case DataBuffer.TYPE_INT: { int[] pixels = ((DataBufferInt) buffer).getData(); ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0); } break; case DataBuffer.TYPE_USHORT: { short[] pixels = ((DataBufferUShort) buffer).getData(); ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0); } break; default: throw new AssertionError("Unexpected buffer element type: " + buffer.getDataType()); } if (ok) { return new BufferedImage(cm, raster, false, null); } else { return null; } } private static final User32 USER = User32.INSTANCE; private static final GDI32 GDI = GDI32.INSTANCE; } interface GDI32 extends com.sun.jna.platform.win32.GDI32, com.sun.jna.platform.win32.WinGDI, com.sun.jna.platform.win32.WinDef { GDI32 INSTANCE = (GDI32) Native.loadLibrary(GDI32.class); boolean BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, int dwRop); HDC GetDC(HWND hWnd); boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, byte[] pixels, BITMAPINFO bi, int usage); boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, short[] pixels, BITMAPINFO bi, int usage); boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, int[] pixels, BITMAPINFO bi, int usage); int SRCCOPY = 0xCC0020; } interface User32 extends com.sun.jna.platform.win32.User32 { User32 INSTANCE = (User32) Native.loadLibrary(User32.class, W32APIOptions.UNICODE_OPTIONS); com.sun.jna.platform.win32.WinDef.HWND GetDesktopWindow(); } 

还有一个测试代码,看比Robot Class 快多少

 import java.awt.AWTException; import java.awt.Rectangle; import java.awt.Robot; import java.awt.image.BufferedImage; public class testClass { public static void main(String[] args) { BufferedImage bi = null, bj = null; Rectangle rect = new Rectangle(0, 0, 810, 384); long startTime, finishTime; startTime = System.currentTimeMillis(); for (int i = 0; i < 10; i++) { bi = JNAScreenShot.getScreenshot(rect); } finishTime = System.currentTimeMillis(); System.out.println("With JNA Library: " + (finishTime - startTime)/10); Robot robo = null; startTime = System.currentTimeMillis(); try { robo = new Robot(); } catch (AWTException a) { } for (int i = 0; i < 10; i++) { bj = robo.createScreenCapture(rect); } finishTime = System.currentTimeMillis(); System.out.println("With Robot Class " + (finishTime - startTime)/10); } } 

结果是

JNA图书馆:77
机器人类37

伙计们,请有人解释为什么会这样,我该怎么办呢?

JNA调用需要很长时间,而JNI直接使用c ++。

不要试图过早优化。 创建一个合理的界面来获取您想要的数据(截图)然后根据Robot,JNA或JNI创建您想要的实现。

我猜这些不同的实现会根据它运行的环境给出完全不同的结果。

编程规则之一:首先使它工作。 然后配置文件,找到瓶颈并删除或减轻瓶颈的影响。

首先,检查本机库是否实际上比您的代码更快。 情况可能并非如此。

假设你已经检查过,我会说这里的问题是用JNA调用真的很慢。 为了跳过每个周期一个调用问题,我建议写一个像这样的C函数:

  void callWithJNAfunction(int rectPosX, int rectPosY, rectSideX, rectSideY,int numberOfCycles) { for (int i = 0; i < numberOfCycles; i++) { //code in C that calls the library } 

现在编译此代码并使用JNA调用callWithJNAfunction(...)。

如果问题是JNA调用的缓慢,它将变得更快。