1. 程式人生 > >Android截圖機制淺析

Android截圖機制淺析

的除錯工具DDMS提供截圖功能,很多同步軟體例如豌豆莢也都提供截圖功能,經分析Android截圖原理大致如下:

DDMS是通過adb呼叫裝置端的adbd(ADBdaemon)提供的framebufferservice進行截圖(原始碼在system/core/adb/framebuffer_service.c),在較早版本的Android中,framebuffer service通過直接讀framebuffer 裝置(/dev/graphics/fb0)來截圖,在較新版本的Android中,framebuffer service則呼叫截圖工具screencap來截圖。那些同步軟體也是呼叫screencap實現截圖

的。

screencap是Android原生自帶的工具,是一個C寫的可執行檔案,在裝置上的/system/bin/下面可以找到它,screencap截圖後可儲存為PNG格式檔案或RGBRAW檔案。

screencap的原始碼在frameworks/base/cmds/screencap/,它呼叫SurfaceFlinger提供的截圖介面ScreenshotClient,其原始碼在frameworks/native/libs/gui/SurfaceComposerClient.cpp(該路徑在不同版本的Android原始碼中可能略有差別),ScreenshotClient通過程序間通訊呼叫SurfaceFlinger service的截圖

功能,原始碼在frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中的函式SurfaceFlinger::captureScreen。

在各種截圖方法中,讀framebuffer裝置(/dev/graphics/fb0)的方式在某些使用硬體overlay顯示的裝置上可能無法擷取到某些畫面(例如video playback和camera preview畫面),但是SurfaceFlinger提供的上述截圖介面則可以完美擷取任何螢幕畫面,因此相對來說是Android上最正規最完善的截圖方法,使用起來也非常簡單。但需注意,ScreenshotClient等介面在不同Android

版本上可能存在差異,例如在JellyBean的最新版本上ScreenshotClient的成員函式update增加了一個引數display(應該是為了支援多個顯示裝置),導致與之前版本不相容。

此外Android還自帶另一個截圖工具screenshot,原始碼在frameworks/base/cmds/screenshot/,它通過直接讀framebuffer 裝置(/dev/graphics/fb0)來截圖,儲存為PNG格式檔案。screenshot貌似沒有用處。

原文二:(用自己的方式實現截圖)

import Java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;

public class ScreenShot {
    // 獲取指定Activity的截圖,儲存到png檔案
    private static Bitmap takeScreenShot(Activity activity) {
        // View是你需要截圖的View
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap b1 = view.getDrawingCache();

        // 獲取狀態列高度
        Rect frame = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;
        Log.i("TAG", "" + statusBarHeight);

        // 獲取螢幕長和高
        int width = activity.getWindowManager().getDefaultDisplay().getWidth();
        int height = activity.getWindowManager().getDefaultDisplay()
                .getHeight();
        // 去掉標題欄
        // Bitmap b = Bitmap.createBitmap(b1, 0, 25, 320, 455);
        Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height
                - statusBarHeight);
        view.destroyDrawingCache();
        return b;
    }

    // 儲存到sdcard
    private static void savePic(Bitmap b, String strFileName) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(strFileName);
            if (null != fos) {
                b.compress(Bitmap.CompressFormat.PNG, 90, fos);
                fos.flush();
                fos.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 程式入口
    public static void shoot(Activity a) {
        ScreenShot.savePic(ScreenShot.takeScreenShot(a), "sdcard/xx.png");
    }
}

需要注意的是,shoot方法只能在view已經被載入後方可呼叫。

或者在    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        // TODO Auto-generated method stub
        super.onWindowFocusChanged(hasFocus);
        ScreenShot.shoot(this);
    }中呼叫


原文三:

淺談android截圖問題

連結:http://my.oschina.net/JumpLong/blog/75556

做了幾個月的截圖開發,稍微瞭解了一下這方面的知識,於是拿來分享一下,也許對你有一些幫助吧。

    我是基於android2.3.3系統之上的,想必大家應該知道在android原始碼下面有個檔案叫做screencap吧,位於frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp,你直接在linux下編譯(就是去screencap的檔案mm,然後儲存在 /system/bin/test-screencap),然後push到手機上再通過電腦去敲命令./test-screencap /mnt/sdcard/scapxx.png就可以實現截圖(不要匯入到/mntsdcard,反正我是匯入到system目錄裡面才可以執行)。

  1. /* 
  2.  * Copyright (C) 2010 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */
  16. #include <utils/Log.h>
  17. #include <binder/IPCThreadState.h>
  18. #include <binder/ProcessState.h>
  19. #include <binder/IServiceManager.h>
  20. #include <binder/IMemory.h>
  21. #include <surfaceflinger/ISurfaceComposer.h>
  22. #include <SkImageEncoder.h>
  23. #include <SkBitmap.h>
  24. usingnamespace android;  
  25. int main(int argc, char** argv)  
  26. {  
  27.     if (argc != 2) {  
  28.         printf("usage: %s path\n", argv[0]);  
  29.         exit(0);  
  30.     }  
  31.     const String16 name("SurfaceFlinger");  
  32.     sp<ISurfaceComposer> composer;  
  33.     getService(name, &composer);  
  34.     sp<IMemoryHeap> heap;  
  35.     uint32_t w, h;  
  36.     PixelFormat f;  
  37.     status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);  
  38.     if (err != NO_ERROR) {  
  39.         fprintf(stderr, "screen capture failed: %s\n", strerror(-err));  
  40.         exit(0);  
  41.     }  
  42.     printf("screen capture success: w=%u, h=%u, pixels=%p\n",  
  43.             w, h, heap->getBase());  
  44.     printf("saving file as PNG in %s ...\n", argv[1]);  
  45.     SkBitmap b;  
  46.     b.setConfig(SkBitmap::kARGB_8888_Config, w, h);  
  47.     b.setPixels(heap->getBase());  
  48.     SkImageEncoder::EncodeFile(argv[1], b,  
  49.             SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);  
  50.     return 0;  
  51. }  

    其實這個程式真正用到的就是一個叫做capturescreen的函式,而capturescreen會呼叫captureScreenImplLocked這個函式

下面是程式碼:

  1. status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,  
  2.         sp<IMemoryHeap>* heap,  
  3.         uint32_t* w, uint32_t* h, PixelFormat* f,  
  4.         uint32_t sw, uint32_t sh)  
  5. {  
  6.    LOGI("captureScreenImplLocked");  
  7.     status_t result = PERMISSION_DENIED;  
  8.     // only one display supported for now
  9.     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))  
  10.         return BAD_VALUE;  
  11.     if (!GLExtensions::getInstance().haveFramebufferObject())  
  12.         return INVALID_OPERATION;  
  13.     // get screen geometry
  14.     const DisplayHardware& hw(graphicPlane(dpy).displayHardware());  
  15.     const uint32_t hw_w = hw.getWidth();  
  16.     const uint32_t hw_h = hw.getHeight();  
  17.     if ((sw > hw_w) || (sh > hw_h))  
  18.         return BAD_VALUE;  
  19.     sw = (!sw) ? hw_w : sw;  
  20.     sh = (!sh) ? hw_h : sh;  
  21.     constsize_t size = sw * sh * 4;  
  22.     // make sure to clear all GL error flags
  23.     while ( glGetError() != GL_NO_ERROR ) ;  
  24.     // create a FBO
  25.     GLuint name, tname;  
  26.     glGenRenderbuffersOES(1, &tname);  
  27.     glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);  
  28.     glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);  
  29.     glGenFramebuffersOES(1, &name);  
  30.     glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);  
  31.     glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,  
  32.             GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);  
  33.     GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);  
  34.     if (status == GL_FRAMEBUFFER_COMPLETE_OES) {  
  35.         // invert everything, b/c glReadPixel() below will invert the FB
  36.         glViewport(0, 0, sw, sh);  
  37.         glScissor(0, 0, sw, sh);  
  38.         glMatrixMode(GL_PROJECTION);  
  39.         glPushMatrix();  
  40.         glLoadIdentity();  
  41.         glOrthof(0, hw_w, 0, hw_h, 0, 1);  
  42.         glMatrixMode(GL_MODELVIEW);  
  43.         // redraw the screen entirely...
  44.         glClearColor(0,0,0,1);  
  45.         glClear(GL_COLOR_BUFFER_BIT);  
  46.         const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);  
  47.         constsize_t count = layers.size();  
  48.         for (size_t i=0 ; i<count ; ++i) {  
  49.             const sp<LayerBase>& layer(layers[i]);  
  50.             layer->drawForSreenShot();  
  51.         }  
  52.         // XXX: this is needed on tegra
  53.         glScissor(0, 0, sw, sh);  
  54.         // check for errors and return screen capture
  55.         if (glGetError() != GL_NO_ERROR) {  
  56.             // error while rendering
  57.             result = INVALID_OPERATION;  
  58.         } else {  
  59.             // allocate shared memory large enough to hold the
  60.             

    相關推薦

    Android機制淺析

    的除錯工具DDMS提供截圖功能,很多同步軟體例如豌豆莢也都提供截圖功能,經分析Android截圖原理大致如下: DDMS是通過adb呼叫裝置端的adbd(ADBdaemon)提供的framebufferservice進行截圖(原始碼在system/core/adb/framebuffer_service

    android

    //截圖 private Bitmap generateSpringCard() { View view = getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCac

    android程式碼實現方法

    分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

    Android

    二、具體實現方式 1),第一種實現方式 /** * 對View進行量測,佈局後截圖 * * @param view * @return */ public Bitmap convertViewTo

    Android方案

    Android截圖 Android截圖的原理:獲取具體需要截圖的區域的Bitmap,然後繪製在畫布上,儲存為圖片後進行分享或者其它用途 在截圖功能中,有時需要擷取全屏的內容,有時需要擷取超過一屏的內容(比如:Listview,Scrollview,RecyclerVi

    Android (Screenshot)程式碼流程小結

    一、基本介紹          在Android 4.0 之前,Android手機上如果要使用截圖功能,只能通過Root手機,且使用第3方截圖軟體來實現截圖功能。          Android4.0中,系統自帶了截圖功能,使用方法是音量下(VOLUME_DOWN

    使用python編寫android指令碼

             測試的過程中經常需要擷取螢幕,通常的做法是使用手機自帶的截圖功能,然後將截圖檔案複製出來,這種方法的優點是不需要連線資料線就可截圖,缺點則是生成的截圖檔案命名是隨機命名的,複製出來也比較麻煩。另一種方法是使用PC端的手機助手類軟體。 這裡使用python編

    Python實現對Android

    背景: 測試過程中,總是需要對Android裝置進行截圖,然後在截圖中標註問題描述; 手動方式: 1.使用adb scrrencap /sdcard/screen.png 命令對Android裝置進行截圖 2.然後再使用adb pull /sdcard/scrren.pn

    android實現

    import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Rect; import android.view.View; import java.io.Fi

    Android 到桌面的最佳處理方案-無須ROOT-適用Android 8.0

    最近在做測試中發現一些重複性動作費時費力還容易出錯,百度了一圈發現竟然沒有WIN的教程,全是MAC的,既然做自動化方向,能偷懶那就當然自動化一波 準備 安卓手機一部,別是2.2以下,我的方法都支援,不需要root 安裝驅動,老生常談,有Android開

    android功能實現

    這篇文章實現了擷取當前Activity頁面,並把截圖圖片儲存在SD卡指定路徑下功能(親測可用)。 首先要確保manifest有操作SD卡許可權 <!-- 允許應用內建sd卡 --> <uses-permission android:name="

    Android(fb0)

    1.申請獲取root許可權,這一步很重要,不然會沒有作用 private void getPermession() { try { Process process = Ru

    Android儲存png圖片的例項程式碼(去掉狀態列)

    今天開發中遇到了android手機截圖的需求,以下是實現程式碼: 這是截圖工具類: public class ScreenShot { // 獲取指定Activity的截圖,儲存到png檔案

    快速新增簡單的Android分享

             基於cocos2d-x開發(我的是V2.2.3),分享的方法有很多,例如用SharedSDK,或者直接用各個開發平臺的SDK,但對於android平臺,如果要求不是很高的話,有一種簡單的方式(Intent元件+jni)。        1.假如工程名是LC

    Android命令screencap與視訊錄製命令screenrecord

    檢視幫助命令[email protected] ~$ adb shell screencap -v screencap: invalid option -- v usage: screencap [-hp] [-d display-id] [FILENAME] -h: this mes

    Android如何生成gif動態

    我們在部落格裡面經常看到有人上傳的截圖是動態的,即gif格式,那麼截圖怎麼生成gif格式的圖片呢? 所需工具 ①視訊截圖工具:用Android Studio開發Android的人,應該都知道,And

    Android的幾種方法總結

    Android截圖   Android截圖的原理:獲取具體需要截圖的區域的Bitmap,然後繪製在畫布上,儲存為圖片後進行分享或者其它用途 一、Activity截圖 1、截Activity介面(包含空白的狀態列) ? 1 2 3 4

    android快捷鍵 工具 音量下鍵+電源鍵

    一般來說android手機或者平板,只要用兩個手指同時按下:音量下鍵+電源鍵,一秒鐘即可截圖,截圖圖片儲存到了/sdcard/Pictures/Screenshots資料夾下面。這個方法在nexus s、小米1原生android 4.0和nexus 7裡面都是確切可用的,其他

    Android、儲存、分享

    原理:將擷取到的Bitmap賦給Dialog上的ImageView,並對Dialog加了彈出和收起的動畫,實現截圖效果。 首先建立一個layout名為show_cut_screen_layout用於彈出截圖對話方塊,上面是一個image,下面是橫向線性佈局的兩個button

    Android事件監聽

    1. 前言 Android系統沒有直接對截圖事件監聽的介面,也沒有廣播,只能自己動手來豐衣足食,一般有三種方法。 利用FileObserver監聽某個目錄中資源變化情況 利用ContentObserver監聽全部資源的變化 監聽截圖快捷按鍵 由