最近由于项目需要,在学习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

第三层由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提供的View.getDrawingCache()方法。网上已经有很多的实例了。首先创建一个android project,然后进行Layout,画一个按键(res/layout/main.xml):

<? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/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实现代码为:

package com.example.helloandroid;    import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;    import android.app.Activity; import android.graphics.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) {         super .onCreate(savedInstanceState);      this .setContentView(R.layout.main);      this .button = (Button) this .findViewById(R.id.my_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 = view.getDrawingCache();          if (bitmap != null ) {            System.out.println( "bitmap got!" );            try {              FileOutputStream out = new FileOutputStream(fname);              bitmap.compress(Bitmap.CompressFormat.PNG, 100 , out);              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来操作,十分方便(其实,有一个库android-screenshot-lib应该实现了类似的功能,但是我尝试了一下没有截图成功,图片大小不正确,且是黑屏。就没有进一步尝试了)。

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系统中,采用的是rbg565的编码方式。这里编程的方法是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 int main( void ) {      printf ( "hello world!\n" );      return 0; }

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!”字符串了。


转载地址为:

http://xzpeter.org/?p=229

更多相关文章

  1. Android RSA与Java RSA加密不同标准产生问题的解决方法
  2. 新浪微博开放平台开发-android客户端(2)
  3. Android sdk manager不能更新下载缓慢的解决方法
  4. Android Cannas.drawTextView 方法坐标问题
  5. Android开机自启动程序设置及控制方法
  6. Android随笔之——Android时间、日期相关类和方法
  7. Android NFC开发(二)——Android世界里的NFC所具备的条件以及使用
  8. Kernel.org 被黑,获取 Android 源码方法一则

随机推荐

  1. Android(安卓)Graphics专题(1)--- Canvas基
  2. ActivityThread
  3. Android:开发自己的Launcher简单demo
  4. Google Android开发精华教程
  5. android是什么?
  6. Android(安卓)数据库SQLite的使用简单Dem
  7. android中Webview与javascript的交互(互相
  8. android 的handler 机制
  9. Android(安卓)音视频深入 七 学习之路的
  10. Android(安卓)中自定义属性(attr.xml,Typ