1. 程式人生 > >Android手機開發總結

Android手機開發總結

導讀:對於Android開發者來說,成系列的技術文章對他們的技術成長幫助最大。如下是我們向您強烈推薦的主題為Android開發的第一個系列文章。

《Android核心分析》整理如下:

1. 方法論探討之設計意圖

為什麼要研究Android,是因為它夠龐大,它夠複雜,他激起了我作為一個程式設計師的內心的渴望,渴望理解這種複雜性。我研究的物件是作為手機開發平臺的Android軟體系統部分,而不是Dalvik虛擬機器本身。

作為一個從其他平臺裝接過來的程式設計師,要從事Andoid平臺系統開發,我的關於手機平臺上積累的知識已經不能滿足需要了,Android為我們帶來了大量的新名詞,Activity,Manifest,INTENT,Service,Binder,Dalvik虛擬機器,Framework,Linux,Navtive ,JNI.....。通過在原始碼,在開發社群,在開發部落格,甚至在招聘過程中,我不斷的尋求Android是什麼。經過一定時間的沉澱,我慢慢的理解到Android不僅僅是一類手機的總稱,不僅僅是一個手機開發平臺,不僅僅是一個虛擬java作業系統,不僅僅是一個開發社群,一個開發標準,不僅僅是一堆程式碼,Android已經成了一個新的潮流。

程式碼多,系統複雜,縱觀社群中Android的研究者,一開始從原始碼分析Android就走向迷途,不斷的跋山涉水,向縱深衝刺,最終腦袋堆疊不夠用,迷失在開始的旅程,或者掛在半途中,鮮有通達者。我感覺到大部分的研究者總是忘記站在高山上向下望一望設計者的意圖,一味的隨著程式碼的控制流走入繁雜的謎團,陷入到複雜性的深淵。

我的研究分析是從設計者的意圖出發,從抽象的甚至從哲學的高度,從最簡單的系統原型開始,從設計猜想開始,而不是一開始就從程式碼分析展開。首先理解Android大的執行框架,主幹流程,系統原型,之後再用原始碼分析充實之。當然我這裡的設計者意圖並不是真正的Android設計者意圖,而是我以為的Android設計者意圖。

要理解設計者意圖,就需要抽象。我們需要在哲學意義空間中去考慮系統的描述,即系統在本質上要表達什麼。在邏輯空間上去考慮系統基本構成和動態結構。從現實到虛擬物件的對映去理解系統物件的組成,在從資料流的角度分析資料的產生者和消費者之間作用關係,從控制流的角度去分析物件之間的互動關係,從函式呼叫去分析具體的層次關係。

在系統設計上,原型是最能表達哲學空間和邏輯空間中系統本質的東西,原型是事物本質的第一層體現。我以為任何複雜的系統都一個簡潔的系統原型,都有它簡潔的意義。系統原型是設計者意圖的第一體現,所以我們需要從幾個方向上去提煉系統原型:

(1)從系統本質和基本原理出發

(2)從分析系統資料流和控制流分析出發。

從設計者意圖出發,得出系統原型,提取到大的邏輯結構和系統構成是第一步。之後我們可以從設計者的角度考慮系統猜想系統設計,為什麼要這樣設計,為什麼要有這些構成。這樣的基本原型是什麼?系統的限制是什麼,應用場景有哪些,有些設計的引進還是系統收斂性而為之呢。我們還可以從程式碼痕跡上去分析,這些概念是如何的得來的?從一定的抽象和高度去理解這些問題,遵循系統原型出發之原則,在深入分析程式碼的時候,就不容易陷入細節中。我們就可以隨時跳出來想,這些程式碼在整體上載表達一個什麼概念,在描繪一個什麼邏輯,他要構成一個虛擬層嗎?他是在管理這個硬體嗎?他在 虛擬這個物件嗎?他在構建管理機構?還是在構建一個物件管理?空間管理,為了快速引入了什麼樣的複雜演算法,實際上的原型演算法應該是什麼樣的?

只有深入到這個抽象層次,我們才能很好的把握住系統的每一條線,每一個物件的意義。只用從原型出發,我們才能把握住這個系統的實質所在,在幹什麼?他要表達什麼?設計者為什麼要這樣想?最終極的想法是什麼?這樣,程式碼分析就變得簡單明瞭,讀程式碼就變成了是在印證猜想,修正方向。

2. 方法論探討之概念空間篇

我們潛意識就不想用計算機的方式來思考問題,我們有自己的思維描述方式,越是接近我們思維描述方式,我們越容易接受和使用。各種計算機語言,建模工具,不外乎就是建立一個更接近人的思維方式的概念空間,再使用工具從該概念空間向另外一個概念空間對映,我稱之為人性思維空間向01序列描述空間的一個對映。實現方面來看,系統就是一個翻譯器,將機器性更加人性化的一種機制。大學計算機經典課“計算機體系結構”,其他的可以忘記,但是下面這個圖不能忘記:

這個就是概念空間最本質的原型體現:作為觀測者看到了什麼?設計者給了觀察者什麼?給出的答案是外部特性。

(1)提供給觀察者的概念空間是什麼?

(2)內部特性的概念空間是什麼?

概念空間所表達的東西帶有兩個方面的纏繞:一面是人性自由,一面是物性制約(實時響應,系統資源的限制)。所以程式實現的概念空間是人性自由與特定計算機系統物性之間有一個折中,並且根據實際系統而採取某種動態的平衡。而這種平衡將會影響到系統架構,以及設計的思想。特別在手機這樣的嵌入式系統中,這種矛盾和平衡無處不在,這種折中無處不在。而對系統的選取和採用,也就接受了某個方面的折中或某中即在的,也許是看不見的標準,及這樣的標準有隱式和顯式的。正因為如此,不管是工具的產生,新的平臺的產生, 都是計算機的物性向人性靠近的一個小臺階。一個新的思想的形成隨即帶來的新工具,新系統框架,新的體系結構。

如果設計者站的高度足夠高,那麼設計者一開始就會考慮到“我該給他們一個什麼樣的概念空間,甚至一個什麼樣的理念,讓他們這個概念空間去建立自己的產品”,於是設計者就會開始主動的去建立概念空間,這個概念空間要表達的實際意義,概念空間應該有哪些內容構成,考慮概念空間的完備性和封閉性,考慮概念空間的邊界,考慮從哪個基礎上建立這個概念空間,考慮如何與概念空間外的實體進行互動,考慮系統的資源限制條件,考慮功能性構建的合理性,考慮機器系統與人的平衡問題。

我們在學習新系統時,首先映入眼簾的就是新概念。新名詞,就如現在我們面臨的Android大量的新名詞,在程式設計師的世界都是從程式碼實踐開始的,是從寫應用開始去涉及。SDK給了我們一個概念,我們就在這個概念框架下,使用SDK給我提供的函式介面,資料結構,初始化過程等,我們最初的接觸到原型就是“HelloWorld”之類的DEMO程式,我們在Hello world上去使用各種不同的介面函式,對於應用程式設計師來講,他說看到的系統就是系統呼叫介面,及其程式設計開發流程。實際上只要一使用這些介面,就不得不接受一系列的概念,只有在這種概念系統下,我們才能工作。但是,實際上我們卻忽略了這樣的概念系統的理解,只是在程式設計介面的這個狹窄的空間去理解系統.我們理解系統在形成理解概念的空間只是微小的一角,很少有資料來介紹這種概念系統的形成和理解,程式設計介面只是這個概念空間一個,對外部的一個表徵。我們可以抽象起來,以介面,協議和行為,來描述系統的情況。SDK API的實質向上層提供了一個語義介面,從而在層間實現了一個轉義過程,同時又成為一個功能的集合體。但是我們很少這樣跳出來看,我們到底是處於一種什麼樣的概念空間,SDK除了呼叫介面外,還給了我們怎樣一種整體概念?目標系統的基本構架在本質上的東西就是一個概念系統到另一個概念系統的對映。讓我們大腦理解的概念系統對映到計算機能實現的概念域的一個對映。我們假定這個概念域E,機器能夠理解的概念域為M,我們的軟體工程要做的事情實質就是:EàM領域的一個對映過程。

為什麼要在巨集觀上把握這些概念呢,顯然有我的目的,理解概念空間是理解設計者意圖的一個重要途徑。設計者要想給開發者提供什麼,設計者想要提供給終端使用者什麼。我們需要站在高處看待系統明白設計者意圖。

Android的實質還是一套管理手機硬體系統的軟體,這個話講起來沒有多大意義,計算機作業系統本質都是如此,Android是Google雲端計算計劃的一部分,我們修正成:Android建立的本質就是讓計算機成為我的雲接入移動智慧終端。作為硬體管理軟體,Android提供概念空間內涵實質上泛作業系統內涵,我們的理解可以從泛作業系統概念空間對映到Android系統中去。而作為雲端計算的一部分的內容,我們可以雲端計算的概念入手去研究Andoird。

3. 手機之硬體形態

本節可能與Android無關,但是Android系統現在這個階段更多的是移動終端形態的開發平臺,本節給出了Android背後的工作-Android管理的硬體是什麼,Android的本質就是要管理好這些硬體部分,為使用者提供一個體驗更好,速度更快的智慧移動終端。對手機硬體形態的認識是要讓我們對手機硬體組成有個感性的認識,讓程式設計師知道系統中的程式碼是管理那一部分的,即我們堆磚頭的目的是什麼,讓思維有一個伸展。

為了對手機這類嵌入式系統有一個較為深入的瞭解,我製作瞭如下的手機硬體結構思維導圖,在這張圖上我們可以看到組成手機硬體的有哪些,初步瞭解到手機管理平臺為什麼要那麼多的管理框架和層次,從最底層理解Android設計者的設計意圖,這個思維導圖其實只是示意圖。

我們知道手機這種嵌入式系統,硬體架構最簡單描述的描述為:

應用處理器+Modem+射頻

對於應用處理器而言,對設計者最為本質的描述為輸入輸出,而對於移動終端裝置電源管理,連線機制,多媒體又是很重要的考慮環節,而這些環節都會在軟體平臺上有所體現。

4. 手機的軟體形態

上節我給出了手機的硬體樹,本節將給出手機軟體形態樹。主要突出手機軟體涵蓋的內容。通過該思維導圖,我們可以看到手機軟體所涉及到的方方面面,Android所涉及到的內容也不會超過下面所示太多,這個也是Andoid系統外特性空間所要展示的,這個也是Android設計者需要考慮管理的大部分內容,通過下面的整理,我們可以讓我們的思維更加貼近Android設計意圖,從而更深入的瞭解Android中各種組成的由來,這個就是前面講到的分析思想之一從退到源頭出發,從思考最終極的問題開始。

5. Android基本空間劃分

Google給了我們一張系統架構圖,在這張圖上我們可以看到Android的大體框架組成。

從上圖可以看到:Android Applications,Application Framework,Dalvik Virtual Machine,Linux。如果將Android泛化,我們可以將系統劃分成兩部分:

但是為了研究的方便我們先看最為本質的三層,上面是Android,中間叫Dalvik虛擬機器,下面叫Linux。

雖然上兩層都包含在Android中,但是為了理解的方便或者從實用主義出發,我還是將虛擬機器這次給分開出來,因為我研究的物件是Android的手機系統相關部分,對於虛擬機器我們不做太深入的研究。

e: pre;"> 從上面我們可以看到這個系統靜態的劃分成這樣的三層。但是從動態執行邏輯上不是這樣劃分的,所以空間的劃分是一個有趣的概念。我們從作業系統的角度看,Android就是一堆Linux應用的集合。從Linux角度看到的空間劃分:程序空間和核心空間。從Android的應用對應著Linux的一個個程序。

Andoid中包含一個Java虛擬機器,虛擬機器是執行在Linux之上的,Android構建在JVM之上,從Android動態執行邏輯上我們需要將Android劃分成Android空間和非Android空間。在Andoid系統中我們面對的是Andoid概念空間,而不是Linux程序了,在Andoid概念空間中已經沒有了Lliux程序的概念,而是Service,proxy,Activity,provider等。

至於虛擬機器JVM,我們只需要知道JVM是Dalvik VM(虛擬機器)這是一個專為嵌入式裝置打造的JAVA虛擬機器,是一個有著自己的code-byte和格式的可以在嵌入式裝置上高效執行的Java虛擬機器。

為了研究的深入,我們還是需要涉及到JNI Native部分。在這個分類中我將JVM分為JVM空間和C++空間。

Android應用的開發者是工作在Android外特性概念空間的,這裡沒有了Linux的一點氣息,Android構建的外特性空間概念包含了:Activity,Provider,Interface,Events,Provider,Service等。至於JVM空間和C++空間的劃分是為了研究Android核心的描述而提出的,我們在做Android系統開發時,常常需要修改到JNI的Native部分。後面我將用較多的篇幅來深入闡述這個部分。

6. IPC框架分析(Binder,Service,Service manager)

我首先從巨集觀的角度觀察Binder,Service,Service Manager,並闡述各自的概念。從Linux的概念空間中,Android的設計Activity託管在不同的的程序,Service也都是託管在不同的程序,不同程序間的Activity,Service之間要交換資料屬於IPC。Binder就是為了Activity通訊而設計的一個輕量級的IPC框架。

在程式碼分析中,我發現Android中只是把Binder理解成程序間通訊的實現,有點狹隘,而是應該站在公共物件請求代理這個高度來理解Binder,Service的概念,這樣我們就會看到不一樣的格局,從這個高度來理解設計意圖,我們才會對Android中的一些天才想法感到驚奇。從Android的外特性概念空間中,我們看不到程序的概念,而是Activity,Service,AIDL,INTENT。一般的如果我作為設計者,在我們的根深蒂固的想法中,這些都是如下的C/S架構,客戶端和服務端直接通過Binder互動資料,開啟Binder寫入資料,通過Binder讀取資料,通訊就可以完成了。

 

該注意到Android的概念中,Binder是一個很低層的概念,上面一層根本都看不到Binder,而是Activity跟一個Service的物件直接通過方法呼叫,獲取服務。

這個就是Android提供給我們的外特性:在Android中,要完成某個操作,所需要做的就是請求某個有能力的服務物件去完成動作,而無需知道這個通訊是怎樣工作的,以及服務在哪裡。所以Andoid的IPC在本質上屬於物件請求代理架構,Android的設計者用CORBA的概念將自己包裝了一下,實現了一個微型的輕量級CORBA架構,這就是Andoid的IPC設計的意圖所在,它並不是僅僅解決通訊,而是給出了一個架構,一種設計理念,這就是Android的閃光的地方。Android的Binder更多考慮了資料交換的便捷,並且只是解決本機的程序間的通訊,所以不像CORBA那樣複雜,所以叫做輕量級。

所以要理解Android的IPC架構,就需要了解CORBA的架構。而CORBA的架構在本質上可以使用下面圖來表示:

在服務端,多了一個代理器,更為抽象一點我們可以下圖來表示。

分析和CORBA的大體理論架構,我給出下面的Android的物件代理結構。

在結構圖中,我們可以較為清楚的把握Android的IPC包含了如下的概念:

裝置上下文什(ContextObject)

裝置上下文包含關於客服端,環境或者請求中沒有作為引數傳遞個操作的上下文資訊,應用程式開發者用ContextObject介面上定義的操作來建立和操作上下文。

Android代理:這個是指代理物件

Binder Linux核心提供的Binder通訊機制

Android的外特性空間是不需要知道服務在那裡,只要通過代理物件完成請求,但是我們要探究Android是如何實現這個架構,首先要問的是在Client端要完成雲服務端的通訊,首先應該知道服務在哪裡?我們首先來看看Service Manger管理了那些資料。Service Manager提供了add service,check service兩個重要的方法,並且維護了一個服務列表記錄登記的服務名稱和控制代碼。

Service manager service使用0來標識自己。並且在初始化的時候,通過binder裝置使用BINDER_SET_CONTEXT_MGR ioctl將自己變成了CONTEXT_MGR。Svclist中儲存了服務的名字和Handle,這個Handle作為Client端發起請求時的目標地址。服務通過add_service方法將自己的名字和Binder標識handle登記在svclist中。而服務請求者,通過check_service方法,通過服務名字在service list中獲取到service 相關聯的Binder的標識handle,通過這個Handle作為請求包的目標地址發起請求。

我們理解了Service Manager的工作就是登記功能,現在再回到IPC上,客服端如何建立連線的?我們首先回到通訊的本質:IPC。從一般的概念來講,Android設計者在Linux核心中設計了一個叫做Binder的裝置檔案,專門用來進行Android的資料交換。所有從資料流來看Java物件從Java的VM空間進入到C++空間進行了一次轉換,並利用C++空間的函式將轉換過的物件通過driver/binder裝置傳遞到服務程序,從而完成程序間的IPC。這個過程可以用下圖來表示。

這裡資料流有幾層轉換過程。

(1) 從JVM空間傳到c++空間,這個是靠JNI使用ENV來完成物件的對映過程。

(2) 從c++空間傳入核心Binder裝置,使用ProcessState類完成工作。

(3) Service從核心中Binder裝置讀取資料。

Android設計者需要利用面嚮物件的技術設計一個框架來遮蔽掉這個過程。要讓上層概念空間中沒有這些細節。Android設計者是怎樣做的呢?我們通過c++空間程式碼分析,看到有如下空間概念包裝([email protected](ProcessState.cpp)

在ProcessState類中包含了通訊細節,利用open_binder開啟Linux裝置dev/binder,通過ioctrl建立的基本的通訊框架。利用上層傳遞下來的servicehandle來確定請求傳送到那個Service。通過分析我終於明白了Bnbinder,BpBinder的命名含義,Bn-代表Native,而Bp代表Proxy。一旦理解到這個層次,ProcessState就容易弄明白了。

下面我們看JVM概念空間中對這些概念的包裝。為了通篇理解裝置上下文,我們需要將Android VM概念空間中的裝置上下文和C++空間總的裝置上下文連線起來進行研究。

為了在上層使用統一的介面,在JVM層面有兩個東西。在Android中,為了簡化管理框架,引入了ServiceManger這個服務。所有的服務都是從ServiceManager開始的,只用通過Service Manager獲取到某個特定的服務標識構建代理IBinder。在Android的設計中利用Service Manager是預設的Handle為0,只要設定請求包的目標控制代碼為0,就是發給Service Manager這個Service的。在做服務請求時,Android建立一個新的Service Manager Proxy。Service Manager Proxy使用ContexObject作為Binder和Service Manager Service(服務端)進行通訊。

我們看到Android程式碼一般的獲取Service建立本地代理的用法如下:

IXXX mIxxx=IXXXInterface.Stub.asInterface(ServiceManager.getService("xxx"));

例如:使用輸入法服務:

IInputMethodManager mImm=

IInputMethodManager.Stub.asInterface(ServiceManager.getService("input_method"));

這些服務代理獲取過程分解如下:

(1) 通過呼叫GetContextObject呼叫獲取裝置上下物件。注意在AndroidJVM概念空間的ContextObject只是 與Service Manger Service通訊的代理Binder有對應關係。這個跟c++概念空間的GetContextObject意義是不一樣的。

注意看看關鍵的程式碼

BinderInternal.getContextObject() @BinderInteral.java

NATIVE JNI:getContextObject() @android_util_Binder.cpp

Android_util_getConextObject @android_util_Binder.cpp

ProcessState::self()->getCotextObject(0) @processState.cpp

getStrongProxyForHandle(0) @

NEW BpBinder(0)

注意ProcessState::self()->getCotextObject(0) @processtate.cpp,就是該函式在程序空間建立 了ProcessState物件,打開了Binder裝置dev/binder,並且傳遞了引數0,這個0代表了與Service Manager這個服務繫結。

(2) 通過呼叫ServiceManager.asInterface(ContextObject)建立一個代理ServiceManger。

mRemote= ContextObject(Binder)

這樣就建立起來ServiceManagerProxy通訊框架。

(3)客戶端通過呼叫ServiceManager的getService的方法建立一個相關的代理Binder。

ServiceMangerProxy.remote.transact(GET_SERVICE)

IBinder=ret.ReadStrongBinder() -》這個就是JVM空間的代理Binder

JNI Navite: android_os_Parcel_readStrongBinder() @android_util_binder.cpp

Parcel->readStrongBinder() @pacel.cpp

unflatten_binder @pacel.cpp

getStrongProxyForHandle(flat_handle)

NEW BpBinder(flat_handle)-》這個就是底層c++空間新建的代理Binder。

整個建立過程可以使用如下的示意圖來表示:

Activity為了建立一個IPC,需要建立兩個連線:訪問Servicemanager Service的連線,IXXX具體XXX Service的代理物件與XXXService的連線。這兩個連線對應c++空間ProcessState中BpBinder。對IXXX的操作最後就是對BpBinder的操作。由於我們在寫一個Service時,在一個Package中寫了Service Native部分和Service Proxy部分,而Native和Proxy都實現相同的介面:IXXX Interface,但是一個在服務端,一個在客服端。客戶端呼叫的方式是使用remote->transact方法向Service發出請求,而在服務端的OnTransact中則是處理這些請求。所以在Android Client空間就看到這個效果:只需要呼叫代理物件方法就達到了對遠端服務的呼叫目的,實際上這個呼叫路徑好長好長。

我們其實還一部分沒有研究,就是同一個程序之間的物件傳遞與遠端傳遞是區別的。同一個程序間專遞服務地和物件,就沒有代理BpBinder產生,而只是物件的直接應用了。應用程式並不知道資料是在同一程序間傳遞還是不同程序間傳遞,這個只有核心中的Binder知道,所以核心Binder驅動可以將Binder物件資料型別從BINDER_TYPE_BINDER修改為BINDER_TYPE_HANDLE或者BINDER_TYPE_WEAK_HANDLE作為引用傳遞。

7. Service詳解

上一章我們分析了Android IPC架構,知道了Android服務構建的一些基本理念和原理,本章我們將深入分析Android的服務。Android體系架構中三種意義上服務:

Native服務

Android服務

Init空間的服務,主要是屬性設定,這個IPC是利用Socket來完成的,這個我將在另外一章來討論。

Navite服務,實際上就是指完全在C++空間完成的服務,主要是指系統一開始初始化,通過Init.rc指令碼起來的服務,例如Service Manger service,Zygote service,Media service , ril_demon service等。

Android服務是指在JVM空間完成的服務,雖然也要使用Navite上的框架,但是服務主體存在於Android空間。Android是二階段初始(Init2)初始化時建立的服務。

1 Service本質結構

我們還是從Service的根本意義分析入手,服務的本質就是響應客戶端請求。要提供服務,就必須建立接收請求,處理請求,應答客服端的框架。我想在Android Service設計者也會無時不刻把這個服務本質框圖掛在腦海中。從程式的角度,服務一定要存在一個閉合迴圈框架和請求處理框架

分析清楚服務框就必須弄清楚以下的機制及其構成。

(1)閉合迴圈結構放置在哪裡?

(2)處理請求是如何分發和管理?

(3)處理框架是如何建立的?

(4)概念框架是如何建立的?

2 Service基本框架分析

Android設計中,Native Service和Android Service採用了同一個閉合迴圈框架。這個閉合迴圈框架放置在Native的C++空間中,,[email protected][email protected]兩個類完成了全部工作。

在服務框架中,ProcessState是公用的部分,這個公用部分最主要的框架就是閉合迴圈框架和接收到從Binder來的請求後的處理框架。我們將服務框架用ProcessSate來表示,簡言之:

(1) addservice

(2) 建立閉合迴圈處理框架。

int main(int argc, char** argv)

{

sp<ProcessState> proc(ProcessState::self());

addService(String16("xxx0"), new xxx0Service());

addService(String16("xxx1"), new xxx1Service());

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();//閉合迴圈框架

}

2.1 Native Service

Native Service是在系統Init階段通過Init.rc指令碼建立的服務。

首先來看看一個例子[email protected]_mediaserver.cpp的建立過程。

int main(int argc, char** argv)

{

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

LOGI("ServiceManager: %p", sm.get());

AudioFlinger::instantiate();

MediaPlayerService::instantiate();

CameraService::instantiate();

AudioPolicyService::instantiate();

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

我們將程式碼向下展開了一層,更能看到事物的本質。

int main(int argc, char** argv)

{

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

defaultServiceManager()->addService(String16("media.audio_flinger"), new AudioFlinger());

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

(1)服務程序建立了ProcessState物件,並將給物件登記在程序的上下文中。

(2)建立一個新AudioFlinger物件,並將物件登記Service Manager Service中。

(3)開始就收請求,處理請求,應答這個迴圈閉合框架。

2.2 Android Service

Androids service是系統二階段(Init2)初始化時建立的服務。

Android的所有服務迴圈框架都是建立[email protected](SystemServer.java)上。在SystemServer.java中看不到迴圈結構,只是可以看到建立了init2的實現函式,建立了一大堆服務,並AddService到service Manager。

main() @ com/android/server/SystemServer

{

init1();

}

Init1()是在Native空間實現的(com_andoird_server_systemServer.cpp)。我們一看這個函式就知道了,原來這個閉合迴圈處理框架在這裡:

init1->system_init() @System_init.cpp

在system_init()我們看到了這個久違的迴圈閉合管理框架。

{

Call "com/android/server/SystemServer", "init2"

…..

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

Init2()@SystemServer.java中建立了Android中所有要用到的服務:

Entropy Service

Power Manager

Activity Manager

Telephony Registry

Package Manager

Account Manager

Content Manager

System Content Providers

Battery Service

Hardware Service

Alarm Manager

Init Watchdog

Sensor Service

Window Manager

Bluetooth Service

statusbar

Clipboard Service

Input Method Service

NetStat Service

Connectivity Service

Accessibility Manager

Notification Manager

Mount Service

Device Storage Monitor

Location Manager

Search Service

Checkin Service

Wallpaper Service

Audio Service

Headset Observer

Backup Service

AppWidget Service

3 ProcessState和IPCThreadState

從巨集觀來講,PocessState及其IPCThreadState處於IPC與核心打交道包裝層。前面的章節已經提到,下面我將更詳細的分析。有關IPC的c++空間的實現都是從ProcessState這個物件完成的。

我們可以得出如下的結論:不管JVM的Binder做了多麼複雜的操作,最終還是需要利用ProcessState 這個c++空間的物件把資料傳遞給Binder Driver,接收資料也是通過ProcessState這個物件完成,ProcessState是所有Binder IPC必經的通道。

ProcessState放置在全域性變數gProcess中,每個程序只有一個ProcessState物件,負責開啟Binder裝置驅動,建立執行緒池等。而IPCThreadState每個執行緒有一個,IPCThreadState例項登記在Linux執行緒程的上下文附屬資料中,主要負責Binder資料讀取,寫入和請求處理框架。IPCThreadSate在構造的時候,獲取程序的ProcessSate並記錄在自己的成員變數mProcess中,通過mProcess可以獲取到Binder的控制代碼。

 

3.1 ProcessState的生命週期

既然ProcessState是Binder通訊的基礎,那麼Process必須在Binder通訊之前建立。客戶端,服務端都必須建立。由於現在重點討論服務端,所以重心放置在服務端。在Android體系中有c++空間的服務,JVM空間的服務,這兩類服務在本質上相同的,只是形式上不同,由於他們都是建立在ProcessState這個基礎上,所以在形式上不同就僅僅表現在對OnTransact的回撥處理的不同。

Native Service

我們直接可以看到使用sp<ProcessState> proc(ProcessState::self()),建立建立ProcessState,一旦呼叫ProcessState就建立了,並且這個self將ProcessSate登記在全域性變數中。

Android Service

建立Android Service服務system_init @System_init.cpp中我們可以看到相同的結構。有一點不同的是所有的Android Service都執行在一個程序中:systemsever程序。

3.2 Binder Driver包裝 @IPCThreadState

ProcessSate構造的時候,使用open_binder開啟/driver/binder,並將控制代碼記錄在mDriverFD,在ProcessState中並不使用這個控制代碼,真正使用這個Binder裝置控制代碼的是IPCThreadState,所有關於Binder的操作放置在IPCThreadState中:

(1)讀取/寫入:talkWithDriver()@IPCThreadState對ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)進行包裝。

(2)請求處理:executeCommand(...)@ IPCThreadState

(3)迴圈結構:joinThreadPool()

joinThreadPool()

{

While(1){

talkWithDriver(...)

...

executeCommand(...)

}

}

8. Android啟動過程詳解

Android從Linux系統啟動有4個步驟;

(1) init程序啟動

(2) Native服務啟動

(3) System Server,Android服務啟動

(4) Home啟動

總體啟動框架圖如:

第一步:initial程序(system/core/init)

init程序,它是一個由核心啟動的使用者級程序。核心自行啟動(已經被載入記憶體,開始執行,並已初始化所有的裝置驅動程式和資料結構等)之後,就通過啟動一個使用者級程式init的方式,完成引導程序。init始終是第一個程序.

Init.rc

Init.marvell.rc

Init程序一起來就根據init.rc和init.xxx.rc指令碼檔案建立了幾個基本的服務:

servicemanamger

zygote

。。。

最後Init並不退出,而是擔當起property service的功能。

1.1指令碼檔案

[email protected]/Core/Init

Init.c: parse_config_file(Init.rc)

@parse_config_file(Init.marvel.rc)

解析指令碼檔案:Init.rc和Init.xxxx.rc(硬體平臺相關)

Init.rc是Android自己規定的初始化指令碼(Android Init Language, System/Core/Init/readme.txt)

該指令碼包含四個型別的宣告:

Actions

Commands

Services

Options.

1.2 服務啟動機制

我們來看看Init是這樣解析.rc檔案開啟服務的。

(1)開啟.rc檔案,解析檔案內容@ system/core/init/init.c

將service資訊放置到service_list中。@ system/core/init parser.c

(2)restart_service()@ system/core/init/init.c

service_start

execve(…).建立service程序。

第二步 Zygote

Servicemanager和zygote程序就奠定了Android的基礎。Zygote這個程序起來才會建立起真正的Android執行空間,初始化建立的Service都是Navtive service.在.rc指令碼檔案中zygote的描述:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

所以Zygote從main(…)@frameworks/base/cmds/app_main.cpp開始。

(1) main(…)@frameworks/base/cmds/app_main.cpp

建立Java Runtime

runtime.start("com.android.internal.os.ZygoteInit", startSystemServer);

(2) [email protected]

建立虛擬機器

執行:com.android.internal.os.ZygoteInit:main函式。

(3)main()@com.android.internal.os.ZygoteInit//正真的Zygote。

registerZygoteSocket();//登記Listen埠

startSystemServer();

進入Zygote服務框架。

經過這幾個步驟,Zygote就建立好了,利用Socket通訊,接收ActivityManangerService的請求,Fork應用程式。

第三步 System Server

[email protected]在Zygote上fork了一個程序: com.android.server.SystemServer.於是[email protected](SystemServer.java)就建立了。Android的所有服務迴圈框架都是建立[email protected](SystemServer.java)上。在SystemServer.java中看不到迴圈結構,只是可以看到建立了init2的實現函式,建立了一大堆服務,並AddService到service Manager。

main() @ com/android/server/SystemServer

{

init1();

}

Init1()是在Native空間實現的(com_andoird_server_systemServer.cpp)。我們一看這個函式就知道了,init1->system_init() @System_init.cpp

在system_init()我們看到了迴圈閉合管理框架。

{

Call "com/android/server/SystemServer", "init2"

…..

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

 

init2()@SystemServer.java中建立了Android中所有要用到的服務。

這個init2()建立了一個執行緒,來New Service和AddService來建立服務

第三步 Home啟動

[email protected]後半段,我們可以看到系統在啟動完所有的Android服務後,做了這樣一些動作:

(1) 使用xxx.systemReady()通知各個服務,系統已經就緒。

(2) 特別對於ActivityManagerService.systemReady(回撥)

Widget.wallpaper,imm(輸入法)等ready通知。

Home就是在ActivityManagerService.systemReady()通知的過程中建立的。下面是ActivityManagerService.systemReady()的虛擬碼:

systemReady()@ActivityManagerService.java

resumeTopActivityLocked()

startHomeActivityLocked();//如果是第一個則啟動HomeActivity。

startActivityLocked(。。。)CATEGORY_HOME

9. Zygote Service詳解

在本章我們會接觸到這兩個單詞:

Zygote [生物] 受精卵, 接合子, 接合體

Spawn:產卵

通過這兩個單詞,我們就可以大體知道Zygote是幹什麼的了,就是叫老母雞下蛋。通過“Zygote”產出不同的子“Zygote”。從大的架構上講,Zygote是一個簡單的典型C/S結構。其他程序作為一個客服端向Zygote發出”孵化”請求,Zygote接收到命令就“孵化”出一個Activity程序來。

Zygote系統程式碼組成及其呼叫結構:

Zygote.java

提供訪問Dalvik “zygote”的介面。主要是包裝Linux系統的Fork,以建立一個新的VM例項程序。

ZygoteConnection.java

Zygote的套介面連線管理及其引數解析。其他Actvitiy建立程序請求是通過套介面傳送命令引數給Zygote。

ZygoteInit.java

Zygote的main函式入口。

Zygote系統程式碼層次呼叫

main()

startSystemServer()…

runSelectLoopMode()

Accept socket connection

Conntecion.RunOnce()

Read argument

folkAndSpecialize

folkAndSpecialize使用Native函式Dalvik_dalvik_system_Zygote_forkAndSpecialize

//native 的獲取

dalvik/vm/native

//dalvik_system_Zygote.c

const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {

{ "fork", "()I",

Dalvik_dalvik_system_Zygote_fork },

{ "forkAndSpecialize", "(II[II[[I)I",

Dalvik_dalvik_system_Zygote_forkAndSpecialize },

{ "forkSystemServer", "(II[II[[I)I",

Dalvik_dalvik_system_Zygote_forkSystemServer },

{ NULL, NULL, NULL },

};

在這裡我們就有了Zygote服務的全貌理解,也在Code中印證了。【應yk_hu0621修正】{由於Android中沒有具體應用程式的入口,都是通過啟動Actvity來啟動相關的Android應用,而這個 Android應用則對應著Linux程序,Activity便Host在這個應用程式上。}

{原文:Activity在本質上是個什麼東西,就是一個Linux程序}

從分析中我們可以看到,Android使用了Linux的fork機制。在Linux中Fork是很高效的。

一個Android的應用實際上一個Linux程序,所謂程序具備下面幾個要素,

a.要有一段程式供該程序執行,程式是可以被多個程序共享的。

b..程序專用的系統堆疊空間。

c.程序控制塊,在linux中具體實現是task_struct

d.有獨立的儲存空間。

fork 創造的子程序複製了父親程序的資源,包括記憶體的內容task_struct內容,在複製過程中,子程序複製了父程序的task_struct,系統堆疊空間和頁面表,而當子程序改變了父程序的變數時候,會通過copy_on_write的手段為所涉及的頁面建立一個新的副本。所以只有子程序有改變變數時,子程序才新建了一個頁面複製原來頁面的內容,基本資源的複製是必須的,整體看上去就像是父程序的獨立儲存空間也複製了一遍。

再看看下面Google在講解Dalvik虛擬機器的圖片,我們就大體有了Android系統中Actvitiy的實際對映狀態有了基本的認識。

10.Android GWES基本原理篇

我這裡的GWES這個術語實際上從Microsoft 的Window上移植過來的,用GWES來表示Android的視窗事件系統不是那麼準確,在Android中Window是個弱化了的概念,更多的表現在View這個概念上。在很大程度上,Android的View的概念可以代替Microsoft Window這個概念,有點和Microsof暗中較勁的意味,你用過的概念我就偏不用,這個也是我以為的設計者意圖。

原始GUI基本框架

首先我們從Android的SDK外特性空間開始,在編寫Actvitiy時,我們都是面對的處理函式:OnXXXX(),例如有按鍵按下就是OnKeyDown等,在這個過程中系統做了怎樣的處理?要詳細的理解這個過程,我們就需要理解Andoid的View管理,視窗系統,訊息系統和輸入系統。我們還是從最本質的地方開始,Android作為一種嵌入式的圖形使用者介面系統,它的基本原理與一般GUI的原理是相同的,同時也是遵循GWES(圖形視窗事件系統)的一般規律,總體上Android就是管理使用者輸入和系統螢幕輸出的一個系統。其實GWES這個名稱更能體現GUI的基本實質要素:圖形、視窗、事件。

1. 一般GUI的基本組成

GUI的實現就是對上面提到的三個基本要素的管理,根據這這三個要素的特性及其涉及的範圍,GUI在總體上可以分為三部分:

事件管理器

視窗管理器

GDI(繪製與GDI邏輯物件管理)

(1) 事件管理器

收集系統訊息,轉換並分發系統訊息和使用者訊息給各個視窗物件。

訊息佇列管理

(2)視窗管理器:

管理視窗的建立,銷燬

視窗的繪製

活動視窗,輸入焦點的切換

視窗間關係的管理

控制元件,選單實現

(3)GDI

上下文裝置管理

上下文裝置物件管理:字型,畫筆等

圖形繪製:點、線,填充等

圖象操作:位傳送、點陣圖操作

2 系統體系構架及其資料流的大體走向

在本質上GUI就是管理使用者輸入和螢幕輸出,我們從上面的體系結構可以看到GUI的這兩大資料流的基本流向,這也決定了Android GWES設計的最基本的著眼點。

Android弱化了視窗的概念,著重使用View的概念。所以Android的基本組成可以從上面的圖修改成如下的組成:

11.Android GWES訊息系統篇

我們要理解Android的訊息系統,Looper,Handle,View等概念還是需要從訊息系統的基本原理及其構造這個源頭開始。從這個源頭,我們才能很清楚的看到Android設計者設計訊息系統之意圖及其設計的技術路線。

訊息系統的基本原理

從一般的系統設計來講,一個訊息迴圈系統的建立需要有以下幾個要素:

訊息佇列

傳送訊息

訊息讀取

訊息分發

訊息迴圈執行緒

首先來研究一下訊息驅動的基本模型,我使用如下的圖形來表示一個訊息系統最基本構成:

上面的模型代表應用程式一直查詢自己的訊息佇列,如果有有訊息進來,應用訊息處理函式中根據訊息型別及其引數來作相應的處理。

訊息系統要運作起來,必定有訊息的產生和消費。我們可以從下圖看到訊息生產和消費的一個基本的鏈條,這是一個最基本的,最簡單的訊息系統。

生產執行緒將訊息傳送到訊息佇列,訊息消費者執行緒從訊息佇列取出訊息進行相應的處理。但是這樣簡單的模型對實際執行的系統來說是不夠的,例如對系統資源的消耗等不能很好的處理,我們就需要一個有旗語的訊息系統模型,在上面的訊息系統模型中加入了一個旗語,讓訊息消費者執行緒在沒有訊息佇列為空時,等待旗語,進入到掛起狀態,而有訊息到達時,才被喚醒繼續執行。當然生產者同時也可以是消費者。

2 Android的訊息模型

Android要建立一個訊息系統使用了Looper,MessageQueue,Handler等概念,從上節的原理我們可以知道這些都是概念包裝,本質的東西就是訊息佇列中訊息的分發路徑的和訊息分發處理方式的設計。Android巧妙的利用了物件抽象技術抽象出了Looper和Handler的概念。在Looper和Handler兩個概念的基礎上,通過View的處理函式框架,Android十分完美的達到訊息分發的目的。

參照基本訊息系統描述模型,我給出了Android訊息系統整體框架,表示如下:

Android訊息系統訊息分發框架

3 Looper,Handler詳解

Looper只是產生一個訊息迴圈框架,首先Looper建立了訊息佇列並把它掛接在Linux的執行緒上下文中,進入到取訊息,並分發訊息的迴圈當中。Handler物件在同一個執行緒上下文中取得訊息佇列,對訊息佇列進行封裝操作,最主要的就是SendMessage和擔當起dispatchMessage這個實際工作。外部系統需要向某個Android執行緒傳送訊息,必須通過屬於該AndroidThread的Handler這個物件進行。

Handler屬於某個執行緒,取決Handlerd物件在哪個執行緒中建立。Handler在構建時做了如下的預設動作:

從執行緒上下文取得Looper。

通過Looper獲取到訊息佇列並記錄在自己的成員mQueue變數中

Handler使用訊息佇列進行物件封裝,提供如下的成員函式:

通過 post(Runnable r)傳送。Runnable是訊息處理的回撥函式,通過該訊息的傳送,引起Runable 的回撥執行,Post訊息放置訊息佇列的前面。Message.callback=Runable.

通過 sendMessage傳送。放置在所有的Post訊息之後,sendMessage傳送訊息.

dispatchMessage分發訊息。訊息帶有回撥函式,則執行訊息回撥函式,如何沒有則使用預設處理函式:handleMessage。而handleMessage往往被過載成某個繼承Handler物件的新的特定的handleMessage。

幾乎所有的Message傳送時,都指定了target。Message.target=(this).

Looper執行在Activity何處?我們現在可以從程式碼堆疊中縱觀一下Looper的位置。

NaiveStart.main()

ZygoteInit.main

ZygoteInit$MethodAndArgsCall.run

Method.Invoke

method.invokeNative

ActivityThread.main()

Looper.loop()

ViewRoot$RootHandler().dispatch()

handleMessage

....

這樣我們就更清楚的瞭解到Looper的執行位置。

=============================================================================================================

 

《Android核心分析》整理如下:

12.Android核心分析之Android GEWS視窗管理基本架構篇

Android的視窗管理是C/S模式的。Android中的Window是表示Top Level等頂級視窗的概念。DecorView是Window的Top-Level View,這個View我稱之為主View,DecorView會預設的attach到Activity的主視窗中。主View被加入到WindowManager中,WM使用WindowState與這個主View對應。

Activity建立一個主視窗後,在將主視窗新增到WindowManager時,首先要建立WindowManager代理物件,並開啟一個會話(實現IWindowSession AIDL介面),並維持該會話。Activity將通過該會話與WindowManager建立聯絡,這個Session是C/S體系的基礎,Client通過WindowSession將window加入到Window Manager中。一個完整的視窗概念橫跨了View,ViewRoot,WindowManager Service。Window,DecorView,View,IWindow ,ISession,WindowState之間的關係如下:

客戶端的Activity通過Session會話與WindowManager建立對話,而WindowManager則通過IWindow介面訪問Client,將訊息傳遞到Client端,通過訊息分發渠道,將訊息傳遞到處理函式OnXXX。

後面我們將通過Client,WM Service分別加以分析。

13.Android GWES視窗管理詳解

Android的視窗管理是C/S模式的。Android中的Window是表示Top Level等頂級視窗的概念。DecorView是Window的Top-Level View,這個View我稱之為主View,DecorView會預設的attach到Activity的主視窗中。主View被加入到WindowManager中,WM使用WindowState與這個主View對應。

Activity建立一個主視窗後,在將主視窗新增到WindowManager時,首先要建立WindowManager代理物件,並開啟一個會話(實現IWindowSession AIDL介面),並維持該會話。Activity將通過該會話與WindowManager建立聯絡,這個Session是C/S體系的基礎,Client通過WindowSession將window加入到Window Manager中。

一個完整的視窗概念橫跨了View,ViewRoot,WindowManager Service。Window,DecorView,View,IWindow ,ISession,WindowState之間的關係如下

Client端的Activity通過Session會話與WindowManager建立對話,而WindowManager則通過IWindow介面訪問Client,將訊息傳遞到Client端,通過訊息分發渠道,將訊息傳遞到處理函式OnXXX。

後面我們將通過Client,WM Service分別加以分析。

2 Client端

我一致認為在Android中Window的概念並不是個很重要的概念。他的Window類,只是在PhoneWindow和MidWindow中使用。而PhoneWindow只是做了一個具體跟手機功能相關的公用事件的處理,所以在Android中PhoneWindow並不是一個抽象的純正概念,而是一個跟手機系統相關的一個特別視窗概念,例如按鍵的預設動作處理,按鍵音的發出等等。

2.1 View

在Activity中真正重要的概念是View,以下是Google官方對View的定義:

This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for <em>widgets</em>, which are used to create interactive UI components (buttons, text fields, etc.). The {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.

我對View不做翻譯,翻譯成檢視好像不太佳,View在Android中,View比檢視具有廣的外延。View包含了使用者互動,包含了顯示,檢視在中文中僅僅表示了靜態的顯示。對於View的理解應該從最容易的理解開始。我們使用過編輯器,在Android中這個編輯器就是一個View,這個編輯器需要顯示文字,需要接收使用者的鍵盤輸入和滑鼠選擇,但是一個螢幕上有多個編輯器,如何管理,如何切換焦點編輯器,這些都是需要管理的。

客戶端的組成:(Window,View,ViewRoot,WindowManager Proxy)

在Activity在performLaunchActivity時,會使用Activity.attach()建立一個PhoneWindow主視窗。這個主視窗的建立並不是一個重點。handleResumeActivity真正要啟動一個Activity時候,將主視窗加入到WindowManager,當然並不是將主視窗本身,而是將主視窗的DecorView加入到WindowManager中。

真正Window核心的抽象概念存在於View,ViewRoot,WindowManger中的WindowState。為了描述概念的方便性,我特別提出主View這個概念,這個主View就是Top-Level View of the window. 主View與View想對,突出主View是attatch到主視窗上的。而一般的View則是存在於主View中的。主視窗這個概念,我講的主視窗實際上就是Android提到的Top Level Window。

我們所提到的概念:View,GroupView,DecorView,ViewRoot都是存在於Client端,只有WindowState這個概念存在於Window Manager Service端。

DecorView實際上是一個ViewGroup。在依存關係上來講,對看個主視窗來講,DecorView是Top-Level View.View並不是關注的重點,重要的是我們如何需要知道分發路徑是建立在什麼關係上的。View的成員變數mParent用來管理View上級關係的。而ViewGroup顧名思義就是一組View的管理,於是在ViewGroup構建了焦點管理和子View節點陣列。這樣通過View的mParent和ViewGroup的mChildren構建了Android中View直接的關係網。

2.2 Focus Path

所謂的Foucs Path就是我們的KeyEvent傳遞的路線。一般的我們的KeyEvent在主迴圈中主View通過View的焦點記錄關係傳遞到焦點View上。例如下圖,View22是焦點,我們從最頂層的View通過mFcous的關係鏈找到最後所形成的路徑就是Focus Path。

2.3 ViewRoot,Window Manager Proxy

ViewRoot與Window Manager的核心是IWindowSession和IWindow。ViewRoot通過IWindowSession新增視窗到Window Manager。而IWindow這是Window Manager分發訊息給Client ViewRoot的渠道。利用AIDL介面進行程序間通訊。

ViewRoot實際是一個Handler,ViewRoot建立主View與WindowsManger通訊的橋樑。ViewRoot在本質上一個Handler。我們知道Handler的基本功能就是處理回撥,傳送訊息。

Activity在使用getSystemService獲取WindowManagerImpl ,建立了一個WindowManagerImpl例項,即Window Manager服務的代理:

wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE);並呼叫wm.addview新增視窗到WMService中。

這個過程在客戶端建立了什麼樣的管理框架,並如何這個會話?在Window Manager Proxy中建立了View,Layout ,ViewRoot三者的對應關係表。構造一個ViewRoot就會開啟一個session,並利用IWindowSession建立會話上下文。

4 Window Manager Service

本次對於Window Manager Service的研究僅限於FocusWindow,訊息系統。其他的部分將在後面的專門章節討論。

Window Manager管理的視窗是應用程式的Top-level視窗,我這裡參照Window的概念叫主視窗。主視窗為什麼要放在在Service這邊來管理呢?為什麼不放在Client那邊?主視窗放置在一起管理是為了計算Z-order序列,根據應用程式的狀態來顯隱應用程式的視窗。我想Android設計者在考慮設計視窗系統的時候,一定首先考慮:

視窗z-order序的管理

活動視窗的計算,及其變化通知

視窗歸屬(屬於哪個應用)

輸入法管理

Window Service大體上實現瞭如下的功能:,

(1)Z-ordered的維護函式

(2)輸入法管理

(3)AddWindow/RemoveWindow

(4)Layerout

(5)Token管理,AppToken

(6)活動視窗管理(FocusWindow)

(7)活動應用管理(FocusAPP)

(8)轉場動畫

(9)系統訊息收集執行緒

(11)系統訊息分發執行緒

在服務端的視窗物件叫做WindowState。在Service維護了一個mWindow陣列,這個mWindow就是Window的Z-order序陣列。mWindowMap用於記錄<Client:Binder,WindowState物件>。

WindowState有一個叫做mClient成員變數來記錄客戶端IWindow例項,通過IWindow介面例項,Service可以訪問客戶端的資訊,說以IWindow是Service連線View橋樑。

(1) FocusWindow活動視窗如何計算?

基本原理就是查詢前景應