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

       在这里首先很感谢博主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初学者,有哪里错误了欢迎大家指正。。。

你可能感兴趣的:(android,jni,buffer,include,methods)