Android: How to Capture Screen in Gingerbread(2.3中实现截屏)(续)

来源:互联网 发布:与人工智能有关的股票 编辑:程序博客网 时间:2024/06/05 21:00

       在这里首先很感谢博主zmyde2010发表的这篇http://blog.csdn.net/zmyde2010/article/details/6925498#reply文章,我最开始也是看了他的文章才弄懂截屏的,而且博主基本上把全部的过程都讲出来,后来还附了代码,对于他这样无私的奉献我表示很感谢。我这篇文章也是在他的基础上修改,所以称做他的一个续集吧。有什么问题请大家及时在评论中提出来,我会尽快修复的。
       在zmyde2010那篇文章中,他是直接在c层截出图并保存在指定位置,而我这篇讲的是在c层将图输出为流的形式传到java层,然后再由java层处理,这样可以在java 层将其转化成图片或者做其他相应的操作。下面首先给出cpp代码,我采用的是动态注册本地函数的形式.

com_android_screencapture_ScreenCaptureNative.cpp

#define LOG_TAG "Screen_Capture"#include "jni.h"#include <utils/Log.h>#include <binder/IPCThreadState.h>#include <binder/ProcessState.h>#include <binder/IServiceManager.h>#include <binder/IMemory.h>#include <surfaceflinger/ISurfaceComposer.h>#include <SkImageEncoder.h>#include <SkBitmap.h>#include "CreateJavaOutputStreamAdaptor.h"using namespace android;const int kJPEG = 0;const int kPNG = 1;//format表示图片格式,目前是jpeg或者png;quality为图片质量,一般为90;outputStream表示截图对应的流输出,buffer为缓冲区,我是定为4096个bytejboolean nativeCaptureScreen(JNIEnv *env, jobject object, jint format, jint quality, jobject outputStream, jbyteArray buffer){    const String16 name("SurfaceFlinger");    sp<ISurfaceComposer> composer;    getService(name, &composer);    sp<IMemoryHeap> heap;    uint32_t w, h;    PixelFormat f;    status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);    if (err != NO_ERROR) {        LOGE("Screen shot failed");        return JNI_FALSE;    }    LOGD("screen capture success: w=%u, h=%u, pixels=%p\n",w, h, heap->getBase());    SkBitmap b;    b.setConfig(SkBitmap::kARGB_8888_Config, w, h);    b.setPixels(heap->getBase());        SkImageEncoder::Type fm;    switch (format) {    case kJPEG:        fm = SkImageEncoder::kJPEG_Type;        break;    case kPNG:        fm = SkImageEncoder::kPNG_Type;        break;    default:        LOGE("FORMAT ERROR");        return JNI_FALSE;    }        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, outputStream, buffer);    if (NULL != strm) {        SkImageEncoder* encoder = SkImageEncoder::Create(fm);        if (NULL != encoder) {            encoder->encodeStream(strm, b, quality);            delete encoder;        }        delete strm;    }        return JNI_TRUE;}static const char *classPathName = "com/android/screencapture/ScreenCaptureNative";static JNINativeMethod methods[] = {  {"nativeCaptureScreen", "(IILjava/io/OutputStream;[B)Z", (void*)nativeCaptureScreen},};/* * Register several native methods for one class. */static int registerNativeMethods(JNIEnv* env, const char* className,    JNINativeMethod* gMethods, int numMethods){    jclass clazz;    clazz = env->FindClass(className);    if (clazz == NULL) {        LOGE("Native registration unable to find class '%s'", className);        return JNI_FALSE;    }    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        LOGE("RegisterNatives failed for '%s'", className);        return JNI_FALSE;    }    return JNI_TRUE;}/* * Register native methods for all classes we know about. * * returns JNI_TRUE on success. */static int registerNatives(JNIEnv* env){  if (!registerNativeMethods(env, classPathName,                 methods, sizeof(methods) / sizeof(methods[0]))) {    return JNI_FALSE;  }  return JNI_TRUE;}/* * This is called by the VM when the shared library is first loaded. */ 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;        LOGI("JNI_OnLoad");    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {        LOGE("ERROR: GetEnv failed");        goto bail;    }    env = uenv.env;    if (registerNatives(env) != JNI_TRUE) {        LOGE("ERROR: registerNatives failed");        goto bail;    }        result = JNI_VERSION_1_4;    bail:    return result;}

下面是对应的Android.mk文件:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES := com_android_screencapture_ScreenCaptureNative.cppLOCAL_SHARED_LIBRARIES := \libnativehelper \libcutils \libutils \libbinder \libskia \    libui \    libsurfaceflinger_client \    libandroid_runtime LOCAL_C_INCLUDES += \    $(JNI_H_INCLUDE) \    external/skia/include/core \external/skia/include/effects \external/skia/include/images \external/skia/src/ports \external/skia/include/utils \frameworks/base/core/jni/android/graphics LOCAL_MODULE := libscreencapjniLOCAL_MODULE_TAGS := optionalLOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)


下面是对应的java层代码:

ScreenCaptureNative.java


package com.android.screencapture;import java.io.OutputStream;import java.io.ByteArrayOutputStream;import android.util.Log;  public class ScreenCaptureNative {  private final static int BUFFER_SIZE = 4096;private byte[] mBuffer = null;    static {          System.loadLibrary("screencapjni");      };          private native boolean nativeCaptureScreen(int format, int quality, OutputStream stream, byte[] buffer);        public ScreenCaptureNative(){    mBuffer = new byte[BUFFER_SIZE];    }              public  byte[] startCaptureScreen(int format, int quality) {      ByteArrayOutputStream srcPic = new ByteArrayOutputStream();     if (!nativeCaptureScreen(format, quality, srcPic, mBuffer)){Log.e("screencapture", "CAPTURE FAILED");return null;}    byte[] screenData = srcPic.toByteArray();        return screenData;      }   }  


上述java程序对应的make文件如下:


LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := $(call all-subdir-java-files)LOCAL_PACKAGE_NAME := ScreenCaptureLOCAL_CERTIFICATE := platformLOCAL_REQUIRED_MODULES := libscreencapjniLOCAL_JNI_SHARED_LIBRARIES := libscreencapjniinclude $(BUILD_PACKAGE)include $(call all-makefiles-under,$(LOCAL_PATH))



在这里特别强调两点:第一,在你所编写的android截屏程序中记得在对应的AndroidManifest.xml添加权限<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />,否则就会在调用c层代码时报错;第二,在编写android截屏程序对应的make文件中,记得添加LOCAL_CERTIFICATE := platform,这也是跟权限相关,然后再放到源码中编译该程序。
另外在zmyde2010的那篇文章中说到编译android自带的截屏demo,结果生成了 test-screencap,我按照博主的要求test-screencap /mnt/sdcard/scapxx.png那样没有弄出截屏,没大明白他说的终端具体指哪个。我这里说下我是怎么用的,首先将test-screencap   push到手机中,最好不要放到sd卡所在位置,你可以push到/data下面,然后手机连着电脑,在电脑命令行中输入adb shell进入手机的linux文件系统中,之后cd到刚才存放test-screencap的目录下,先修改下该文件的权限,使其可以执行,然后再输入命令 ./test-screencap /mnt/sdcard/scapxx.png即可截图了,最终在你的sd卡中就可看到刚才截的图。
我也是个android初学者,有哪里错误了欢迎大家指正。。。

原创粉丝点击