【问题标题】:Android take screenshot on rooted deviceAndroid在root设备上截图
【发布时间】:2012-06-09 23:46:45
【问题描述】:

更新 还有许多其他帖子询问如何在 android 中获取屏幕截图,但似乎没有一个完整的答案。最初我将这个问题发布为一个问题,因为我在尝试打开帧缓冲区的流时遇到了一个特定问题。现在我已经切换到将帧缓冲区转储到文件中,所以我更新了我的帖子以显示我是如何到达那里的。作为参考(和确认),我找到了将 FrameBuffer 发送到来自this post 的文件的命令(不幸的是,他没有提供他是如何做到这一点的)。我只是想念如何将我从帧缓冲区中提取的原始数据转换为实际的图像文件。

我的意图是完整转储 Android 设备上的实际屏幕。在不使用 adb 桥的情况下,我能找到的唯一方法是直接访问系统的帧缓冲区。显然,这种方法需要设备上的 root 权限以及运行它的应用程序!幸运的是,出于我的目的,我可以控制设备的设置方式,并且让设备以 root 权限为我的应用程序提供是可行的。我目前正在运行 2.2.3 的旧 Droid 上进行测试。

我从https://stackoverflow.com/a/6970338/1446554 找到了如何处理它的第一个提示。经过一番研究,我发现另一篇文章描述了如何正确地run shell commands as root。他们使用它来执行重新启动,我使用它将当前帧缓冲区发送到实际文件。我目前的测试只是通过 ADB 和一个基本的 Activity(每个都提供了 root)来完成。我将在后台运行的服务中进行进一步的测试,更新即将到来!这是我可以将当​​前屏幕导出到文件的整个测试活动:

public class ScreenshotterActivity extends Activity {
    public static final String TAG = "ScreenShotter";

    private Button _SSButton;
    private PullScreenAsyncTask _Puller;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        _SSButton = (Button)findViewById(R.id.main_screenshotButton);
        _SSButton.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                if (_Puller != null)
                    return;
                //TODO: Verify that external storage is available! Could always use internal instead...

                _Puller = new PullScreenAsyncTask();
                _Puller.execute((Void[])null);
            }
        });
    }

    private void runSuShellCommand(String cmd) {
        Runtime runtime = Runtime.getRuntime();
        Process proc = null;
        OutputStreamWriter osw = null;
        StringBuilder sbstdOut = new StringBuilder();
        StringBuilder sbstdErr = new StringBuilder();

        try { // Run Script
            proc = runtime.exec("su");
            osw = new OutputStreamWriter(proc.getOutputStream());
            osw.write(cmd);
            osw.flush();
            osw.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (osw != null) {
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();                    
                }
            }
        }

        try {
            if (proc != null)
                proc.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        sbstdOut.append(readBufferedReader(new InputStreamReader(proc.getInputStream())));
        sbstdErr.append(readBufferedReader(new InputStreamReader(proc.getErrorStream())));
    }

    private String readBufferedReader(InputStreamReader input) {

        BufferedReader reader = new BufferedReader(input);
        StringBuilder found = new StringBuilder();
        String currLine = null;
        String sep = System.getProperty("line.separator");
        try {
            // Read it all in, line by line.
            while ((currLine = reader.readLine()) != null) {
                found.append(currLine);
                found.append(sep);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    class PullScreenAsyncTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... params) {

            File ssDir = new File(Environment.getExternalStorageDirectory(), "/screenshots");
            if (ssDir.exists() == false) {
                Log.i(TAG, "Screenshot directory doesn't already exist, creating...");
                if (ssDir.mkdirs() == false) {
                    //TODO: We're kinda screwed... what can be done?
                    Log.w(TAG, "Failed to create directory structure necessary to work with screenshots!");
                    return null;
                }
            }
            File ss = new File(ssDir, "ss.raw");            
            if (ss.exists() == true) {
                ss.delete();
                Log.i(TAG, "Deleted old Screenshot file.");
            }
            String cmd = "/system/bin/cat /dev/graphics/fb0 > "+ ss.getAbsolutePath();
            runSuShellCommand(cmd);
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            _Puller = null;
        }
    }
}

这还需要将android.permission.WRITE_EXTERNAL_STORAGE 权限添加到Manifest。正如this post 中所建议的那样。否则它会运行,不会抱怨,不会创建目录或文件。

最初,由于不了解如何正确运行 shell 命令,我无法从帧缓冲区获取可用数据。现在我已经切换到使用流来执行命令,我可以使用 '>' 将帧缓冲区的当前数据发送到实际文件......

【问题讨论】:

  • 感谢您的编码。但是,.raw 文件可能有问题。我试图用google picassa打开.raw图像文件,但它说有些部分picassa无法读取。你知道有什么问题吗?如何从中获取正常的图像文件?谢谢。
  • @swk 这不是一个简单的问题。我的要求更多的是确定连续屏幕截图之间的差异,所以我还没有完全确定如何将它们转换回图像!我建议彻底搜索 Stack Overflow 的其余部分,特别是如何将 /dev/graphics/fb0 中的数据转换为图像。
  • @swk 你有没有找到将帧缓冲区转换为图像文件的方法??

标签: android screenshot root


【解决方案1】:

您可以通过编程方式运行“adb shell /system/bin/screencap -p /sdcard/img.png”,如下所示:

Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream  os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();

【讨论】:

  • 它不工作..它返回 NULL 。甚至我已经设置了权限&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
  • 你用的是哪个安卓版本
  • 它的 KitKat (4.4) @ Viswanath
  • 好吧,你没解释,其实kitkat有&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;的问题,可以参考kitkat这个权限的问题
  • 没关系:它在我的根设备上完美运行。谢谢!
【解决方案2】:

ICS 设备的一个简单解决方案是从命令行使用以下命令

adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png screenshot.png

这会将 screenshot.png 文件保存在当前目录中。

在运行 4.0.3 的三星 Galaxy SII 上测试。

【讨论】:

  • 这个问题与在单独的机器上工作无关。在现场情况下,这纯粹是针对设备上的。 ADB 很好,但在这种情况下对我没有任何好处。
【解决方案3】:

对于不同的手机,情况会有所不同。这取决于您设备的底层图形格式。您可以使用系统调用轮询图形格式。如果您只打算在您知道图形格式的设备上运行它,您可以编写一个转换器将其转换为已知格式。

你可以看看以下项目:http://code.google.com/p/android-fb2png/

如果您查看 fb2png.c 的源代码,您会发现他们轮询 FBIOGET_VSCREENINFO,其中包含有关设备如何将屏幕图像存储在内存中的信息。一旦知道这一点,您应该能够将其转换为您可以使用的格式。

我希望这会有所帮助。

【讨论】:

  • 这看起来确实是解决这个问题的最有可能的途径。由于似乎对该主题没有任何进一步的兴趣,因此我将其标记为答案。如果有人完全解决了这个问题,那就太好了,但不幸的是,我现在正忙于其他事情!
猜你喜欢
  • 2017-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 2013-11-16
  • 2011-11-17
  • 2014-02-05
  • 1970-01-01
相关资源
最近更新 更多