如何从linux 截屏命令行截屏

Ubuntu小技巧:解决VirtualBox中截屏时的黑屏故障
VirtualBox是一个可以让你在一台物理主机上运行多个系统的软件。它支持Windows、Mac OS X 和Linux 并且是Oracle公司在维护它。
我们在这里写了很多关于VirtualBox的文章,你可以做一个快速的查找来查看所有关于VirtualBox的文章。
现在,这里有一个故事:VirtualBox有一个功能,允许用户快速的截取虚拟机会话的屏幕截图。这些截图可以通过粘贴板和宿主机共享。
这就是我们这里要说的了。当某一个
虚拟机中的功能启用后,这项截图的功能就不能够正常的工作了。这种现象一直是一个痛点,直到最近我们偶然发现了这个解决方法。
如果不采取我们发现的这个方法,当你在Ubuntu的虚拟机里面截完屏幕之后,截屏的图片将会被存储成全黑的文件,这样的文件会毫无用处。
我一直在苦苦寻找解决方法,但是一直都没有好运气,直到刚刚。
要开启Ubuntu下的虚拟机和宿主机之间的双向黏贴板的话,请在Ubuntu虚拟机上面点击 &设置&,之后找到&普通-&高级标签&,选择这个功能来开启它。
现在,理论上,当你开启Ubuntu的虚拟机之后,按下&截屏键&,或者按下&Ctrl+截屏键&,虚拟机就应该为当前的桌面会话来张截屏。
之后你就可以通过在宿主机上面使用粘贴板的功能来看到那些图片了。
但是!如果你同时启用了Ubuntu虚拟机中的&3D 加速&功能,那些图片将会变得漆黑一片。
为了解决这个问题,你必须关闭Ubuntu虚拟机的3D加速功能!网管之家
关闭3D加速功能可以这样做:在Ubuntu虚拟机种点击&设置&,之后找到&系统-&显示-&显卡&标签,关闭3D加速
现在你必须在Ubuntu的虚拟机中设置关闭3D加速才能让截屏正常工作。现在打开一个虚拟机尝试来截个屏幕吧。
希望这个方法能够帮到你!
希望你喜欢!
顶一下(0) 踩一下(0)
热门标签:19034人阅读
&LinearLayoutxmlns:android=&&
&&&&android:orientation=&vertical&
&&&&android:layout_width=&fill_parent&
&&&&android:layout_height=&fill_parent&
&&&&android:layout_width=&fill_parent&
&&&&android:layout_height=&wrap_content&
&&&&android:text=&@string/hello&
&&android:text=&NiceButton&
&&android:id=&@+id/my_button&
&&android:layout_width=&fill_parent&
&&android:layout_height=&wrap_content&
&&android:layout_alignParentBottom=&true&&&/Button&
&/LinearLayout&
importjava.io.FileOutputS
importjava.text.SimpleDateF
importjava.util.D
importjava.util.L
importandroid.app.A
importandroid.graphics.B
importandroid.os.B
importandroid.view.V
importandroid.view.View.OnClickL
importandroid.widget.B
publicclassHelloAndroidextendsActivity
&&privateButton
Called when the activity is first created. */
&&@Override
&&publicvoidonCreate(Bundle
savedInstanceState) {
&&&&super.onCreate(savedInstanceState);
&&&&this.setContentView(R.layout.main);
&&&&this.button
= (Button) this.findViewById(R.id.my_button);
&&&&this.button.setOnClickListener(newOnClickListener()
&&&&&&publicvoidonClick(View
&&&&&&&&SimpleDateFormat
sdf = newSimpleDateFormat(
&&&&&&&&&&&&&yyyy-MM-dd_HH-mm-ss&,
Locale.US);
&&&&&&&&String
fname = &/sdcard/&+
sdf.format(newDate())
+ &.png&;
&&&&&&&&View
view = v.getRootView();
&&&&&&&&view.setDrawingCacheEnabled(true);
&&&&&&&&view.buildDrawingCache();
&&&&&&&&Bitmap
bitmap = view.getDrawingCache();
&&&&&&&&if(bitmap
&&&&&&&&&&System.out.println(&bitmap
&&&&&&&&&&try{
&&&&&&&&&&&&FileOutputStream
out = newFileOutputStream(fname);
&&&&&&&&&&&&pressFormat.PNG,100,
&&&&&&&&&&&&System.out.println(&file
+ fname + &output
&&&&&&&&&&}catch(Exception
&&&&&&&&&&&&e.printStackTrace();
&&&&&&&&&&}
&&&&&&&&}else{
&&&&&&&&&&System.out.println(&bitmap
is NULL!&);
该命令读取系统的framebuffer,需要获得系统权限:
(1). 在AndroidManifest.xml文件中添加
&uses-permissionandroid:name=&android.permission.READ_FRAME_BUFFER&/&
(2). 修改APK为系统权限,将APK放到源码中编译, 修改Android.mk
&LOCAL_CERTIFICATE := platform
.利用系统的API,实现Screenshot,这部分代码是系统隐藏的,需要在源码下编译,
& & 1).修改Android.mk, 添加系统权限
& & & & & LOCAL_CERTIFICATE := platform
& & & & &2).修改AndroidManifest.xml 文件,添加
&uses-permissionandroid:name=&android.permission.READ_FRAME_BUFFER&/&
&&&&&&public&boolean&takeScreenShot(String
imagePath){
&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&if(imagePath.equals(&&&)){
&&&&&&&&&&&&&&&&&&&&&&imagePath
= Environment.getExternalStorageDirectory()+File. separator+&Screenshot.png&&;
&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&Bitmap
&&&&&&&&&&WindowManager
&&&&&&&&&&DisplayMetrics
&&&&&&&&&&Display
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&mWindowManager
= (WindowManager) mcontext.getSystemService(Context.WINDOW_SERVICE);
&&&&&&&&&&mDisplay
= mWindowManager.getDefaultDisplay();
&&&&&&&&&&mDisplayMetrics
=&new&DisplayMetrics();
&&&&&&&&&&mDisplay.getRealMetrics(mDisplayMetrics);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&float[]
dims = {mDisplayMetrics.widthPixels , mDisplayMetrics.heightPixels };
&&&&&&&&&&mScreenBitmap
= Surface. screenshot((int)
&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&if&(mScreenBitmap
&&&&&&&&&&&&&&&&&return&false&;
&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&
&&&&&&&try&{
&&&&&&&&&&FileOutputStream
out =&new&FileOutputStream(imagePath);
&&&&&&&&&&pressFormat.
&&&&&&&&&&&&&
&&&&&&&&}&catch&(Exception
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
&&&&&&&&&&return&false&;
&&&&&&&&}&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&return&true&;
public class ScreenShot {
private BufferedImage image =
* @param args
public static void main(String[] args) {
// TODO Auto-generated method stub
AndroidDebugBridge.init(false); //
ScreenShot screenshot = new ScreenShot();
IDevice device = screenshot.getDevice();
for (int i = 0; i & 10; i++) {
Date date=new Date();
SimpleDateFormat df=new SimpleDateFormat(&MM-dd-HH-mm-ss&);
String nowTime = df.format(date);
screenshot.getScreenShot(device, &Robotium& + nowTime);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
public void getScreenShot(IDevice device,String filename) {
RawImage rawScreen =
rawScreen = device.getScreenshot();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AdbCommandRejectedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (rawScreen != null) {
Boolean landscape =
int width2 = landscape ? rawScreen.height : rawScreen.
int height2 = landscape ? rawScreen.width : rawScreen.
if (image == null) {
image = new BufferedImage(width2, height2,
BufferedImage.TYPE_INT_RGB);
if (image.getHeight() != height2 || image.getWidth() != width2) {
image = new BufferedImage(width2, height2,
BufferedImage.TYPE_INT_RGB);
int index = 0;
int indexInc = rawScreen.bpp && 3;
for (int y = 0; y & rawScreen. y++) {
for (int x = 0; x & rawScreen. x++, index += indexInc) {
int value = rawScreen.getARGB(index);
if (landscape)
image.setRGB(y, rawScreen.width - x - 1, value);
image.setRGB(x, y, value);
ImageIO.write((RenderedImage) image, &PNG&, new File(&D:/&
+ filename + &.jpg&));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
* 获取得到device对象
private IDevice getDevice(){
AndroidDebugBridge bridge = AndroidDebugBridge
.createBridge(&adb&, true);//如果代码有问题请查看API,修改此处的参数值试一下
waitDevicesList(bridge);
IDevice devices[] = bridge.getDevices();
device = devices[0];
* 等待查找device
* @param bridge
private void waitDevicesList(AndroidDebugBridge bridge) {
int count = 0;
while (bridge.hasInitialDeviceList() == false) {
Thread.sleep(500);
} catch (InterruptedException e) {
if (count & 240) {
System.err.print(&等待获取设备超时&);
(1)命令行,框架的截屏功能是通过framebuffer来实现的,所以我们先来介绍一下framebuffer。
framebuffer介绍
帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行 读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。
Linux FrameBuffer 本质上只是提供了对图形设备的硬件抽象,在开发者看来,FrameBuffer 是一块显示缓存,往显示缓存中写入特定格式的数据就意味着向屏幕输出内容。所以说FrameBuffer就是一块白板。例如对于初始化为16 位色的FrameBuffer 来说, FrameBuffer中的两个字节代表屏幕上一个点,从上到下,从左至右,屏幕位置与内存地址是顺序的线性关系。
帧缓存有个地址,是在内存里。我们通过不停的向frame buffer中写入数据, 显示控制器就自动的从frame buffer中取数据并显示出来。全部的图形都共享内存中同一个帧缓存。
Android截屏实现思路
Android系统是基于Linux内核的,所以也存在framebuffer这个设备,我们要实现截屏的话只要能获取到framebuffer中的数据,然后把数据转换成图片就可以了,android中的framebuffer数据是存放在&/dev/graphics/fb0&文件中的,所以我们只需要来获取这个文件的数据就可以得到当前屏幕的内容。
现在我们的测试代码运行时候是通过RC(remote controller)方式来运行被测应用的,那就需要在PC机上来访问模拟器或者真机上的framebuffer数据,这个的话可以通过android的ADB命令来实现。
/***********************************************************************
& *&& ScreenShot.java
& ***********************************************************************/
import java.awt.image.BufferedI
import java.io.ByteArrayOutputS
import java.io.DataI
import java.io.EOFE
import java.io.F
import java.io.FileInputS
import java.io.IOE
import java.io.InputS
import javax.imageio.ImageIO;
import org.openqa.selenium.OutputT
import org.openqa.selenium.internal.Base64E
import mon.io.C
import mon.io.LittleEndianDataInputS
public class ScreenShot {
&&& &* @param args
&&& &* @throws InterruptedException&
&&& public static void main(String[] args) throws InterruptedException {&&&&
&&& &&& try {
&&& &&& &&& //分辨率大小,后续可以通过代码来获取到当前的分辨率
&&& &&& &&& int xResolution = 320;
&&& &&& &&& int yResolution = 480;
&&& &&& &&& //执行adb命令,把framebuffer中内容保存到fb1文件中
&&& &&& &&& &Runtime.getRuntime().exec(&adb pull /dev/graphics/fb0 C:/fb1&);
&&& &&& &&& &//等待几秒保证framebuffer中的数据都被保存下来,如果没有保存完成进行读取操作会有IO异常
&&& &&& &&& &Thread.sleep(15000);
&&& &&& &&& &//读取文件中的数据
&&& &&& &&& &InputStream in = (InputStream)new FileInputStream(&C:/fb1&);
&&& &&& &&& &DataInput frameBuffer = new LittleEndianDataInputStream(in);
&&& &&& &&& &
&&& &&& &&& &BufferedImage screenImage = new BufferedImage(
&&& &&& &&& &&& &&& &xResolution, yResolution, BufferedImage.TYPE_INT_ARGB);
&&& &&& &&& &&&& int[] oneLine = new int[xResolution];
&&& &&& &&& &&& for (int y = 0; y & yR y++) {
&&& &&& &&& &&& &&& //从frameBuffer中计算出rgb值
&&& &&& &&& &&& &&& convertToRgba32(frameBuffer, oneLine);
&&& &&& &&& &&& &&& //把rgb值设置到image对象中
&&& &&& &&& &&& &&& screenImage.setRGB(0, y, xResolution, 1, oneLine, 0, xResolution);
&&& &&& &&& &&& }
&&& &&& &&& &&& Closeables.closeQuietly(in);
&&& &&& &&& &&&&
&&& &&& &&& &&& ByteArrayOutputStream rawPngStream = new ByteArrayOutputStream();
&&& &&& &&& &&& try {
&&& &&& &&& &&& &&&&& if (!ImageIO.write(screenImage, &png&, rawPngStream)) {
&&& &&& &&& &&& &&&&&&& throw new RuntimeException(
&&& &&& &&& &&& &&&&&&&&&&& &This Java environment does not support converting to PNG.&);
&&& &&& &&& &&& &&&&& }
&&& &&& &&& &&& &&& } catch (IOException exception) {
&&& &&& &&& &&& &&&&& // This should never happen because rawPngStream is an in-memory stream.
&&& &&& &&& &&& &&&& System.out.println(&IOException=& + exception);
&&& &&& &&& &&& &&& }
&&& &&& &&& &&& byte[] rawPngBytes = rawPngStream.toByteArray();
&&& &&& &&& &&& String base64Png = new Base64Encoder().encode(rawPngBytes);
&&& &&& &&& &&&&
&&& &&& &&& &&& File screenshot = OutputType.FILE.convertFromBase64Png(base64Png);
&&& &&& &&& &&& System.out.println(&screenshot==& + screenshot.toString());
&&& &&& &&& &&& screenshot.renameTo(new File(&C:\\screenshottemp.png&));
&&& &&& &&& &&&&
&&& &&& } catch (IOException e) {
&&& &&& &&& // TODO Auto-generated catch block
&&& &&& &&& e.printStackTrace();
&&& &&& &&& System.out.println(e);
&&& public static void convertToRgba32(DataInput frameBuffer, int[] into) {
&&& &&& try {
&&& &&& &&& for (int x = 0; x & into. x++) {
&&& &&&&&&& &&& try{
&&& &&&&&&&&&&& int rgb = frameBuffer.readShort() & 0
&&& &&&&&&&&&&& int red = rgb && 11;
&&& &&&&&&&&&&& red = (red && 3) | (red && 2);
&&& &&&&&&&&&&& int green = (rgb && 5) & 63;
&&& &&&&&&&&&&& green = (green && 2) | (green && 4);
&&& &&&&&&&&&&& int blue = rgb & 31;
&&& &&&&&&&&&&& blue = (blue && 3) | (blue && 2);
&&& &&&&&&&&&&& into[x] = 0xff000000 | (red && 16) | (green && 8) |
&&& &&&&&&&&&&& }catch (EOFException e){
&&& &&&&&&& &&& &&& System.out.println(&EOFException=& + e);
&&& &&&&&&& &&& }
&&& &&&&&&&&& }
&&& &&& } catch (IOException exception) {
&&& &&& &&& System.out.println(&convertToRgba32Exception=& + exception);
首先是直接移植SystemUI的代码,实现截图效果,这部分的代码就不贴出来了,直接去下载代码吧, 关键的代码没有几句,最最主要的是:Surface.screenshot(),请看代码吧。
&SPAN style=&FONT-SIZE: 16px&&package org.winplus.
import java.io.F
import java.io.FileNotFoundE
import java.io.FileOutputS
import java.io.IOE
import java.text.SimpleDateF
import java.util.D
import android.app.A
import android.content.C
import android.graphics.B
import android.graphics.C
import android.graphics.M
import android.os.B
import android.util.DisplayM
import android.util.L
import android.view.D
import android.view.S
import android.view.WindowM
import android.os.SystemP
public class SimpleScreenshotActivity extends Activity {
private Display mD
private WindowManager mWindowM
private DisplayMetrics mDisplayM
private Bitmap mScreenB
private Matrix mDisplayM
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(new Runnable() {
public void run() {
takeScreenshot();
}).start();
private float getDegreesForRotation(int value) {
switch (value) {
case Surface.ROTATION_90:
return 360f - 90f;
case Surface.ROTATION_180:
return 360f - 180f;
case Surface.ROTATION_270:
return 360f - 270f;
return 0f;
private void takeScreenshot() {
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
mDisplay.getRealMetrics(mDisplayMetrics);
mDisplayMatrix = new Matrix();
float[] dims = { mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels };
int value = mDisplay.getRotation();
String hwRotation = SystemProperties.get(&ro.sf.hwrotation&, &0&);
if (hwRotation.equals(&270&) || hwRotation.equals(&90&)) {
value = (value + 3) % 4;
float degrees = getDegreesForRotation(value);
boolean requiresRotation = (degrees & 0);
if (requiresRotation) {
// Get the dimensions of the device in its native orientation
mDisplayMatrix.reset();
mDisplayMatrix.preRotate(-degrees);
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
if (requiresRotation) {
// Rotate the screenshot to the current orientation
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(ss);
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
c.rotate(degrees);
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
mScreenBitmap =
// If we couldn't take the screenshot, notify the user
if (mScreenBitmap == null) {
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
saveBitmap(mScreenBitmap);
} catch (IOException e) {
System.out.println(e.getMessage());
public void saveBitmap(Bitmap bitmap) throws IOException {
String imageDate = new SimpleDateFormat(&yyyy-MM-dd-HH-mm-ss&)
.format(new Date(System.currentTimeMillis()));
File file = new File(&/mnt/sdcard/Pictures/&+imageDate+&.png&);
if(!file.exists()){
file.createNewFile();
FileOutputS
out = new FileOutputStream(file);
if (pressFormat.PNG, 70, out)) {
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
package org.winplus.
import java.io.F
import java.io.FileNotFoundE
import java.io.FileOutputS
import java.io.IOE
import java.text.SimpleDateF
import java.util.D
import android.app.A
import android.content.C
import android.graphics.B
import android.graphics.C
import android.graphics.M
import android.os.B
import android.util.DisplayM
import android.util.L
import android.view.D
import android.view.S
import android.view.WindowM
import android.os.SystemP
public class SimpleScreenshotActivity extends Activity {
private Display mD
private WindowManager mWindowM
private DisplayMetrics mDisplayM
private Bitmap mScreenB
private Matrix mDisplayM
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(new Runnable() {
public void run() {
takeScreenshot();
}).start();
private float getDegreesForRotation(int value) {
switch (value) {
case Surface.ROTATION_90:
return 360f - 90f;
case Surface.ROTATION_180:
return 360f - 180f;
case Surface.ROTATION_270:
return 360f - 270f;
return 0f;
private void takeScreenshot() {
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
mDisplay.getRealMetrics(mDisplayMetrics);
mDisplayMatrix = new Matrix();
float[] dims = { mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels };
int value = mDisplay.getRotation();
String hwRotation = SystemProperties.get(&ro.sf.hwrotation&, &0&);
if (hwRotation.equals(&270&) || hwRotation.equals(&90&)) {
value = (value + 3) % 4;
float degrees = getDegreesForRotation(value);
boolean requiresRotation = (degrees & 0);
if (requiresRotation) {
// Get the dimensions of the device in its native orientation
mDisplayMatrix.reset();
mDisplayMatrix.preRotate(-degrees);
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
if (requiresRotation) {
// Rotate the screenshot to the current orientation
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(ss);
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
c.rotate(degrees);
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
mScreenBitmap =
// If we couldn't take the screenshot, notify the user
if (mScreenBitmap == null) {
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
saveBitmap(mScreenBitmap);
} catch (IOException e) {
System.out.println(e.getMessage());
public void saveBitmap(Bitmap bitmap) throws IOException {
String imageDate = new SimpleDateFormat(&yyyy-MM-dd-HH-mm-ss&)
.format(new Date(System.currentTimeMillis()));
File file = new File(&/mnt/sdcard/Pictures/&+imageDate+&.png&);
if(!file.exists()){
file.createNewFile();
FileOutputS
out = new FileOutputStream(file);
if (pressFormat.PNG, 70, out)) {
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
PS:1、需要在AndroidManifest.xml中加入代码:android:sharedUserId=&android.uid.system&
2、由于调用了@hide的API,所以编译得时候请使用makefile编译。或者通过在Eclipse中添加Jar文件通过编译。
3、此代码只在Android4.0中使用过,2.3的就没去做测试了。
Android手机一般都自带有手机屏幕截图的功能:在手机任何界面(当然手机要是开机点亮状态),通过按组合键,屏幕闪一下,然后咔嚓一声,截图的照片会保存到当前手机的图库中,真是一个不错的功能!
以我手头的测试手机为例,是同时按电源键+音量下键来实现截屏,苹果手机则是电源键 + HOME键,小米手机是菜单键+音量下键,而HTC一般是按住电源键再按左下角的“主页”键。那么Android源码中使用组合键是如何实现屏幕截图功能呢?前段时间由于工作的原因仔细看了一下,这两天不忙,便把相关的知识点串联起来整理一下,分下面两部分简单分析下实现流程:
Android源码中对组合键的捕获。
Android源码中对按键的捕获位于文件PhoneWindowManager.java(alps\frameworks\base\policy\src\com\android\internal\policy\impl)中,这个类处理所有的键盘输入事件,其中函数interceptKeyBeforeQueueing()会对常用的按键做特殊处理。以我手头的测试机为例,是同时按电源键和音量下键来截屏,那么在这个函数中我们会看到这么两段代码:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (isScreenOn && !mVolumeDownKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mVolumeDownKeyTriggered = true;
mVolumeDownKeyTime = event.getDownTime();
mVolumeDownKeyConsumedByScreenshotChord = false;
cancelPendingPowerKeyAction();
interceptScreenshotChord();
mVolumeDownKeyTriggered = false;
cancelPendingScreenshotChordAction();
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
if (isScreenOn && !mPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mPowerKeyTriggered = true;
mPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
可以看到正是在这里(响应Down事件)捕获是否按了音量下键和电源键的,而且两个地方都会进入函数interceptScreenshotChord()中,那么接下来看看这个函数干了什么工作:
private void interceptScreenshotChord() {
if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
final long now = SystemClock.uptimeMillis();
if (now &= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
&& now &= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mVolumeDownKeyConsumedByScreenshotChord = true;
cancelPendingPowerKeyAction();
mHandler.postDelayed(mScreenshotChordLongPress,
ViewConfiguration.getGlobalActionKeyTimeout());
在这个函数中,用两个布尔变量判断是否同时按了音量下键和电源键后,再计算两个按键响应Down事件之间的时间差不超过150毫秒,也就认为是同时按了这两个键后,算是真正的捕获到屏幕截屏的组合键。
附言:文件PhoneWindowManager.java类是拦截键盘消息的处理类,在此类中还有对home键、返回键等好多按键的处理。
Android源码中调用屏幕截图的接口。
捕获到组合键后,我们再看看android源码中是如何调用屏幕截图的函数接口。在上面的函数interceptScreenshotChord中我们看到用handler判断长按组合键500毫秒之后,会进入如下函数:
private final Runnable mScreenshotChordLongPress = new Runnable() {
public void run() {
takeScreenshot();
在这里启动了一个线程来完成截屏的功能,接着看函数takeScreenshot():
private void takeScreenshot() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
ComponentName cn = new ComponentName(&com.android.systemui&,
&com.android.systemui.screenshot.TakeScreenshotService&);
Intent intent = new Intent();
intent.setComponent(cn);
ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, 1);
final ServiceConnection myConn = this;
Handler h = new Handler(mHandler.getLooper()) {
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mHandler.removeCallbacks(mScreenshotTimeout);
msg.replyTo = new Messenger(h);
msg.arg1 = msg.arg2 = 0;
if (mStatusBar != null && mStatusBar.isVisibleLw())
msg.arg1 = 1;
if (mNavigationBar != null && mNavigationBar.isVisibleLw())
msg.arg2 = 1;
messenger.send(msg);
} catch (RemoteException e) {
public void onServiceDisconnected(ComponentName name) {}
if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
可以看到这个函数使用AIDL绑定了service服务到&com.android.systemui.screenshot.TakeScreenshotService&,注意在service连接成功时,对message的msg.arg1和msg.arg2两个参数的赋值。其中在mScreenshotTimeout中对服务service做了超时处理。接着我们找到实现这个服务service的类TakeScreenshotService,看看其实现的流程:
public class TakeScreenshotService extends Service {
private static final String TAG = &TakeScreenshotService&;
private static GlobalScreenshot mScreenshot;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
final Messenger callback = msg.replyTo;
if (mScreenshot == null) {
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
mScreenshot.takeScreenshot(new Runnable() {
@Override public void run() {
Message reply = Message.obtain(null, 1);
callback.send(reply);
} catch (RemoteException e) {
}, msg.arg1 & 0, msg.arg2 & 0);
public IBinder onBind(Intent intent) {
return new Messenger(mHandler).getBinder();
在这个类中,我们主要看调用接口,用到了mScreenshot.takeScreenshot()传递了三个参数,第一个是个runnable,第二和第三个是之前message传递的两个参数msg.arg1和msg.arg2。最后我们看看这个函数takeScreenshot(),位于文件GlobalScreenshot.java中(跟之前的函数重名但是文件路径不一样):
* Takes a screenshot of the current display and shows an animation.
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots
// only in the natural orientation of the device :!)
mDisplay.getRealMetrics(mDisplayMetrics);
float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
float degrees = getDegreesForRotation(mDisplay.getRotation());
boolean requiresRotation = (degrees & 0);
if (requiresRotation) {
// Get the dimensions of the device in its native orientation
mDisplayMatrix.reset();
mDisplayMatrix.preRotate(-degrees);
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
// Take the screenshot
mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager);
finisher.run();
if (requiresRotation) {
// Rotate the screenshot to the current orientation
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(ss);
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
c.rotate(degrees);
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
mScreenBitmap = ss;
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
这段代码的注释比较详细,其实看到这里,我们算是真正看到截屏的操作了,具体的工作包括对屏幕大小、旋转角度的获取,然后调用Surface类的screenshot方法截屏保存到bitmap中,之后把这部分位图填充到一个画布上,最后再启动一个延迟的拍照动画效果。如果再往下探究screenshot方法,发现已经是一个native方法了:
* Like {@link #screenshot(int, int, int, int)} but includes all
* Surfaces in the screenshot.
public static native Bitmap screenshot(int width, int height);
使用JNI技术调用底层的代码,如果再往下走,会发现映射这这个jni函数在文件android_view_Surface.cpp中,这个真的已经是底层c++语言了,统一调用的底层函数是:
static jobject doScreenshot(JNIEnv* env, jobject clazz, jint width, jint height,
jint minLayer, jint maxLayer, bool allLayers)
ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL);
if (pixels-&update(width, height, minLayer, maxLayer, allLayers) != NO_ERROR) {
delete pixels;
uint32_t w = pixels-&getWidth();
uint32_t h = pixels-&getHeight();
uint32_t s = pixels-&getStride();
uint32_t f = pixels-&getFormat();
ssize_t bpr = s * android::bytesPerPixel(f);
SkBitmap* bitmap = new SkBitmap();
bitmap-&setConfig(convertPixelFormat(f), w, h, bpr);
if (f == PIXEL_FORMAT_RGBX_8888) {
bitmap-&setIsOpaque(true);
if (w & 0 && h & 0) {
bitmap-&setPixelRef(pixels)-&unref();
bitmap-&lockPixels();
// be safe with an empty bitmap.
delete pixels;
bitmap-&setPixels(NULL);
return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
由于对C++不熟,我这里就不敢多言了。其实到这里,算是对手机android源码中通过组合键屏幕截图的整个流程有个大体了解了,一般我们在改动中熟悉按键的捕获原理,并且清楚调用的截屏函数接口即可,如果有兴趣的,可以继续探究更深的底层是如何实现的。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:254146次
积分:3118
积分:3118
排名:第3976名
转载:291篇
评论:118条
(2)(4)(1)(5)(15)(6)(9)(1)(11)(6)(20)(20)(5)(4)(10)(12)(10)(4)(12)(7)(17)(16)(6)(14)(11)(6)(9)(2)(1)(12)(6)(10)(25)}

我要回帖

更多关于 linux 命令行 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信