您的位置:首页 > 移动开发 > Android开发

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

2011-12-14 23:39 323 查看
在这里首先很感谢博主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个byte
jboolean 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.cpp

LOCAL_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 := libscreencapjni
LOCAL_MODULE_TAGS := optional

LOCAL_PRELINK_MODULE := false
include $(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 := optional

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := ScreenCapture
LOCAL_CERTIFICATE := platform

LOCAL_REQUIRED_MODULES := libscreencapjni
LOCAL_JNI_SHARED_LIBRARIES := libscreencapjni
include $(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初学者,有哪里错误了欢迎大家指正。。。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: