android截屏代码实现⽅法
最近由于项⽬需要,在学习系统。android是⼀个基于linux的专门针对⼿机平台的操作系统。当然,现在的android 3似乎也将进⼊平板电脑的市场。由于⾄今为⽌,⼤部分的智能⼿机采⽤的是ARM的硬件平台,因此android本⾝对ARM的平台进⾏了全⾯的⽀持,从源代码中可以看出,也在逐步加⼊对x86平台的⽀持,暂时没有看到第三个平台的⾝影。
这篇⽂章是我对android系统认识的⼀个总结,同时介绍⼀下我⾄今为⽌发现的获取屏幕图像数据的⽅法。经过⼀定的搜索,我发现⽅法有很多种,⽽实现效果会有所差别。其中,有通过最顶层的Android SDK进⾏截屏,也可以通过C直接读取framebuffer实现。由于framebuffer的⽅法⽹上虽然有很多,但是很多我认为并没有写的⼗分清楚,特别是在编译的⽅法上,没有到很好的⽂章,可能是因为⾃⼰的不够,或者android本⾝就在不断发展。对此,我在最后给出了⼀个Android Native Programming的Hello World Howto,需要先到github上mirror下来Android的源代码(2.多G),⾥边将会带所有的编译器和Emulator等⼯具。
1 android之我的粗浅理解
android的版本号很有意思,⼀个版本号对应⼀个API Level(⽐如android 2.3.3则对应Android API Level 10)。写这篇⽂章的时候,最新的android版本号为3.1,⽽API Level为12。
学习android,⾸先需要了解的是其系统框架。如下图所⽰:
android操作系统框架图
这个图在⽹上⽐⽐皆是,这⾥还是贴了⼀下,因为的确可以⽐较好的描述这个系统。它的最底层是linux的内核,只有这⼀层是⼯作在内核级的,其余三层都是⼯作在⽤户级的。这⾥,android也不是使⽤的标准的linux内核,⽽是⼀个经过了裁剪,并添加了⼀些特定功能的内核。其中⼀个让我记得的⽐较有意思的feature是,android内核中会有⼀个叫做Low Memory Killer的驱动,它的主要功能是在系统缺少内存的情况下,杀死进程。显然,我们平时的电脑中,往往不是很需要这个东西;⽽在⼿机中,由于资源⼗分有限,缺少内存的现象或许会发⽣的⾮常频繁,这个功能似乎就显得⼗分重要了(或许还有⼀个原因是⼿机没有虚拟内存?毕竟它是⽤Flash作为长期存储介质,⽽Flash 的特性似乎并不适合作为虚拟内存来使⽤)。我想这是android针对⼿机定制进⾏优化的⼀个很好的例⼦。
第⼆层就是所谓的Native层,由C/C++实现。如果熟悉ARM的应⽤程序开发的话,我理解这⼀层就是ARM平台的应⽤开发层。我们仍然可以⽤⼀个类似arm-linux-gcc的交叉编译⼯具对C程序进⾏编译(这⾥是arm-linux-androideabi-gcc)进⾏编译,具体的⽅法⼀会详细介绍。⽽在这⼀层,系统给我们提供的库可是⽐较少的,往往就是最基本的libc等(从提供的可⽤C基础库来看,数量上肯定是
android<ARM<x86/64)。随着android的发展,提供的库也在不断增多。详细的关于Native API的信
息需要参考Android NDK Dev Guide⽂档中有关Stable API的部分。这⼀层还有⼀个最核⼼的东西,就是Dalvik虚拟机。我想,android的核⼼就是这个虚拟机(或者可以说:Java的核⼼就是虚拟机?简直是废话!⽽虚拟机的核⼼是Portable,即可移植)。
第三层由Java实现,是Android SDK的核⼼,是android最上层的框架。所有的Android SDK的接⼝我想就是在这⼀层实现的了。
最上⾯就不说了,我们基于android SDK使⽤Java开发出来的东西应该都在这了。
因为我之前对通⽤ARM平台有⼀定的了解,⽽android也是⼀个ARM平台,但是⽐较特殊。让我⽤我现在的理解来对android总结⼀下的话:Android=(ARM平台)+(针对⼿机平台的定制)+(Dalvik虚拟机核⼼ & Android SDK)。
2 开发平台搭建
在android平台下,我想有三个开发内容。第⼀个,就是最好⼊门、最常见的应⽤程序开发了(也就是我们所说的基于SDK的开发),我们需要搭建⼀个SDK的环境,还需要懂得Java语⾔。如何搭建我就不说了,!另外两个,⼀个就是ative代码的开发(⽐如,⽤C写⼀个native库,通过JNI给Java调⽤,或者直接写⼀个native application),还⼀个就是⽜⼈们才能搞定的android源代码开发了(我短期恐
怕是⼊不了门了),这些⼯作都最好搞到android的source tree⽐较好(这⾥也包括了所谓的NDK,也就是Native Development Kit)。⽅法我也不说了,!
基本的实验⽅法,SDK在上⾯的developer⽹站上写的很详细,有⼀个HelloWorld的demo,⾛⼀遍肯定就知道了。⼀会我会给出⼀个利⽤NDK开发的helloworld。
3 截屏⽅法种种
是的,让我们开始截屏吧!这⾥我截屏的环境都是在Android Emulator中(这个基于qemu的模拟器在Android SDK和NDK中都有提供)3.1 基于Android SDK的截屏⽅法
主要就是利⽤SDK提供的DrawingCache()⽅法。⽹上已经有很多的实例了。⾸先创建⼀个android project,然后进⾏Layout,画⼀个按键(res/l):
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android=""
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:text="NiceButton"
android:id="@+id/my_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"></Button>
</LinearLayout>
HelloAndroid.java实现代码为:
张翰千眼图
ample.helloandroid;
import java.io.FileOutputStream;
SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.app.Activity;
aphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class HelloAndroid extends Activity {
private Button button;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
this.setContentView(R.layout.main);
this.button = (Button) this.findViewById(_button);
this.button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd_HH-mm-ss", Locale.US);
String fname = "/sdcard/"+ sdf.format(new Date()) + ".png";
View view = v.getRootView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = DrawingCache();
if(bitmap != null) {
System.out.println("bitmap got!");
try{
FileOutputStream out = new FileOutputStream(fname);
bitmappress(Bitmap.CompressFormat.PNG, 100, out);
word打不开怎么回事
System.out.println("file "+ fname + "output done.");
} catch(Exception e) {过桥米线
e.printStackTrace();
}
} else{
System.out.println("bitmap is NULL!");
}
}
});
}
}
这个代码会在按下app中按键的时候⾃动在⼿机的/sdcard/⽬录下⽣成⼀个时间戳命名的png截屏⽂件。
这种截屏有⼀个问题,就是只能截到⼀部分,⽐如电池指⽰部分就截不出来了。
3.2 基于Android ddmlib进⾏截屏
这种⽅法。我也没有测试过,此处略过。
3.3 Android本地编程(Native Programming)读取framebuffer
这是我现在使⽤的⽅法。它的优点是整个屏幕都可以截下来,同时不需要写JNI,也不需要Java层的实现。⽽且如果是emulator的话,也可以直接⽤adb来操作,⼗分⽅便(其实,有⼀个库应该实现了
类似的功能,但是我尝试了⼀下没有截图成功,图⽚⼤⼩不正确,且是⿊屏。就没有进⼀步尝试了)。
3.3.1 Android的framebuffer介绍
framebuffer是linux内核对显⽰的最底层驱动。在⼀般的linux⽂件系统中,通过/dev/fb0设备⽂件来提供给应⽤程序对framebuffer进⾏读写的访问。这⾥,如果有多个显⽰设备,就将依次出现fb1,fb2,…等⽂件。⽽在我们所说的android系统中,这个设备⽂件被放在
了/dev/graphics/fb0,⽽且往往只有这⼀个。
中国娱乐3.3.2 framebuffer的读取
读取的⽅法很简单,就将/dev/graphics/fb0当作⼀般的⽂件读取即可。可以通过ioctl()⽅法获取图像的长宽,以及每⼀个pixel对应的数据量。在android系统中,采⽤的是的编码⽅式。这⾥编程的⽅法是C最基本的,难点主要是编译器的配置。我会在第4节介绍⼀个Native的tutorial: hello world程序,这⾥会讲到编译的⽅法和具体配置。
3.3.3 在android emulator中利⽤命令直接进⾏截屏
有⼀个⼤致的流程。主要是⽤cat读取fb,后⽤ffmpeg转换编码的⽅法。此处略。
4 Android本地编程⼊门:”hello world!” again!
我简单介绍⼀下怎么在android上开发基本的C程序。如果做过ARM的C应⽤程序开发的话会发现,ARM⼀般情况下提供了⼗分完备的编译器,⽽android没有⽽已(android提供了完善的Java层开发⼯具,C的却不是那么完善)。
4.1 编写hello.c
这个太简单了,不是么?
#include <stdio.h>
int main(void)
{
printf("hello world!\n");海信e930
return0;
}
4.2 编写Android的编译器配置⽂件make_android
在Android SDK中,并没有提供Android系统的C编译器。就算是在NDK中,也只是提供了ndk-build⼯具,⽤来编译native
static/dynamic library。只有仔细翻阅NDK的⼿册(它的⼿册位于NDK根⽬录的doc/OVERVIEW.html,⽐较简略),才会发现有⼀个STANDALONE-TOOLCHAIN的页⾯,会提到单独编译C Level应⽤程序的⽅法。我这⾥提供⼀段简单的makefile,命名⽂件为
make_android,⽤来配置CC宏:
# make_android: this is a sub makefile for android native compile
# you have to set ANDROID_VER and ANDROID_ROOT to your flavor to work
### these two things have to be set first
ANDROID_VER=android-8
ANDROID_ROOT=/home/xzpeter/android
PLATFORM_DIR=${ANDROID_ROOT}/prebuilt/ndk/android-ndk-r4/platforms
SYSROOT=${PLATFORM_DIR}/${ANDROID_VER}/arch-arm
EABI_GCC=${ANDROID_ROOT}/prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-gcc
CC=${EABI_GCC} --sysroot=${SYSROOT}
这⾥,ANDROID_ROOT和ANDROID_VER是需要针对⾃⼰的android source⽬录地址和android API level修改⼀下的。这⾥的android source是我⽤repo sync从github上mirror下来的android源代码。
4.3 编写Makefile
利⽤上⾯的make_android,写Makefile:
# to make x86 version of code, run: "make X86=1"
ifdef X86
CC=gcc
CLFAGS=-g
else
include make_android
endif
default: hello
hello: hello.o
康纳 肯尼迪clean:
rm hello *.o
我们提供了X86和android两种编译⽅式,默认是android⽅式。
4.4 编译
可以⽤make X86=1先在本地编译⼀下,并运⾏./hello试试看。如果想编译android版本,先make clean⼀下,然后直接make就可以了。
4.5 在模拟器中运⾏
利⽤shell命令启动emulator并将⽂件放到⽬标模拟器上去:
emulator -avd my_avd # my_avd is my config name of avd
# wait for some time to boot up
adb push ./hello /data/hello
adb shell chmod 0755/data/hello
adb shell ./data/hello
应该可以看到返回的”hello world!”字符串了。
转载地址为: