Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« December 2010 | (回到Blog入口) | February 2011 »

January 2011 歸檔

January 3, 2011

Android 軟體開發的思惟與建議

(原文刊載於零組件雜誌,2010年12月)

2011年是Android揮軍平板電腦的重要一年。

AP+OS+APP主導產品開發,AP(Application Processor)應用處理器是產品主板(PCBA)的靈魂,主要的功能是用來執行作業系統。OS(Operating System)的重要性在於它提供應用軟體的執行環境,並負責驅動主板上的所有硬體。APP(Applications)則是基本作業系統所撰寫的應用程式。

作業系統有近二十年來,不斷蓬勃發展,並採取社群模式開發的Linux kernel。應用程式部份,有Google提供的AOSP(Android Open Source Project)以及Android API標準。應用程式開發者基於標準API撰寫各式有創意的應用程式,產品開發商或硬體製造商,可基於AOSP的架構以及API標準發展產品。對產品開發商的優點是,基於AOSP架構與API標準所發展的產品,可搭載「現有的 Android 應用程式」。意思是,Android Market或第三方來源的各種Android應用軟體,「很早就為我們的產品準備好了」。

因此,AP+OS+APP的產品公式,可等價於AP+Android Application Framework+Developers。這讓產品開發的思惟很不同,但也可以很傳統,取決定產品本身的定義。以下是幾點「很不同」的想法,提供大家參考指教。

第一、Android Application Framework的開發強調「相容性」。這個相容性並不是傳統上的「硬體相容」或是「舊版本軟體相容」,而是「API相容」。如同上述所提,當一個 Android框架無法開發到API相容時,「很可能多數的現成軟體都無法正常執行」。開發Android產品不是只為了硬體,而是要支援網路上「現成的各種軟體」。消費者可能無法接受一個API不相容的Android產品。大部份的應用開發者都基於標準Android SDK做開發,此時,API不相容的產品,會讓這些應用軟體無法執行。因應這個問題,Google提出了CTS套件,希望廠商開發的 Android 框架與產品都可以通過CTS(Compatible Test Suite)測試。

第二、開發軟體是「設計導向思惟」。寫程式(Coding)並不等於做軟體(Software),寫code可以很straight forward,意思是,大家可以通往直前,不受任何限制地自由發揮,程式碼怎麼寫,很自由心證。但是做軟體就很不同了。以Android框架的開發為例,寫code要考慮架構,要先做設計(OOD),要驗證設計的正確性,同時也要達到重用(Design Reuse)框架設計的要求;所以開發Android框架,是在一套系統化且制式的規模下進行,寫code受到規範。過去硬體商寫code是為了驅動硬體,或驗證硬體,現在要擔綱軟體開發的工作,coding的思惟就要改變。

第三、這是開放平台。開放平台(Open Platform)與開源軟體(Free and Open Source Software)是二個概念。開放平台代表開放API給開發者使用,或是開放Platform Builder供製造商使用,製造商很可能無法取得內部的實作源碼(Implementation source),取而代之的是一個configurable的環境。意思是說,AOSP版本的程式碼很可能永遠都是reference code,廠商自已的implementation也不會公開源碼。Android裡的Launcher是reference Launcher,Android裡的rild也只是reference code;大部份implementation是reference implementation,不是workable或useable code。所以,不能只顧著等候AOSP的釋出,也不能渴望著取得所有的源碼。強化開發能力,動手發展AOSP成為好用的自有版本,才是務實之道。

2011年是Android揮軍平板電腦的重要一年, 要知道製造商在Android的研發儲備能量,這將是重要的觀察指標。

Android 2.3 的更新:SensorService 的「Native 化」

近期在進行 Android 2.3 的新框架程式碼研究,Android 2.3 在 Platform (Framework) 部份包含了許多重大的更新,其中一個部份就是 SensorService 改寫成 Native Service 形式。在 Android 2.2 以前的框架,SensorService 包含在 SystemServer 裡,實務上,可能也會對 SensorService 做小幅度改寫,以增進效能,或是將 SensorService 獨立成為一個 process。

在 Android 2.3 裡的 SystemServer 已經找不到 SensorService 了,這個重要的 Android Service 被改寫成 Native Service。「如何將 Android Service 改寫為 Native Service」,以及「Native Service」的開發,從 Android 2.3 開始,將成為重量級主題。由於本週即將進行「Android HAL & Framework: 軟硬整合實作訓練」課程,利用元旦假期,也順利完成課程以及教材的更新,將開始著重 Native Service 的講解,並透過實例解說 Native Service 的開發。

由於 Android 2.2/2.3 可能是併行的關係,而非取代關係。因此,Android 2.2 以及 Android 2.3 的學習必要性很高;意思是,最好能由 2.1/2.2 的框架開發開始學習。了解 Android 2.1/2.2 的 SensorService 架構,再對 Android 2.3 的 SensorService 進行了解,除了可比較其設計與實作差異外,也能知道「效能改進之道」。了解過去 SensorService 架構與實作上的不足,以及 Android 2.3 的改寫,解決了什麼問題。

Android 2.3 的 libhardware 沒有太大變動。從 Anroid 2.1/2.2 開始的開發者,可以由 Android 2.3 的 SensorService 做為「更新知識」的進入點。

January 4, 2011

Dalvik VM 與 JVM 差異比較:Zygote 與 Class Preload

Android 2.3 框架源碼釋出後,開始有必要對 Dalvik VM 做深入的研究。建議可以由 Dalvik VM 與 Java VM 的差異起步。Dalvik VM 與 Java VM 雖然字面上都是「Java 虛擬機」,但內部設計並不完全相同,存在一些差異。最中最重要的差異,就是 Dalvik VM 加入了 Zygote 的設計。

在過去許多的演講場合,不斷提到 Zygote 的觀念,在 [Jollen 的 Android 觀念解析, #1: Zygote Mode] 日記裡也曾簡要提及。Zygote 負責幾項重要的工作:

1. Listening Socket (Forking child process)
2. Preload Resource
3. Preload Class
4. Start System Server
5. Enter Zygote Fork Mode

與 JVM 最大的不同是,Dalvik VM 透過 Zygote 進行「Class Preloading」。意思是,把絕大部份的「Java class file」載入記憶體。Java class file 被打包成 *.jar 檔,Java class file 就是 Java library,提供 Android 應用程式與框架所需的 API。Zygote 所載入的 class file,幾乎包含所有的 API,當然,大部份自已都用不到,因此稱之為「preload」,也就是「預載」。透過 preload,讓 Android 應用程式在載入時,不需要重覆「class loading」的動作,除了加快應用程式啟動速度外,也達到許多效果。

Class preloading 是 Dalvik VM 最重要的特色之一,也是與 JVM 不同之處。Class preloading 是在 Android 裝置開機時進行,可能產生的不良效應之一就是「開機變慢」,不過,已經有一些方法可以解決這個問題。在了解 Dalvik VM 與 Zygote 後,便能開始深入 Dalvik VM 的 heap 原理。

January 10, 2011

Jollen 的 Android Leak 開發解密, #1: Dont' Play Music in UI Thread

設計音樂撥放器應用程式時,如何正確地進行「Play Music」的動作?在按下 Button 後立即進行音樂撥放,是可行 (work) 的做法,但卻不建議這樣設計。在類似 onButtonPressed() 的 event callback 裡直接撥音樂會有「UI 成本」產生,意思是可能對 UI 反應造成不利影響。

設計一個簡單的 Music Player 界面如下:

musicplayer-v1-ui.png
圖一:Music Player

以 Android SDK 2.3 為例,我們可使用 android.view.View.OnClickListener 以及 android.media.MediaPlayer 設計出「不建議」的音樂撥放器。

musicplayer-v1-ood.gif
圖二:Bad Design: Music Player v1

以下以 pseudo code 呈現圖二的實作:

public class MusicPlayerUI extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        Button playBtn = (Button) findViewById(R.id.playBtn);
        Button stopBtn = (Button) findViewById(R.id.playBtn);
 
        playBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
    }
 
	public void onClick(View v) {
		Access Audio Hardware and get it ready.
		Control Audio Hardware and start music.
	}
}

以 MediaPlayer 來實作音樂撥放功能,完整程式碼片斷如下:

public class MusicPlayerUI extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        Button playBtn = (Button) findViewById(R.id.playBtn);
        Button stopBtn = (Button) findViewById(R.id.playBtn);
 
        playBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
    }
 
	public void onClick(View v) {
		// TODO Auto-generated method stub
		MediaPlayer p = MediaPlayer.create(this, R.raw.test);
		p.start();
	}
}

在了解 Android Process 模式後會發現,在 UI thread 裡做撥音樂的動作,會對 UI 反應有不利影響。以 MediaPlayer.create() 為例,在實例化 MediaPlayer 時,會去讀取音樂檔,並預先將檔案準備好,這個動作由 .prepare() 完成。由於 .prepare() 是同步式的實作,所以在 UI thread 裡不適合進行這項操作。

一般來說,我們不會在 UI thread 裡做控制(control)或硬體存取(hardware access)的操作。我們將利用以下二種方式來設計 Music Player:

1. 使用 secondary thread 執行「撥」的動作
2. 使用 separated process 執行「撥」的動作

Secondary thread 指的是由應用程式本身建立的 thread;由系統自動建立的 thread 稱為 main thread。在設計 iPhone 應用程式,或是撰寫 C# 時,也經常使用到 main thread 與 secondary thread 的觀念。Don't play music in UI thread 可說是通識觀念。Android Leak #2 將說明 how to do。

Revision history

* 2011.1.11: 加入一段 pseudo code 輔助說明。根據網友 allstars 的意見,加強 MediaPlayer 例子說明,之前寫得太簡單,造成困擾了,sorry!

Android Leak 開發解密系列,整理自 Jollen 過去在 Android 領域的經驗,以「Do & Don't Do」形式分享一些重要但常被略的開發觀念。轉載請全文引用,並註明出處 (Jollen's Blog, http://www.jollen.org/blog),謝謝您對數位內容著作權的支持。

January 11, 2011

Jollen 的 Android Leak 開發解密, #2: Play Music in Secondary Thread

根據先前所討論過的觀念,我們把 "play" music 的功能設計在 secondary thread 裡。Android SDK 使用 java.lang.Thread 來建立 thread,使用 secondary thread 來產生 MediaPlayer 的 instance,並撥放音樂。Music Player v2 設計圖如下:

musicplayer-v2-threading.png
圖一:Music Player v2 設計

針對 Music Player 個案,因為 MediaPlayer.create() 是以同步 (Synchronous) 方式準備 MediaPlayer,所以有出現 ANR 的機會。在早期 (2008 年) 的 MediaPlayer 設計中,因為 .create() 的實作上有點問題,遇到比較大的音樂檔時,.create() 花費的時間可能較久,形成一個 long operation。雖然後續版本已經改善這個問題,不過如果我們的資料來源不是檔案 (file) 的話,對 UI 就會有很不好的影響。

因此,採用 secondary thread 的方式來把 Music Player 設計的更好,避免可能產生的問題。這個範例衍生的議題是關於 association 的設計。假設我們想在「撥完音樂後」回報 MusicPlayerUI,我們可以讓 secondary thread 以 callback 方式回報 MusicPlayerUI,不過這不是一個好設計。

Music Player v2 的設計,MusicPlayerUI 成為一個 navigable object,意思是,實作上,我們可能會採取以下的實作(pseudo code):

Thread thr = new Thread(this);

因為不理想,所以暫時不進行程式碼實作。現在,我們已經將撥音樂的動作放到 secondary thread 裡了,接下來我們會想重構 association 的設計。

Android Leak 開發解密系列,整理自 Jollen 過去在 Android 領域的經驗,以「Do & Don't Do」形式分享一些重要但常被略的開發觀念。轉載請全文引用,並註明出處 (Jollen's Blog, http://www.jollen.org/blog),謝謝您對數位內容著作權的支持。

關於 January 2011

此頁面包含了在January 2011發表於Jollen's Blog的所有日記,它們從老到新列出。

前一個存檔 December 2010

後一個存檔 February 2011

更多信息可在 主索引 頁和 歸檔 頁看到。

Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org