生活随笔
收集整理的這篇文章主要介紹了
android后台截屏实现(2)--screencap源码修改
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ? ? ? 首先找到screencap類在Android源碼中的位置,/442/frameworks/base/cmds/screencap/screencap.cpp。
源碼如下:
[cpp]?view plaincopy
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? #include?<errno.h>?? #include?<unistd.h>?? #include?<stdio.h>?? #include?<fcntl.h>?? ?? #include?<linux/fb.h>?? #include?<sys/ioctl.h>?? #include?<sys/mman.h>?? ?? #include?<binder/ProcessState.h>?? ?? #include?<gui/SurfaceComposerClient.h>?? #include?<gui/ISurfaceComposer.h>?? ?? #include?<ui/PixelFormat.h>?? ?? #include?<SkImageEncoder.h>?? #include?<SkBitmap.h>?? #include?<SkData.h>?? #include?<SkStream.h>?? ?? using?namespace?android;?? ?? static?uint32_t?DEFAULT_DISPLAY_ID?=?ISurfaceComposer::eDisplayIdMain;?? ?? static?void?usage(const?char*?pname)?? {?? ????fprintf(stderr,?? ????????????"usage:?%s?[-hp]?[-d?display-id]?[FILENAME]\n"?? ????????????"???-h:?this?message\n"?? ????????????"???-p:?save?the?file?as?a?png.\n"?? ????????????"???-d:?specify?the?display?id?to?capture,?default?%d.\n"?? ????????????"If?FILENAME?ends?with?.png?it?will?be?saved?as?a?png.\n"?? ????????????"If?FILENAME?is?not?given,?the?results?will?be?printed?to?stdout.\n",?? ????????????pname,?DEFAULT_DISPLAY_ID?? ????);?? }?? ?? static?SkBitmap::Config?flinger2skia(PixelFormat?f)?? {?? ????switch?(f)?{?? ????????case?PIXEL_FORMAT_RGB_565:?? ????????????return?SkBitmap::kRGB_565_Config;?? ????????default:?? ????????????return?SkBitmap::kARGB_8888_Config;?? ????}?? }?? ?? static?status_t?vinfoToPixelFormat(const?fb_var_screeninfo&?vinfo,?? ????????uint32_t*?bytespp,?uint32_t*?f)?? {?? ?? ????switch?(vinfo.bits_per_pixel)?{?? ????????case?16:?? ????????????*f?=?PIXEL_FORMAT_RGB_565;?? ????????????*bytespp?=?2;?? ????????????break;?? ????????case?24:?? ????????????*f?=?PIXEL_FORMAT_RGB_888;?? ????????????*bytespp?=?3;?? ????????????break;?? ????????case?32:?? ?????????????? ????????????*f?=?PIXEL_FORMAT_RGBX_8888;?? ????????????*bytespp?=?4;?? ????????????break;?? ????????default:?? ????????????return?BAD_VALUE;?? ????}?? ????return?NO_ERROR;?? }?? ?? int?main(int?argc,?char**?argv)?? {?? ????ProcessState::self()->startThreadPool();?? ?? ????const?char*?pname?=?argv[0];?? ????bool?png?=?false;?? ????int32_t?displayId?=?DEFAULT_DISPLAY_ID;?? ????int?c;?? ????while?((c?=?getopt(argc,?argv,?"phd:"))?!=?-1)?{?? ????????switch?(c)?{?? ????????????case?'p':?? ????????????????png?=?true;?? ????????????????break;?? ????????????case?'d':?? ????????????????displayId?=?atoi(optarg);?? ????????????????break;?? ????????????case?'?':?? ????????????case?'h':?? ????????????????usage(pname);?? ????????????????return?1;?? ????????}?? ????}?? ????argc?-=?optind;?? ????argv?+=?optind;?? ?? ????int?fd?=?-1;?? ????if?(argc?==?0)?{?? ????????fd?=?dup(STDOUT_FILENO);?? ????}?else?if?(argc?==?1)?{?? ????????const?char*?fn?=?argv[0];?? ????????fd?=?open(fn,?O_WRONLY?|?O_CREAT?|?O_TRUNC,?0664);?? ????????if?(fd?==?-1)?{?? ????????????fprintf(stderr,?"Error?opening?file:?%s?(%s)\n",?fn,?strerror(errno));?? ????????????return?1;?? ????????}?? ????????const?int?len?=?strlen(fn);?? ????????if?(len?>=?4?&&?0?==?strcmp(fn+len-4,?".png"))?{?? ????????????png?=?true;?? ????????}?? ????}?? ?? ????if?(fd?==?-1)?{?? ????????usage(pname);?? ????????return?1;?? ????}?? ?? ????void?const*?mapbase?=?MAP_FAILED;?? ????ssize_t?mapsize?=?-1;?? ?? ????void?const*?base?=?0;?? ????uint32_t?w,?s,?h,?f;?? ????size_t?size?=?0;?? ?? ????ScreenshotClient?screenshot;?? ????sp<IBinder>?display?=?SurfaceComposerClient::getBuiltInDisplay(displayId);?? ????if?(display?!=?NULL?&&?screenshot.update(display)?==?NO_ERROR)?{?? ????????base?=?screenshot.getPixels();?? ????????w?=?screenshot.getWidth();?? ????????h?=?screenshot.getHeight();?? ????????s?=?screenshot.getStride();?? ????????f?=?screenshot.getFormat();?? ????????size?=?screenshot.getSize();?? ????}?else?{?? ????????const?char*?fbpath?=?"/dev/graphics/fb0";?? ????????int?fb?=?open(fbpath,?O_RDONLY);?? ????????if?(fb?>=?0)?{?? ????????????struct?fb_var_screeninfo?vinfo;?? ????????????if?(ioctl(fb,?FBIOGET_VSCREENINFO,?&vinfo)?==?0)?{?? ????????????????uint32_t?bytespp;?? ????????????????if?(vinfoToPixelFormat(vinfo,?&bytespp,?&f)?==?NO_ERROR)?{?? ????????????????????size_t?offset?=?(vinfo.xoffset?+?vinfo.yoffset*vinfo.xres)?*?bytespp;?? ????????????????????w?=?vinfo.xres;?? ????????????????????h?=?vinfo.yres;?? ????????????????????s?=?vinfo.xres;?? ????????????????????size?=?w*h*bytespp;?? ????????????????????mapsize?=?offset?+?size;?? ????????????????????mapbase?=?mmap(0,?mapsize,?PROT_READ,?MAP_PRIVATE,?fb,?0);?? ????????????????????if?(mapbase?!=?MAP_FAILED)?{?? ????????????????????????base?=?(void?const?*)((char?const?*)mapbase?+?offset);?? ????????????????????}?? ????????????????}?? ????????????}?? ????????????close(fb);?? ????????}?? ????}?? ?? ????if?(base)?{?? ????????if?(png)?{?? ????????????SkBitmap?b;?? ????????????b.setConfig(flinger2skia(f),?w,?h,?s*bytesPerPixel(f));?? ????????????b.setPixels((void*)base);?? ????????????SkDynamicMemoryWStream?stream;?? ????????????SkImageEncoder::EncodeStream(&stream,?b,?? ????????????????????SkImageEncoder::kPNG_Type,?SkImageEncoder::kDefaultQuality);?? ????????????SkData*?streamData?=?stream.copyToData();?? ????????????write(fd,?streamData->data(),?streamData->size());?? ????????????streamData->unref();?? ????????}?else?{?? ????????????write(fd,?&w,?4);?? ????????????write(fd,?&h,?4);?? ????????????write(fd,?&f,?4);?? ????????????size_t?Bpp?=?bytesPerPixel(f);?? ????????????for?(size_t?y=0?;?y<h?;?y++)?{?? ????????????????write(fd,?base,?w*Bpp);?? ????????????????base?=?(void?*)((char?*)base?+?s*Bpp);?? ????????????}?? ????????}?? ????}?? ????close(fd);?? ????if?(mapbase?!=?MAP_FAILED)?{?? ????????munmap((void?*)mapbase,?mapsize);?? ????}?? ????return?0;?? }??
??????? 由源碼可以看出,screencap的入口main方法是從命令行獲取參數,通過分析后執行相應的操作。我們要想在java層調用這個類,就要把它的入口改成native方法的接口,修改后的代碼如下:
[cpp]?view plaincopy
#include?<jni.h>?? #include?"com_android_servicescreencap_ScreenCap.h"?? ?? #include?<errno.h>?? #include?<unistd.h>?? #include?<stdio.h>?? #include?<fcntl.h>?? ?? #include?<linux/fb.h>?? #include?<sys/ioctl.h>?? #include?<sys/mman.h>?? ?? #include?<binder/ProcessState.h>?? ?? #include?<gui/SurfaceComposerClient.h>?? #include?<gui/ISurfaceComposer.h>?? ?? #include?<ui/PixelFormat.h>?? ?? #include?<SkImageEncoder.h>?? #include?<SkBitmap.h>?? #include?<SkData.h>?? #include?<SkStream.h>?? ?? #include?<android/log.h>?? #define?LOG_TAG?"ServiceScreenCap"?? #define?LOGD(...)?__android_log_print(ANDROID_LOG_DEBUG,?LOG_TAG,?__VA_ARGS__)?? #define?LOGI(...)?__android_log_print(ANDROID_LOG_INFO,?LOG_TAG,?__VA_ARGS__)?? #define?LOGE(...)?__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)?? ?? using?namespace?android;?? ?? static?uint32_t?DEFAULT_DISPLAY_ID?=?ISurfaceComposer::eDisplayIdMain;?? ?? ?? static?status_t?vinfoToPixelFormat(const?fb_var_screeninfo&?vinfo,?? ????????uint32_t*?bytespp,?uint32_t*?f)?? {?? ?? ????switch?(vinfo.bits_per_pixel)?{?? ????????case?16:?? ????????????*f?=?PIXEL_FORMAT_RGB_565;?? ????????????*bytespp?=?2;?? ????????????break;?? ????????case?24:?? ????????????*f?=?PIXEL_FORMAT_RGB_888;?? ????????????*bytespp?=?3;?? ????????????break;?? ????????case?32:?? ?????????????? ????????????*f?=?PIXEL_FORMAT_RGBX_8888;?? ????????????*bytespp?=?4;?? ????????????break;?? ????????default:?? ????????????return?BAD_VALUE;?? ????}?? ????return?NO_ERROR;?? }?? ?? static?SkBitmap::Config?flinger2skia(PixelFormat?f)?? {?? ????switch?(f)?{?? ????????case?PIXEL_FORMAT_RGB_565:?? ????????????return?SkBitmap::kRGB_565_Config;?? ????????default:?? ????????????return?SkBitmap::kARGB_8888_Config;?? ????}?? }?? ?? ?? ? ? ? ? ?? JNIEXPORT?jint?? JNICALL?ScreenCap_currentscreen(JNIEnv?*env,?? ????????jclass?clazz,?jstring?jpath)?{?? ?? ????ProcessState::self()->startThreadPool();?? ?? ????int32_t?displayId?=?DEFAULT_DISPLAY_ID;?? ?? ????const?char*?fn?=?env->GetStringUTFChars(jpath,NULL);?? ????LOGI("=====jpath:%s?\n",?fn);?? ?? ????if?(fn?==?NULL)?{?? ????????LOGE("=====path?=?%s?\n?=====err:?%s?\n",fn,?strerror(errno));?? ????????return?1;?? ????}?? ????int?fd?=?-1;?? ????fd?=?open(fn,?O_WRONLY?|?O_CREAT?|?O_TRUNC,?0664);?? ????LOGI("=====after?open?,fd:%d?\n",fd);?? ????if?(fd?==?-1)?{?? ????????LOGE("=====err:?%s?\n",?strerror(errno));?? ????????return?2;?? ????}?? ?? ????void?const*?mapbase?=?MAP_FAILED;?? ????ssize_t?mapsize?=?-1;?? ?? ????void?const*?base?=?0;?? ????uint32_t?w,?s,?h,?f;?? ????size_t?size?=?0;?? ?? ????ScreenshotClient?screenshot;?? ????sp?<?IBinder?>?display?=?SurfaceComposerClient::getBuiltInDisplay(displayId);?? ????if?(display?!=?NULL?&&?screenshot.update(display)?==?NO_ERROR)?{?? ????????base?=?screenshot.getPixels();?? ????????w?=?screenshot.getWidth();?? ????????h?=?screenshot.getHeight();?? ????????s?=?screenshot.getStride();?? ????????f?=?screenshot.getFormat();?? ????????size?=?screenshot.getSize();?? ????}?else?{?? ????????const?char*?fbpath?=?"/dev/graphics/fb0";?? ????????int?fb?=?open(fbpath,?O_RDONLY);?? ????????LOGI("=====read?framebuffer,?fb:%d?\n",?fb);?? ????????if?(fb?>=?0)?{?? ????????????struct?fb_var_screeninfo?vinfo;?? ????????????if?(ioctl(fb,?FBIOGET_VSCREENINFO,?&vinfo)?==?0)?{?? ????????????????uint32_t?bytespp;?? ????????????????if?(vinfoToPixelFormat(vinfo,?&bytespp,?&f)?==?NO_ERROR)?{?? ????????????????????size_t?offset?=?(vinfo.xoffset?+?vinfo.yoffset?*?vinfo.xres)?? ????????????????????????????*?bytespp;?? ????????????????????w?=?vinfo.xres;?? ????????????????????h?=?vinfo.yres;?? ????????????????????s?=?vinfo.xres;?? ????????????????????size?=?w?*?h?*?bytespp;?? ????????????????????mapsize?=?offset?+?size;?? ????????????????????mapbase?=?mmap(0,?mapsize,?PROT_READ,?MAP_PRIVATE,?fb,?0);?? ????????????????????if?(mapbase?!=?MAP_FAILED)?{?? ????????????????????????base?=?(void?const?*)?((char?const?*)?mapbase?+?offset);?? ????????????????????}?? ????????????????}?? ????????????}?? ????????????close(fb);?? ????????}else{?? ????????????LOGE("=====fb?=?%d?,?err:?%s?\n",fb,?strerror(errno));?? ????????????return?3;?? ????????}?? ????}?? ?? ????if?(base)?{?? ????????SkBitmap?b;?? ????????b.setConfig(flinger2skia(f),?w,?h,?s?*?bytesPerPixel(f));?? ????????b.setPixels((void*)?base);?? ????????SkDynamicMemoryWStream?stream;?? ????????SkImageEncoder::EncodeStream(&stream,?b,?SkImageEncoder::kPNG_Type,?? ????????????????SkImageEncoder::kDefaultQuality);?? ????????SkData*?streamData?=?stream.copyToData();?? ????????write(fd,?streamData->data(),?streamData->size());?? ????????streamData->unref();?? ????}?? ????close?(fd);?? ????if?(mapbase?!=?MAP_FAILED)?{?? ????????munmap((void?*)?mapbase,?mapsize);?? ????}?? ????return?0;?? }?? ?? ?? static?JNINativeMethod?methods[]?=?{?? ????????{"currentscreen","(Ljava/lang/String;)I",(void*)ScreenCap_currentscreen},?? };?? ?? static?int?registerNativeMethods(JNIEnv*?env,const?char*?classname,JNINativeMethod*?gMethods,int?numMethods?){?? ????jclass?clazz;?? ????clazz?=?env->FindClass(classname);?? ????if(clazz?==?NULL){?? ????????return?JNI_FALSE;?? ????}?? ????if(env->RegisterNatives(clazz,gMethods,numMethods)?<0?){?? ????????return?JNI_FALSE;?? ????}?? ????return?JNI_TRUE;?? }?? ?? ?? ?? static?int?registerNatives(JNIEnv*?env)?? {?? ??if?(!registerNativeMethods(env,?"com/android/servicescreencap/ScreenCap",?? ?????????????????methods,?sizeof(methods)?/?sizeof(methods[0])))?{?? ????return?JNI_FALSE;?? ??}?? ?? ??return?JNI_TRUE;?? }?? ?? ?? typedef?union?{?? ????JNIEnv*?env;?? ????void*?venv;?? }?UnionJNIEnvToVoid;?? ?? jint?JNI_OnLoad(JavaVM*?vm,?void*?reserved)?? {?? ????UnionJNIEnvToVoid?uenv;?? ????uenv.venv?=?NULL;?? ????jint?result?=?-1;?? ????JNIEnv*?env?=?NULL;?? ?? ?? ????if?(vm->GetEnv(&uenv.venv,?JNI_VERSION_1_6)?!=?JNI_OK)?{?? ?????????return?result;?? ????}?? ????env?=?uenv.env;?? ?? ????if?(registerNatives(env)?!=?JNI_TRUE)?{?? ?????????return?result;?? ????}?? ?? ????result?=?JNI_VERSION_1_6;?? ?? ????return?result;?? }?? ??????? 修改后的代碼入口是ScreenCap_currentscreen,該方法接收一個地址,將當前屏幕截取到該地址下。代碼中加入了日志,可以打印native層的錯誤信息。
此處需采用動態方式注冊本地方法,靜態方式好像會有問題。
參考鏈接:?
Android: How to Capture Screen in Gingerbread(2.3中實現截屏)
Android: How to Capture Screen in Gingerbread(2.3中實現截屏)(續)
Android系統截屏的實現(附代碼)
原文地址: http://blog.csdn.net/lingfengxu/article/details/43487793
總結
以上是生活随笔為你收集整理的android后台截屏实现(2)--screencap源码修改的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。