android 截屏 和屏幕录像

android上获取屏幕内容的方法有以下几种:

1.调用view的getDrawingCache接口,得到屏幕内容的bitmap;优点是使用方便,缺点是只能获取单个view的图像内容,如果此view被切换到后台,或者屏幕上还有别的view,那获取的内容都不是你在屏幕上看到的全屏的图像。

    使用此方法先要通过setDrawingCacheEnable方法把cache开启,然后再调用getDrawingCache方法就可以获得view的cache图片了。buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,若果cache没有建立,系统会自动调用buildDrawingCache方法生成cache。若果要更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。

当调用setDrawingCacheEnabled方法设置为false, 系统也会自动把原来的cache销毁。

 

 

2.调用系统截屏接口。android4.0以后系统自带截屏功能,使用方法是按下 power+volume - ,在同时按下2键并保持0.5s左右后,

会听到咔嚓一声响声,并弹出一个浮动动画,显示截图效果。我们在代码里可以模拟按键的方式来调用这个接口实现截屏,例如:

public void testScreenshot() throws Exception {
        Log.d(LOG_TAG, "starting testScreenshot");
        // launch the activity.
        ScreenshotStubActivity activity = getActivity();
        assertNotNull(activity);

        File screenshotDir = getScreenshotDir();
        NewScreenshotObserver observer = new NewScreenshotObserver(
                screenshotDir.getAbsolutePath());
        observer.startWatching();
        takeScreenshot();
        // unlikely, but check if a new screenshot file was already created
        if (observer.getCreatedPath() == null) {
            // wait for screenshot to be created
            synchronized(observer) {
                observer.wait(SCREEN_WAIT_TIME_SEC*1000);
            }
        }
        assertNotNull(String.format("Could not find screenshot after %d seconds",
                SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());

        File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
        try {
            assertTrue(String.format("Detected new screenshot %s but its not a file",
                    screenshotFile.getName()), screenshotFile.isFile());
            assertTrue(String.format("Detected new screenshot %s but its not an image",
                    screenshotFile.getName()), isValidImage(screenshotFile));
        } finally {
            // delete the file to prevent external storage from filing up
            screenshotFile.delete();
        }
    }

private void takeScreenshot() {
        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
                KeyEvent.KEYCODE_POWER));
        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
                KeyEvent.KEYCODE_VOLUME_DOWN));
        // the volume down key event will cause the 'volume adjustment' UI to appear in the
        // foreground, and steal UI focus
        // unfortunately this means the next key event will get directed to the
        // 'volume adjustment' UI, instead of this test's activity
        // for this reason this test must be signed with platform certificate, to grant this test
        // permission to inject key events to another process
        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
                KeyEvent.KEYCODE_VOLUME_DOWN));
        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
                KeyEvent.KEYCODE_POWER));
    }

此方法的实现的方法比较简单,代码流程如下:

2.1 按下 volume down+power;

2.2 PhoneWindowManager.java 里捕获按键,当VOLUME_DOWN和Power键被同时按下后,向SystemUI发送截屏Message;

2.3 SystemUI(TakeScreenshotService.java和GlobalScreenshot.java)收到来自Client端的截屏Message后,调用Surface.java的方法 screenshot()截屏,并将截取到的图片通过WindowManager以浮动窗口的形式显示给用户查看。screenshot()方法是@hide的,sdk里无法直接调用。

2.4 surface.java 调用JNI的android_view_Surface.cpp中的doScreenshot(...)方法,最终调用opengl的接口来获取屏幕图像:

 -> [android_view_Surface.cpp] doScreenshot(...);

->update(...);

-> [SurfaceComposerClinet.cpp] update(...);

-> [SurfaceFlinger.cpp] captureScreen(...);

->captureScreenImplLocked(...);

->glReadPixels(...);

 

 

3.直接调用ComposerService的captureScreen接口,获取屏幕的rgb数据。此方法直接获取fb里的内容,是最灵活的,可实现屏幕录像;缺点是使用上最麻烦。示例代码如下:

frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp

            

#include <utils/Log.h>    

#include <binder/IPCThreadState.h>  

#include <binder/ProcessState.h>  

#include <binder/IServiceManager.h>    

#include <binder/IMemory.h>  

#include <surfaceflinger/ISurfaceComposer.h> 

#include <SkImageEncoder.h>  

#include <SkBitmap.h>  

  

using namespace android;   

int main(int argc, char** argv)  

{  

    if (argc != 2) {  

        printf("usage: %s path\n", argv[0]);  

        exit(0);  

    }  

  

    const String16 name("SurfaceFlinger");  

    sp<ISurfaceComposer> composer;  

    getService(name, &composer);  

  

    sp<IMemoryHeap> heap;  

    uint32_t w, h;  

    PixelFormat f;  

    status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);  

    if (err != NO_ERROR) {  

        fprintf(stderr, "screen capture failed: %s\n", strerror(-err));  

        exit(0);  

    }  

  

    printf("screen capture success: w=%u, h=%u, pixels=%p\n",  

            w, h, heap->getBase());  

  

    printf("saving file as PNG in %s ...\n", argv[1]);  

  

    SkBitmap b;  

    b.setConfig(SkBitmap::kARGB_8888_Config, w, h);  

    b.setPixels(heap->getBase());  

    SkImageEncoder::EncodeFile(argv[1], b,  SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);    

    return 0;  

} 

 

如果想在你的apk里使用,需要添加权限:

<SPAN style="FONT-SIZE: 24px"><uses-permission android:name="android.permission.READ_FRAME_BUFFER" /></SPAN>

 

方法3实际上和方法2的原理一样,但是结果不一样,方法3可获取到rgb颜色数据,而不是图片。

 

4.使用opengl接口获取屏幕内容

4.1 java接口

示例代码如下:

public static Bitmap SavePixels(int x, int y, int w, int h, GL10 gl)
{
int b[]=new int[w*h];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0; i<h; i++)
{
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-i-1)*w+j]=pix1;
}
}
Bitmap sb=Bitmap.createBitmap(bt, w, h, true);
return sb;
}

上面是一个简单的GL读取RGBA分量的方法,然后生成Android通用的Bitmap对象。

public static void SavePNG(int x, int y, int w, int h, String fileName, GL10 gl)
{


Bitmap bmp=SavePixels(x,y,w,h,gl);
try
{
FileOutputStream fos=new FileOutputStream("/sdcard/android123/"+fileName); //如何2.2或更高的系统sdcard路径为/mnt/sdcard/
bmp.compress(CompressFormat.PNG, 100, fos); //保存为png格式,质量100%
try
{
fos.flush();
}


catch (IOException e)
{
e.printStackTrace();
}
try
{
fos.close();
}


catch (IOException e)
{
e.printStackTrace();
}

}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

 

 

4.2 native接口

示例代码如下:

 unsigned char* buffer[height * width * 3]; //可以定义成其他类型
 glReadBuffer(GL_FRONT); //适合single buffer,如果用double的话,要GL_BACK
 glPixelStorei(GL_PACK_ALIGNMENT,1); //具体原因是opengl本身的buffer格式和我们要用来写图像的长度不太一样
 glReadPixels(0, 0, height, width, GL_RGB, GL_UNSIGNED_BYTE ,buffer); // 读出来是每个象素占3个字节(RGB)

 ......

 

 

 

 

5.直接读取framebuffer内容

由于android读取驱动的framebuffer内容的效率较低,在刷新比较快的时候,比如游戏画面,可能会由于刷新问题只截取到半张图或图片上下不匹配的问题。而且驱动的节点文件没有root权限没法访问,所以不推荐。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值