更新: 有很多其他帖子都在问如何在Android上获取屏幕截图,但似乎没有一个完整的答案。最初,我发布了这篇文章作为一个问题,因为我尝试打开一个与Frame Buffer相关的流时遇到了特定的问题。现在我已经转而将Frame Buffer转储到文件中,所以我更新了我的帖子来展示我是如何做到的。供参考(并致谢),我从这篇文章中找到了将Frame Buffer发送到文件的命令(不幸的是他没有提供他是如何到达那一步的)。我只是不知道如何将我从Frame Buffer中提取的原始数据转换成实际的图像文件。
这还需要在清单文件中添加
最初我无法从帧缓冲区获取可用数据,因为我不了解如何正确运行 shell 命令。现在,我已经切换到使用流来执行命令,可以使用'>'将帧缓冲区的当前数据发送到实际文件中...
我的意图是对Android设备上的实际屏幕进行全面转储。我发现唯一的方法是直接访问系统的Frame Buffer,而不使用adb桥接。显然,这种方法需要设备和运行它的应用程序具有root权限!幸运的是,对于我的目的,我可以控制设备的设置,并且通过设备获得root权限并提供给我的应用程序是可行的。我的测试目前正在运行2.2.3的旧版Droid上进行。
我从https://dev59.com/wm445IYBdhLWcg3wLXMK#6970338中找到了第一条接近的线索。经过更多的研究,我发现另一篇文章,描述了如何正确地以root身份运行shell命令。他们用它来执行重新启动,我用它来将当前的frame buffer发送到实际的文件中。我的当前测试只是通过ADB和一个基本的Activity(每个都提供root)进行了这样的操作。我将从后台运行的Service进行进一步的测试,敬请期待!下面是可以将当前屏幕导出到文件的整个测试活动:
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
权限,如此帖子所建议的。否则,应用程序运行时不会报错,但也不会创建目录或文件。最初我无法从帧缓冲区获取可用数据,因为我不了解如何正确运行 shell 命令。现在,我已经切换到使用流来执行命令,可以使用'>'将帧缓冲区的当前数据发送到实际文件中...
.raw
文件可能存在问题。我试图使用Google Picassa打开.raw
图像文件,但它说有一部分Picassa无法读取。你知道问题在哪里吗?我该如何得到正常的图像文件?谢谢。 - user1468308