最近由于项目需要,在学习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之MediaPlayer的简单介绍之播放音乐
  2. Android中如何修改编译的资源ID值(默认值是0x7F...可以随意改成0
  3. Android(安卓)Service的使用方法 音乐播放器实例
  4. 关于Scroller ,scrollTo,scrollBy
  5. Android(安卓)RSA与Java RSA加密不同标准产生问题的解决方法
  6. Android多线程AsyncTask详解
  7. Android(安卓)P 调用隐藏API限制原理
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Ubuntu下搭建Android(安卓)NDK开发环境
  2. android设置Activity背景透明
  3. Android(安卓)启动过程分析 (二)
  4. 如何让android apk 获得系统权限
  5. Android(安卓)SearchView详细使用
  6. android开发每日汇总【2011-10-27】
  7. 【Android学习入门】Android中activity的
  8. 【收藏】android WebView总结
  9. Android:ImageView如何显示网络图片
  10. Android20_广播机制(一)