1. 程式人生 > >android截圖程式碼實現方法

android截圖程式碼實現方法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

最近由於專案需要,在學習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提供的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 <stdio.h> 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

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述