getDrawingCache()和Android中的截图方法简介

getDrawingCache()方法截取部分屏幕:

view.setDrawingCacheEnabled(true);//设置能否缓存图片信息(drawing cache)
view.buildDrawingCache();//如果能够缓存图片,则创建图片缓存
Bitmap bitmap = view.getDrawingCache();//如果图片已经缓存,返回一个bitmap

view.destroyDrawingCache();//释放缓存占用的资源

Tips:

如果在一个界面中,重复截取图片,在每次截屏之前,都应该清除缓存;

假如图片不符合我们的要求,可以使用Bitmap.createBitmap( )方法处理图片(图片压缩过度,会致使不能显示);

图片本地存储

File f = new File(" ");
try {
    f.createNewFile();
} catch (IOException e1) {
    e1.printStackTrace();
}
FileOutputStream fOut = null;
try {
    fOut = new FileOutputStream(f);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
try {
    fOut.flush();
} catch (IOException e) {
    e.printStackTrace();
}
try {
    fOut.close();
} catch (IOException e) {
    e.printStackTrace();
}

其他截屏方法

以下内容转载自:http://blog.csdn.net/woshinia/article/details/11520403

1、在APK中调用“adb shell screencap -pfilepath” 命令(基于Android SDK的截屏方法)

该命令读取系统的framebuffer,需要获得系统权限:

(1). 在AndroidManifest.xml文件中添加

<uses-permissionandroid:name="android.permission.READ_FRAME_BUFFER"/>

(2). 修改APK为系统权限,将APK放到源码中编译, 修改Android.mk

LOCAL_CERTIFICATE := platform

publicvoid takeScreenShot(){
    String mSavedPath = Environment.getExternalStorageDirectory()+File. separator + "screenshot.png" ;
try {
           Runtime. getRuntime().exec("screencap -p " + mSavedPath);
    } catch (Exception e) {
           e.printStackTrace();
    } 

2、利用系统的API,实现Screenshot,这部分代码是系统隐藏的,需要在源码下编译(基于Android SDK的截屏方法)

(1). 在AndroidManifest.xml文件中添加

<uses-permissionandroid:name="android.permission.READ_FRAME_BUFFER"/>

(2). 修改APK为系统权限,将APK放到源码中编译, 修改Android.mk

LOCAL_CERTIFICATE := platform

public boolean takeScreenShot(String imagePath){

             if(imagePath.equals("" )){
                      imagePath = Environment.getExternalStorageDirectory()+File. separator+"Screenshot.png" ;
             }

          Bitmap mScreenBitmap;
          WindowManager mWindowManager;
          DisplayMetrics mDisplayMetrics;
          Display mDisplay;

          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) dims[0], ( int) dims[1]);

          if (mScreenBitmap == null) {
                 return false ;
          }

       try {
          FileOutputStream out = new FileOutputStream(imagePath);
          mScreenBitmap.compress(Bitmap.CompressFormat. PNG, 100, out);

        } catch (Exception e) {

          return false ;
        }       

       return true ;
}

基于Android ddmlib进行截屏

public class ScreenShot {
 private BufferedImage image = null;
 /**
  * @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);
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }  

 public void getScreenShot(IDevice device,String filename) {
  RawImage rawScreen = null;
  try {
   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 = false;
   int width2 = landscape ? rawScreen.height : rawScreen.width;
   int height2 = landscape ? rawScreen.width : rawScreen.height;
   if (image == null) {
    image = new BufferedImage(width2, height2,
      BufferedImage.TYPE_INT_RGB);
   } else {
    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.height; y++) {
    for (int x = 0; x < rawScreen.width; x++, index += indexInc) {
     int value = rawScreen.getARGB(index);
     if (landscape)
      image.setRGB(y, rawScreen.width - x - 1, value);
     else
      image.setRGB(x, y, value);
    }
   }
   try {
    ImageIO.write((RenderedImage) image, "PNG", new File("D:/"
      + filename + ".jpg"));
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }  

 /**
  * 获取得到device对象
  * @return
  */
 private IDevice getDevice(){
  IDevice device;
  AndroidDebugBridge bridge = AndroidDebugBridge
    .createBridge("adb", true);//如果代码有问题请查看API,修改此处的参数值试一下
  waitDevicesList(bridge);
  IDevice devices[] = bridge.getDevices();
  device = devices[0];
  return device;
 }  

 /**
  * 等待查找device
  * @param bridge
  */
 private void waitDevicesList(AndroidDebugBridge bridge) {
  int count = 0;
  while (bridge.hasInitialDeviceList() == false) {
   try {
    Thread.sleep(500);
    count++;
   } catch (InterruptedException e) {
   }
   if (count > 240) {
    System.err.print("等待获取设备超时");
    break;
   }
  }
 }  

Android本地编程(Native Programming)读取framebuffer

(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命令来实现。

代码实现

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 < yResolution; 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.length; x++) {
                try{
                int rgb = frameBuffer.readShort() & 0xffff;
                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) | blue;
                }catch (EOFException e){
                    System.out.println("EOFException=" + e);
                }
              }
        } catch (IOException exception) {
            System.out.println("convertToRgba32Exception=" + exception);
      }
    }

}

(2)

public class SimpleScreenshotActivity extends Activity {   

    private Display mDisplay;
    private WindowManager mWindowManager;
    private DisplayMetrics mDisplayMetrics;
    private Bitmap mScreenBitmap;
    private Matrix mDisplayMatrix;   

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

        new Thread(new Runnable() {   

            @Override
            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 = ss;
        }   

        // If we couldn‘t take the screenshot, notify the user
        if (mScreenBitmap == null) {
            return;
        }   

        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();   

        try {
            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();
        }
        FileOutputStream out;
        try {
            out = new FileOutputStream(file);
            if (bitmap.compress(Bitmap.CompressFormat.PNG, 70, out)) {
                out.flush();
                out.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
</SPAN>   

package org.winplus.ss;  

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;  

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import android.os.SystemProperties;  

public class SimpleScreenshotActivity extends Activity {  

 private Display mDisplay;
 private WindowManager mWindowManager;
 private DisplayMetrics mDisplayMetrics;
 private Bitmap mScreenBitmap;
 private Matrix mDisplayMatrix;  

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

  new Thread(new Runnable() {  

   @Override
   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 = ss;
        }  

        // If we couldn‘t take the screenshot, notify the user
        if (mScreenBitmap == null) {
            return;
        }  

        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();  

  try {
   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();
  }
  FileOutputStream out;
  try {
   out = new FileOutputStream(file);
   if (bitmap.compress(Bitmap.CompressFormat.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的就没去做测试了。

利用TakeScreenShotService截图

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();
                        }
                    } else {
                        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) {
                return;
            }
            ComponentName cn = new ComponentName("com.android.systemui",
                    "com.android.systemui.screenshot.TakeScreenshotService");
            Intent intent = new Intent();
            intent.setComponent(cn);
            ServiceConnection conn = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    synchronized (mScreenshotLock) {
                        if (mScreenshotConnection != this) {
                            return;
                        }
                        Messenger messenger = new Messenger(service);
                        Message msg = Message.obtain(null, 1);
                        final ServiceConnection myConn = this;
                        Handler h = new Handler(mHandler.getLooper()) {
                            @Override
                            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;
                        try {
                            messenger.send(msg);
                        } catch (RemoteException e) {
                        }
                    }
                }
                @Override
                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() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    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);
                            try {
                                callback.send(reply);
                            } catch (RemoteException e) {
                            }
                        }
                    }, msg.arg1 > 0, msg.arg2 > 0);
            }
        }
    };

    @Override
    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();
            return;
        }

        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.
     *
     * @hide
     */
    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;
        return 0;
    }

    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();
    } else {
        // be safe with an empty bitmap.
        delete pixels;
        bitmap->setPixels(NULL);
    }

    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
}

待补内容:

Canvas;

Bitmap.createBitmap;

时间: 2024-08-27 07:24:12

getDrawingCache()和Android中的截图方法简介的相关文章

使用SourceInsight查看android中的native方法

SourceInsight真的是查看源代码的神器.只要知道了文件名,就可以快速定位到文件.最近在研究Android的touch系统,发现MotionEvent的getX()方法调用的是nativeGexAxis(),于是想看看该本地代码是如何实现的,但我不知道nativeGexAxis的实现代码在什么位置.Android中的类调用本地代码是通过类名相似或Android.mk文件定义.MotionEvent所在的pack为android.view,将点号换成下划线,我们可以推测MotionEven

Android中Toast的用法简介

转自:http://www.cnblogs.com/GnagWang/archive/2010/11/26/1888762.html Toast是Android中用来显示显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失.下面用一个实例来看看如何使用Toast. 1.默认效果 代码 Toast.makeText(getApplicationContext(), "默认Toast样式",     Toast.LEN

Android中常见IPC方法总结

欢迎转载,转载请注明出处http://blog.csdn.net/l664675249/article/details/50654926 IPC (Interprocess communication)跨进程通信,是指在两个进程之间交换数据的过程.多进程通信一般分为两种情况.第一种,一个应用因为自身的需要采用多进程实现,比如某些模块由于特殊原因需要运行在单独的进程中.第二种情况,当前应用需要获得其它应用的数据,由于是两个应用,所以必须采用跨进程的方式.下面就对常用的IPC方法做一个总结. 使用B

Android中测试框架使用简介

测试 好的程序是测出来的. 测试的目的:尽可能多的测试出程序中的bug. 测试分为 黑盒测试:测试业务逻辑 白盒测试:测试逻辑方法.一般是写一段脚本代码,通过脚本代码去调用业务逻辑方法. 按照测试粒度分为: 方法测试:function test   测试某一个方法 单元测试:unit test      测试某一个类或者某一个单元的多个方法 集成测试:integration testv服务器和客户端联合调试,测试的是多个单元. 系统测试 system test      测试的是整个系统的功能,

Android中getResources().getConfiguration()方法的作用

Android中Configuration类专门用于描述手机设备上的配置信息,这些配置信息既包括用户特定的配置项,也包括系统的动态设备配置. Configuration对象获得: 1 Configuration configuration = getResources().getConfiguration(); Configuration对象提供的基本属性可以获取系统的配置信息: 1 public int densityDpi; //得到设备的密度 2 public float fontScal

android中nterpolator的方法详细

android:interpolator Interpolator 被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等. android中的文档内容如下: AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速 AccelerateInterpolator  在动画开始的地方速率改变比较慢,然后开始加速 Ant

为Android中PackageManagerService添加方法

PackageManagerService相关类结构 分析 从上图可以看出若想为PackageManagerService添加方法需要进行以下改动 1,在IPackageManager.aidl文件中增加方法的声明: 2,在PackageManager类中增加抽象方法说明: 3,在ApplicationPackageManager中增加方法,具体实现为调用远程PackageManagerService的方法: 4,在PackageManagerService中增加方法的具体实现: 注:由于Moc

Android截屏截图方法汇总(Activity、View、ScrollView、ListView、RecycleView、WebView截屏截图)

Android截屏 Android截屏的原理:获取具体需要截屏的区域的Bitmap,然后绘制在画布上,保存为图片后进行分享或者其它用途 一.Activity截屏 1.截Activity界面(包含空白的状态栏) /** * 根据指定的Activity截图(带空白的状态栏) * * @param context 要截图的Activity * @return Bitmap */ public static Bitmap shotActivity(Activity context) { View vie

android中的一些方法理解 onFling,onScroll,onMeasure,onLayout,getLeft,getX,getRawX

前言 第一次注册博客,并开始写博客,希望自己能够坚持写下去(呵呵,坚持乃成功字母).以后把自己在android方面的理解,或者别的博友的优秀代码片段都写入自己的博客,一是方便自己日后回顾(温故而知新嘛),二是给需要的同学阅读了解. 正文 下面开始讲下我对onFling,onScroll,onMeasure,onLayout,getLeft,getX,getRawX这几个方法的理解. onFling @Override public boolean onFling(MotionEvent e1,