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,首先需要了解的是其系統框架。如下圖所示:
這個圖在網上比比皆是,這裡還是貼了一下,因為的確可以比較好的描述這個系統。它的最底層是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