Android - 如何通过编程方式进行屏幕截图

16

当我的应用程序在后台运行时,我需要以编程方式获取Android设备或模拟器的屏幕截图,并每200毫秒保存一次图像到计算机上。我已经使用以下代码实现了该过程,但只能在我的应用程序在前台运行时工作。我希望在应用程序在后台运行时也能够截取屏幕截图。以下是我的代码:

public static Bitmap takeScreenshot(Activity activity, int ResourceID) { 
    Random r = new Random();
    int iterator=r.nextInt();   
     String mPath = Environment.getExternalStorageDirectory().toString() + "/screenshots/";
    View v1 = activity.getWindow().getDecorView().findViewById(ResourceID);
    v1.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    v1.layout(0, 0, v1.getMeasuredWidth(), v1.getMeasuredHeight()); 
    v1.setDrawingCacheEnabled(true);
    final Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
    Bitmap resultBitmap = Bitmap.createScaledBitmap(bitmap, 640, 480, false);
    v1.setDrawingCacheEnabled(false);
    File imageFile = new File(mPath);
    imageFile.mkdirs();
    imageFile = new File(imageFile+"/"+iterator+"_screenshot.png");
    try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        resultBitmap.compress(CompressFormat.PNG, 100, bos);
        byte[] bitmapdata = bos.toByteArray();

        //write the bytes in file
        FileOutputStream fos = new FileOutputStream(imageFile);
        fos.write(bitmapdata);
        fos.flush();
        fos.close();    
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return bitmap;
    }  

我如何在程序中实现设备 -> DDMS屏幕截图的刷新和保存按钮功能?我能做到吗?


10
如果这是可能的话,那会让我吓坏了。应用程序这样做会引发严重的安全问题。 - Blake Caldwell
1
除非手机已获得root权限(kitkat),否则无法实现这一点。至于“严重的安全问题”,我认为其他地方可能存在更严重的安全问题。如果应用程序可以请求权限来截取屏幕或其他操作,那也不是什么大问题。 - Sherif elKhatib
2
这不是一张截图之类的东西...它是每200毫秒一张截图。本质上是一个5帧每秒的视频。这将轻松捕捉到手机上所做的所有操作。我并不反对为了自己的使用(制作应用程序的视频)而这样做,但是给第三方应用程序授权进行截屏将是一个漫长的兔子洞。 - Justin Smith
请查看此答案以获取可能的解决方案:https://dev59.com/3nE85IYBdhLWcg3wr1ge#54779181 - Peter
4个回答

17
如果您的手机已经取得了root权限,请尝试以下操作。
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();

然后将img.png作为位图文件读取,并按以下方式将其转换为jpg格式

Bitmap screen = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+         
File.separator +"img.png");

//my code for saving
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    screen.compress(Bitmap.CompressFormat.JPEG, 15, bytes);

//you can create a new file name "test.jpg" in sdcard folder.

File f = new File(Environment.getExternalStorageDirectory()+ File.separator + "test.jpg");
            f.createNewFile();
//write the bytes in file
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
// remember close de FileOutput

    fo.close();

如果您的应用程序在后台运行且未经 root 处理,则无法访问屏幕,除非使用上述代码,即使在后台,也可以最有效地截取任何屏幕。

更新

谷歌已经提供了一个库,可以在没有 root 的情况下进行截屏,我尝试过了,但是我确定它会尽可能快地耗尽内存。

请尝试http://code.google.com/p/android-screenshot-library/


屏幕截图在模拟器中可以工作,但在真实设备上无法工作,请帮忙解决。 - Harsha
你的设备是否已经取得了 root 权限? - Viswanath Lekshmanan
请将您的代码片段发布为gist并分享链接。 - Viswanath Lekshmanan
你能检查一下 mPath 吗?在 catch 里有没有抛出异常? - Viswanath Lekshmanan
当我截屏时,实际设备上的白色背景会变成黑色。它看起来并不完全像视图可见的样子。 - Harsha
显示剩余3条评论

12

这里是完成它的方法。

通过代码在Android上截屏

结果输出:

输入图像描述

输入图像描述

public class CaptureScreenShots extends Activity {
    LinearLayout L1;
    ImageView image;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.screen_shots);
         L1 = (LinearLayout) findViewById(R.id.LinearLayout01);
            Button but = (Button) findViewById(R.id.munchscreen);
            but.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    View v1 = L1.getRootView();
                    v1.setDrawingCacheEnabled(true);
                    Bitmap bm = v1.getDrawingCache();
                    BitmapDrawable bitmapDrawable = new BitmapDrawable(bm);
                    image = (ImageView) findViewById(R.id.screenshots);
                    image.setBackgroundDrawable(bitmapDrawable);
                }
            });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.screen_shots, menu);
        return true;
    }

}

2
这只适用于应用程序在前台的情况下才有效,不是吗? - Justin Smith
2
new BitmapDrawable(bitmap) 构造函数已被弃用。请改用 image.setImageBitmap(bm) - Darpan
2
如果视图层次结构中有SurfaceView,则此方法无效,因为SurfaceView没有绘制缓存。在截屏中会看到一个黑色矩形。 - shift66

4
在后台(如ADB)截屏需要 groups=1003(graphics)。否则,你只能获取自己进程的屏幕截图。因此,你只能在已经root的设备上执行或者通过运行ADB本地程序来执行。
可以在https://android.googlesource.com/platform/frameworks/base/+/android-4.3_r2.3/cmds/screencap/找到本地cpp代码示例。
如果你想在Java代码中执行此操作,则需要访问Surface类的隐藏API。
/**
 * Like {@link #screenshot(int, int, int, int)} but includes all
 * Surfaces in the screenshot.
 *
 * @hide
 */
public static native Bitmap screenshot(int width, int height);

自ICS版本以来,这两个都应该没问题了;对于早期版本像GB,您可以查看原生cpp代码。
然而,在某些Android设备中,媒体系统和画布等的实现并不是很好,因此在这种情况下无法捕获任何视频播放或任何SurfaceView内容。

5
需要翻译的内容:It should be noted this method was removed in Android 4.3 and up.这个方法在Android 4.3及以上版本已被移除,需要注意。 - Tom

0

你可以考虑混合使用MediaProjectionAccessibilityService。第一个可以轻松地截屏,第二个则始终在所有应用程序“上方”运行。

如何在CODELABS中运行AccessibilityService的教程

以上示例的完整源代码在此处

此答案中,您可以找到非常有用的Activity代码片段。请记得设置适当的完全透明的Theme

<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.NoActionBar">
    <item name="android:background">#00000000</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@null</item>
</style>

然后在您的AccessibilityService中循环startActivity,并根据需要频繁地执行,或者更好的方法是-使用LocalBroadcastManager通知Service,告知Activity已经截取了屏幕并完成(因此Service可以再次启动此Activity

我曾经做过类似的事情(仅供内部使用),并且对截图进行了OCR分析以从中获取文本。当其他应用程序防止AccesibilityService功能时进行解析(这种Service旨在帮助残障人士,例如在文本阅读方面通常可以访问TextView


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接