android 截屏

来源:互联网 发布:seo大牛 编辑:程序博客网 时间:2024/05/15 23:59

http://www.oschina.net/question/223340_45857

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

做了几个月的截屏开发,稍微了解了一下这方面的知识,于是拿来分享一下,也许对你有一些帮助吧。

    我是基于android2.3.3系统之上的,想必大家应该知道在android源码下面有个文件叫做screencap吧,位于frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp,你直接在linux下编译(保存在 /system/bin/test-screencap),然后push到手机上再通过电脑去敲命令test-screencap /mnt/sdcard/scapxx.png就可以实现截屏。

01/*
02 * Copyright (C) 2010 The Android Open Source Project
03 *
04 * Licensed under the Apache License, Version 2.0 (the "License");
05 * you may not use this file except in compliance with the License.
06 * You may obtain a copy of the License at
07 *
08 *      http://www.apache.org/licenses/LICENSE-2.0
09 *
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  
17#include <utils/Log.h>
18  
19#include <binder/IPCThreadState.h>
20#include <binder/ProcessState.h>
21#include <binder/IServiceManager.h>
22  
23#include <binder/IMemory.h>
24#include <surfaceflinger/ISurfaceComposer.h>
25  
26#include <SkImageEncoder.h>
27#include <SkBitmap.h>
28  
29usingnamespaceandroid;
30  
31intmain(intargc, char** argv)
32{
33    if(argc != 2) {
34        printf("usage: %s path\n", argv[0]);
35        exit(0);
36    }
37  
38    constString16 name("SurfaceFlinger");
39    sp<ISurfaceComposer> composer;
40    getService(name, &composer);
41  
42    sp<IMemoryHeap> heap;
43    uint32_t w, h;
44    PixelFormat f;
45    status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
46    if(err != NO_ERROR) {
47        fprintf(stderr,"screen capture failed: %s\n",strerror(-err));
48        exit(0);
49    }
50  
51    printf("screen capture success: w=%u, h=%u, pixels=%p\n",
52            w, h, heap->getBase());
53  
54    printf("saving file as PNG in %s ...\n", argv[1]);
55  
56    SkBitmap b;
57    b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
58    b.setPixels(heap->getBase());
59    SkImageEncoder::EncodeFile(argv[1], b,
60            SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
61  
62    return0;
63}


 

    其实这个程序真正用到的就是一个叫做capturescreen的函数,而capturescreen会调用captureScreenImplLocked这个函数

下面是代码:

view source
print?
001status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
002        sp<IMemoryHeap>* heap,
003        uint32_t* w, uint32_t* h, PixelFormat* f,
004        uint32_t sw, uint32_t sh)
005{
006   LOGI("captureScreenImplLocked");
007    status_t result = PERMISSION_DENIED;
008  
009    // only one display supported for now
010    if(UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
011        returnBAD_VALUE;
012  
013    if(!GLExtensions::getInstance().haveFramebufferObject())
014        returnINVALID_OPERATION;
015  
016    // get screen geometry
017    constDisplayHardware& hw(graphicPlane(dpy).displayHardware());
018    constuint32_t hw_w = hw.getWidth();
019    constuint32_t hw_h = hw.getHeight();
020  
021    if((sw > hw_w) || (sh > hw_h))
022        returnBAD_VALUE;
023  
024    sw = (!sw) ? hw_w : sw;
025    sh = (!sh) ? hw_h : sh;
026    constsize_tsize = sw * sh * 4;
027  
028    // make sure to clear all GL error flags
029    while( glGetError() != GL_NO_ERROR ) ;
030  
031    // create a FBO
032    GLuint name, tname;
033    glGenRenderbuffersOES(1, &tname);
034    glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
035    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
036    glGenFramebuffersOES(1, &name);
037    glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
038    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
039            GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
040  
041    GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
042    if(status == GL_FRAMEBUFFER_COMPLETE_OES) {
043  
044        // invert everything, b/c glReadPixel() below will invert the FB
045        glViewport(0, 0, sw, sh);
046        glScissor(0, 0, sw, sh);
047        glMatrixMode(GL_PROJECTION);
048        glPushMatrix();
049        glLoadIdentity();
050        glOrthof(0, hw_w, 0, hw_h, 0, 1);
051        glMatrixMode(GL_MODELVIEW);
052  
053        // redraw the screen entirely...
054        glClearColor(0,0,0,1);
055        glClear(GL_COLOR_BUFFER_BIT);
056  
057        constVector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
058        constsize_tcount = layers.size();
059        for(size_ti=0 ; i<count ; ++i) {
060            constsp<LayerBase>& layer(layers[i]);
061            layer->drawForSreenShot();
062        }
063  
064        // XXX: this is needed on tegra
065        glScissor(0, 0, sw, sh);
066  
067        // check for errors and return screen capture
068        if(glGetError() != GL_NO_ERROR) {
069            // error while rendering
070            result = INVALID_OPERATION;
071        }else {
072            // allocate shared memory large enough to hold the
073            // screen capture
074            sp<MemoryHeapBase> base(
075                    newMemoryHeapBase(size, 0,"screen-capture") );
076            void*const ptr = base->getBase();
077            if(ptr) {
078                // capture the screen with glReadPixels()
079                glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
080                if(glGetError() == GL_NO_ERROR) {
081                    *heap = base;
082                    *w = sw;
083                    *h = sh;
084                    *f = PIXEL_FORMAT_RGBA_8888;
085                    result = NO_ERROR;
086                }
087            }else {
088                result = NO_MEMORY;
089            }
090        }
091        glEnable(GL_SCISSOR_TEST);
092        glViewport(0, 0, hw_w, hw_h);
093        glMatrixMode(GL_PROJECTION);
094        glPopMatrix();
095        glMatrixMode(GL_MODELVIEW);
096  
097  
098    }else {
099        result = BAD_VALUE;
100    }
101  
102    // release FBO resources
103    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
104    glDeleteRenderbuffersOES(1, &tname);
105    glDeleteFramebuffersOES(1, &name);
106  
107    hw.compositionComplete();
108  
109    returnresult;
110}
111  
112  
113status_t SurfaceFlinger::captureScreen(DisplayID dpy,
114        sp<IMemoryHeap>* heap,
115        uint32_t* width, uint32_t* height, PixelFormat* format,
116        uint32_t sw, uint32_t sh)
117{
118           LOGI("into captureScreen");           
119    // only one display supported for now
120    if(UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
121        returnBAD_VALUE;
122  
123    if(!GLExtensions::getInstance().haveFramebufferObject())
124        returnINVALID_OPERATION;
125  
126    classMessageCaptureScreen : public MessageBase {
127        SurfaceFlinger* flinger;
128        DisplayID dpy;
129        sp<IMemoryHeap>* heap;
130        uint32_t* w;
131        uint32_t* h;
132        PixelFormat* f;
133        uint32_t sw;
134        uint32_t sh;
135        status_t result;
136    public:
137        MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
138                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
139                uint32_t sw, uint32_t sh)
140            : flinger(flinger), dpy(dpy),
141              heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
142        {
143  
144        }
145        status_t getResult()const {
146           LOGI("getResult()");           
147         returnresult;
148        }
149        virtualboolhandler() {
150  
151  
152   LOGI("handler()");
153            Mutex::Autolock _l(flinger->mStateLock);
154  
155            // if we have secure windows, never allow the screen capture
156            if(flinger->mSecureFrameBuffer)
157                returntrue;
158  
159            result = flinger->captureScreenImplLocked(dpy,
160                    heap, w, h, f, sw, sh);
161  
162            returntrue;
163        }
164    };
165   LOGI("before messagecapturescreen");
166    sp<MessageBase> msg =new MessageCaptureScreen(this,
167            dpy, heap, width, height, format, sw, sh);
168    status_t res = postMessageSync(msg);
169    if(res == NO_ERROR) {
170        res =static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
171    }
172    returnres;
173}

而这个函数关键又使用了opengl的几个函数去获得图片,然而opengl又去read framebuffer(这是我的理解)。如果你去用jni调用so的方法去截屏的话,就可以把screencap这个文件稍微修改一下然后做成so文件,方法可以参考这篇博客:http://blog.csdn.net/zx19899891/article/details/7072291

    主要是补充一下怎么去存放文件与编译吧,当然我说的方法只是我做的方法不代表是很好用的。

    存放:在eclipse新建一个android工程,保存后找到这个工程(如screencap)的存放位置 然后把这个文件放到android源代码的development文件里面,然后在你的那个工程文件里面新建一个文件夹,名字叫做jni(这个文件夹平行于src文件夹,screencap/jni),把上面博客提到的那个C++跟mk(screencap/jni/com_android_ScreenCap_ScreenCapNative.cpp和screencap/jni/Android.mk)文件放进去,最后在把编译的mk文件放在screencap目录下(screencap/Android.mk);

    编译:编译是个很伟大的工程,需要你花大量的时间与精力。直接在终端进入工程存放的所在位置,我的是Administrator/Android.2.3.3/development,然后mm(Builds all of the modules in the current directory),如果成功,那么你运气比较好,在终端回提示你APK保存的位置。push进手机试一试。但是往往是不成功的。你可能会遇到一些问题,比如android.permission.ACCESS_SURFACE_FLINGER ,android.permission.READ_FRAME_BUFFER(因为capturescrren这个函数是surfaceflinger里面的函数,然而surfaceflinger里面的opengl截屏函数会去读取framebuffer),相关源代码是:

view source
print?
001status_t SurfaceFlinger::onTransact(
002    uint32_t code,const Parcel& data, Parcel* reply, uint32_t flags)
003{
004  
005    switch(code) {
006        caseCREATE_CONNECTION:
007        caseOPEN_GLOBAL_TRANSACTION:
008        caseCLOSE_GLOBAL_TRANSACTION:
009        caseSET_ORIENTATION:
010        caseFREEZE_DISPLAY:
011        caseUNFREEZE_DISPLAY:
012        caseBOOT_FINISHED:
013        caseTURN_ELECTRON_BEAM_OFF:
014        caseTURN_ELECTRON_BEAM_ON:
015        {
016            // codes that require permission check
017            IPCThreadState* ipc = IPCThreadState::self();
018            constintpid = ipc->getCallingPid();
019            constintuid = ipc->getCallingUid();
020            if((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) {
021                LOGE("Permission Denial: "
022                        "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
023                returnPERMISSION_DENIED;
024            }
025            break;
026        }
027        caseCAPTURE_SCREEN:
028        {
029            // codes that require permission check
030            IPCThreadState* ipc = IPCThreadState::self();
031            constintpid = ipc->getCallingPid();
032            constintuid = ipc->getCallingUid();
033            if((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
034  
035                LOGE("Permission Denial: "
036                        "can't read framebuffer pid=%d, uid=%d", pid, uid);
037                returnPERMISSION_DENIED;
038            }
039  
040            break;
041        }
042    }
043  
044    status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
045    if(err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
046        CHECK_INTERFACE(ISurfaceComposer, data, reply);
047        if(UNLIKELY(!mHardwareTest.checkCalling())) {
048            IPCThreadState* ipc = IPCThreadState::self();
049            constintpid = ipc->getCallingPid();
050            constintuid = ipc->getCallingUid();
051  
052   LOGI("err");
053            LOGE("Permission Denial: "
054                    "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
055            returnPERMISSION_DENIED;
056        }
057        intn;
058        switch(code) {
059            case1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
060            case1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
061                returnNO_ERROR;
062            case1002:  // SHOW_UPDATES
063                n = data.readInt32();
064                mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
065                returnNO_ERROR;
066            case1003:  // SHOW_BACKGROUND
067                n = data.readInt32();
068                mDebugBackground = n ? 1 : 0;
069                returnNO_ERROR;
070            case1004:{ // repaint everything
071                Mutex::Autolock _l(mStateLock);
072                constDisplayHardware& hw(graphicPlane(0).displayHardware());
073                mDirtyRegion.set(hw.bounds());// careful that's not thread-safe
074                signalEvent();
075                returnNO_ERROR;
076            }
077            case1005:{ // force transaction
078                setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
079                returnNO_ERROR;
080            }
081            case1006:{ // enable/disable GraphicLog
082                intenabled = data.readInt32();
083                GraphicLog::getInstance().setEnabled(enabled);
084                returnNO_ERROR;
085            }
086            case1007: // set mFreezeCount
087                mFreezeCount = data.readInt32();
088                mFreezeDisplayTime = 0;
089                returnNO_ERROR;
090            case1010:  // interrogate.
091                reply->writeInt32(0);
092                reply->writeInt32(0);
093                reply->writeInt32(mDebugRegion);
094                reply->writeInt32(mDebugBackground);
095                returnNO_ERROR;
096            case1013: {
097                Mutex::Autolock _l(mStateLock);
098                constDisplayHardware& hw(graphicPlane(0).displayHardware());
099                reply->writeInt32(hw.getPageFlipCount());
100            }
101            returnNO_ERROR;
102        }
103    }
104    returnerr;
105}

这个仅仅只是开始!  你会发现你即使在xml里面添加相应的权限仍然会有这个问题出现,为什么呢?在packageManger文件里面发现相关代码:

view source
print?
01   intcheckSignaturesLP(Signature[] s1, Signature[] s2) {
02       if(s1 == null) {
03           returns2 == null
04                   ? PackageManager.SIGNATURE_NEITHER_SIGNED
05                   : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
06       }
07       if(s2 == null) {
08           returnPackageManager.SIGNATURE_SECOND_NOT_SIGNED;
09       }
10       HashSet<Signature> set1 =new HashSet<Signature>();
11       for(Signature sig : s1) {
12           set1.add(sig);
13       }
14       HashSet<Signature> set2 =new HashSet<Signature>();
15       for(Signature sig : s2) {
16           set2.add(sig);
17       }
18       // Make sure s2 contains all signatures in s1.
19       if(set1.equals(set2)) {
20           returnPackageManager.SIGNATURE_MATCH;
21       }
22       returnPackageManager.SIGNATURE_NO_MATCH;
23   }
24 
25 
26 
27// Check for shared user signatures
28       if(pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
29           if(checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
30                   pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
31               Slog.e(TAG,"Package " + pkg.packageName
32                       +" has no signatures that match those in shared user "
33                       + pkgSetting.sharedUser.name +"; ignoring!");
34               mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
35               returnfalse;
36           }
37       }
38       returntrue;
39 
40 
41   privateboolean verifySignaturesLP(PackageSetting pkgSetting,
42           PackageParser.Package pkg) {
43 
44       // Check for shared user signatures
45       if(pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
46             
47       if(checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
48                   pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
49                 
50           Slog.e(TAG,"Package " + pkg.packageName
51                       +" has no signatures that match those in shared user "
52                       + pkgSetting.sharedUser.name +"; ignoring!");
53               mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
54               returnfalse;
55 
56           }
57       }
58       returntrue;
59   }

你在终端输入adb logcat | grep PackageManager 你会发现这两个权限根本没有赋予给你的apk,我的理解是,程序需要权限,然后apk仍然需要权限。那怎么样给apk赋予权限呢,两个方法,一个是在我上面说的screencap/Android.mk里面添加platform一行,然后在回到mm。还有一个方法就是通过sign。这两个方法都是给apk赋予system权限,但是我试过这两种方法,都有问题,就是在adb install的时候会显示签名不兼容,查看源代码会发现uid跟gid不匹配。这些是我这段时间发现的问题,大家有问题可以交流交流。

再说说几个简单的应用层截屏吧,很简单,就是几个函数调用而已

view source
print?
1View view = getWindow().getDecorView();   
2      Display display =this.getWindowManager().getDefaultDisplay();   
3      view.layout(0,0, display.getWidth(), display.getHeight());   
4      view.setDrawingCacheEnabled(true);//允许当前窗口保存缓存信息,这样  getDrawingCache()方法才会返回一个Bitmap   
5      Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache());

我对这个程序的理解就是,它仅仅只能截取当前的activity,也就是说如果你运行这个程序后它就截取你这个程序的当前屏幕的信息。我们假设你做成一个按钮,在点击按钮后sleep5秒再调用这个方法(假设你的activity叫做screen)。当你点击按钮以后,然后你再点击home或者返回按钮,等到5秒后你那个程序就会截取到你当前屏幕?不是!它只会截取那个运行于后台的screen这个activity。 

这些只是我的一点小小的总结,而且肯定有不对的地方,希望大家一起来解决截屏的问题!

原创粉丝点击