深入理解android卷2
① 怎麼開發android framework
一.認識android的架構
Android其本質就是在標準的linux系統上增加了Java虛擬機Dalvik,並在Dalvik虛擬機上搭建了一個JAVA的application framework,所有的應用程序都是基於JAVA的application framework之上。
android分為四個層,從高層到低層分別是應用程序層、應用程序框架層、系統運行庫層和linux核心層。
二.搭建環境
搭建開發環境
對國內的開發者來說最痛苦的是無法去訪問android開發網站。為了更好的認識世界,對程序員來說,會翻牆也是的一門技術,帶你去領略牆外的世界,好了,不廢話了, 國內開發者訪問(androiddevtools) 上面已經有了所有你要的資源,同時可以下載到我們的主角framework
但是這樣的搭建只能去閱讀源代碼,我們無法去更進一步去實現自己的rom,我們看到錘子的系統在早期的開放rom是自己從新實現了framework的代碼,現在看起來他成功了,所以我們還要去搭建android系統的源碼編譯環境。
搭建源碼編譯環境
http://www.cnblogs.com/bluestorm/p/4419135.html
https://source.android.com/source/downloading.html(這里詳細的介紹了如何下載編譯)
三.開始主題
在一開始寫c程序的時候都有一個運行的入口,比如
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
//這里的main就是應用的入口
int main(int argc, const char * argv[]){
return 0;
}
在計算機網路原理中我們用socket實現一個伺服器端,不斷的接聽客戶端的訪問,而且他的代碼是這樣實現的:
#include <winsock2.h>
#pragma comment(lib, "WS2_32.lib")
#include <stdio.h>
void main()
{
WORD wVersionRequested;//版本號
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);//2.2版本的套接字
//載入套接字型檔,如果失敗返回
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}
//判斷高低位元組是不是2,如果不是2.2的版本則退出
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2)
{
return;
}
//創建流式套接字,基於TCP(SOCK_STREAM)
SOCKET socSrv = socket(AF_INET, SOCK_STREAM, 0);
//Socket地址結構體的創建
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//轉換Unsigned long型為網路位元組序格
addrSrv.sin_family = AF_INET;//指定地址簇
addrSrv.sin_port = htons(6000);
//指定埠號,除sin_family參數外,其它參數都是網路位元組序,因此需要轉換
//將套接字綁定到一個埠號和本地地址上
bind(socSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//必須用sizeof,strlen不行
listen(socSrv, 5);
SOCKADDR_IN addrClient;//字義用來接收客戶端Socket的結構體
int len = sizeof(SOCKADDR);//初始化參數,這個參數必須進行初始化,sizeof
//循環等待接受客戶端發送請求
while (1)
{
//等待客戶請求到來;當請求到來後,接受連接請求,
//返回一個新的對應於此次連接的套接字(accept)。
//此時程序在此發生阻塞
SOCKET sockConn = accept(socSrv, (SOCKADDR*)&addrClient, &len);
char sendBuf[100];
sprintf(sendBuf, "Welcome %s to JoyChou",
inet_ntoa(addrClient.sin_addr));//格式化輸出
//用返回的套接字和客戶端進行通信
send(sockConn, sendBuf, strlen(sendBuf)+1, 0);//多發送一個位元組
//接收數據
char recvBuf[100];
recv(sockConn, recvBuf, 100, 0);
printf("%s\\n", recvBuf);
closesocket(sockConn);
}
}
他採用了一個while死循環去監聽客戶端的請求。
在一遍啰嗦之後,主角終於閃亮的登場了。
先上源代碼
public final class ActivityThread {
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
//從中可以看到為app開辟了一個線程進入了looper之中
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
看到源碼失望了,沒有一個while循環啊,其實用了他方法實現
//用一個looper的機制循環監聽響應
Looper.prepareMainLooper();
Looper.loop();
進一步深入代碼
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 在這里看到了一個循環監聽消息
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that ring the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
② 深入理解ANDROID怎麼樣
《深入理解Android》是一本以情景方式對Android的源代碼進行深入分析的書。內容廣泛,以對Framework層的分析為主,兼顧Native層和Application層;分析深入,每一部分源代碼的分析都力求透徹;針對性強,注重實際應用開發需求,書中所涵蓋的知識點都是Android應用開發者和系統開發者需要重點掌握的。
全書共10章:
第1章介紹了閱讀本書所需要做的准備工作,主要包括對Android系統架構和源碼閱讀方法的介紹;
第2章通過對Android系統中的MediaScanner進行分析,詳細講解了Android中十分重要的JNI技術;
第3章分析了init進程,揭示了通過解析init.rc來啟動Zygote以及屬性服務的工作原理;
第4章分析了Zygote、SystemServer等進程的工作機制,同時還討論了Android的啟動速度、虛擬機HeapSize的大小調整、Watchdog工作原理等問題;
第5章講解了Android系統中常用的類,包括sp、wp、RefBase、Thread等類,同步類,以及Java中的Handler類和Looper類,掌握這些類的知識後方能在後續的代碼分析中做到游刃有餘;
第6章以MediaServer為切入點,對Android中極為重要的Binder進行了較為全面的分析,深刻揭示了其本質。
第7章對Audio系統進行了深入的分析,尤其是AudioTrack、AudioFlinger和AudioPolicyService等的工作原理。
第8章深入講解了Surface系統的實現原理,分析了Surface與Activity之間以及Surface與SurfaceFlinger之間的關系、SurfaceFlinger的工作原理、Surface系統中的幀數據傳輸以及LayerBuffer的工作流程。
第9章對Vold和Rild的原理和機制進行了深入的分析,同時還探討了Phone設計優化的問題;
第10章分析了多媒體系統中MediaScanner的工作原理。
本書適合有一定基礎的Android應用開發工程師和系統工程師閱讀。通過對本書的學習,大家將能更深刻地理解Android系統,從而自如應對實際開發中遇到的難題。
作者簡介 :
鄧凡平,資深Android開發工程師,熱衷於Android源代碼的研究,對Android的架構設計和實現原理有非常深刻的認識和理解,應用開發經驗也十分豐富。目前就職於國內一家領先的Android企業,負責Framework的開發和維護。樂於分享,活躍於CSDN等專業社區,撰寫的Android Framework源碼的系列文章深受讀者歡迎。此外,他對Linux內核、C/C++/Python相關的技術,以及高性能網路伺服器和多核並行開發等也有一定的研究。
③ 《深入理解 Android卷 I I I》txt下載在線閱讀全文,求百度網盤雲資源
《深入理解Android 卷III》(張大偉)電子書網盤下載免費在線閱讀
鏈接: https://pan..com/s/1NRSBBKYubmJsWp8DwJgRJg
書名:深入理解Android 卷III
作者:張大偉
豆瓣評分:9.0
出版社:機械工業出版社
出版年份:2015-8-1
頁數:539
內容簡介:
深入理解Android(卷3)》是Android經典暢銷書系(對Android系統源代碼的分析最為系統和細致)「深入理解Android」系列Framework卷的第III卷,從源代碼的角度,對Android系統的Audio和UI兩大功能的相關模塊的實現原理和工作機製做了系統且詳細的分析,填補了市場的空白。
《深入理解Android(卷3)》在邏輯上分為4個部分:
Part 01(第1~2章):這是本書的基礎部分,首先介紹了Android源碼環境的搭建、編譯和調試;然後講解了Android進程間通信與任務調度的工具Binder與MessageQueue。這兩項基礎工作是深入研究Android前必須做的功課。
Part 02(第3章):詳細分析了AudioService服務的實現,包括音量管理、音頻外設管理、AudioFocus機制的實現等內容。
Part 03(第4~6章):這是本書的核心內容之一,詳細分析了Android UI的通用實現,依次剖析了WindowManagerService、Android輸入系統、Android控制項系統的工作原理。
Part 04(第7~8章):主要分析了SystemUI和Android壁紙相關服務的實現,包括StatusBarManagerService與NotificationManagerService兩個系統服務,以及WallpaperManagerService系統服務、動態壁紙與靜態壁紙的工作原理等內容。
作者簡介:
張大偉,資深Android系統開發工程師,現就職於索尼移動。從2011年開始從事Android開發,專注於Android系統源代碼的研究、定製與維護工作,對Android的架構設計與運行原理有著深入的認識與實踐經驗,其中對UI相關模塊、多媒體系統尤為擅長。曾主持了Android多窗口、多任務以及單手操作等系統定製的開發工作。此外對於其他開發平台如.NET亦有相當的經驗。
④ 如何評價《1616深入理解Android內核設計思想 第2版》
、《深入理解Android內核設計思想(第2版)(冊)》
本書既適合Android系統工程師適合於應用發工程師閱讀提升Android發能力讀者本書潛移默化習程更深刻理解Android系統並所知識自應用實際發難題解決
二、《Android Studio應用發實戰詳解》
本書適合Android初級讀者、Android應用發員、Android者、Android Studio發員、Android智能家居、Android穿戴設備研發員習作相關培訓校專院校相關專業教用書
三、《深入解析Android 虛擬機》
本書幾乎涵蓋Dalvik VM系統所主要內容並且講解通俗易懂特別利於讀者習並消化
四、《Android傳器發與智能設備案例實戰》
本書幾乎涵蓋Android傳器外設發所需所主要內容講解通俗易懂
五、《構建安全Android App》
本書介紹主流Android安全技術發並應用整Android應用發代碼
六、《Android 應用測試指南》
《Android應用測試指南》本移測試實用工具書本書針前流行技術、框架工程質量改進工具進行介紹步步清晰指導家何寫應用程序測試用例利用各種測試手段保證Android項目質量
七、《精通Android網路發》
本書幾乎涵蓋Android網路應用所主要內容講解通俗易懂
⑤ 《深入理解Android:WiFi,NFC和GPS卷》pdf下載在線閱讀全文,求百度網盤雲資源
《深入理解Android:WiFi,NFC和GPS卷》網路網盤pdf最新全集下載:
鏈接:https://pan..com/s/1inU9q9tp3SkEq9LSo87w7Q
簡介:本書從實際應用的需求出發,適合所有Android系統工程師、Android應用開發工程師和BSP開發工程師閱讀。本書是經典暢銷書「深入理解Android」系列的新作,由資深Android系統專家鄧凡平先生撰寫,全志和高通等公司資深專家擔任技術審校並強烈推薦。從通信專業知識和Android系統代碼實現的角度,對Netd、Wi-Fi、NFC和GPS等模塊的代碼進行深入的剖析,旨在深刻揭示其實現原理和工作流程。其中涉及大量通信相關的專業知識,因此特意邀請全志和高通等著名晶元公司的資深專家擔任技術審校。
⑥ 深入理解android2-WMS,控制項
title: '深入理解android2-WMS,控制項-圖床版'
date: 2020-03-08 16:22:42
tags:
typora-root-url: ./深入理解android2-WMS-控制項
typora--images-to: ./深入理解android2-WMS-控制項
WMS主要負責兩個功能, 一是負責窗口的管理,如窗口的增加刪除,層級.二是負責全局事件的派發.如觸摸點擊事件.
先簡單介紹幾個重要的類
IWindowSession. 進程唯一的.是一個匿名binder.通過他向WMS請求窗口操作
surface 繪畫時,canvas會把內容繪制到surface里.surface是有surfaceFlinger提供給客戶端的.
WindowManager.LayoutParams 集成自ViewGroup.LayoutParams.用來指明client端的窗口的一些屬性.最重要的是type. 根據這屬性來對多個窗口進程ZOrder的排序.
windowToken.向WMS添加的窗口令牌.每個窗口都要有一個令牌.
IWindow. 是client提供給WMS的.繼承自binder.WMS通過IWindow對象來主動發起client端的事件.
窗口的本周就是進行繪制所使用的surface,客戶端向WMS添加窗口的過程,就是WMS為客戶端分配surface的過程.
ui框架層就是使用surface上繪制ui元素.及響應輸入事件
WMS負責surface的分配.窗口的層級順序
surfaceFlinger負責將多個Surface混合並輸出.
WMS有SystemServer 進程啟動.他和AMS其實是運行於一個進程中的.只是分別有各自的線程.
上邊傳入了兩個handler.這里就使用windowManager的handler來創建WMS.也就是在一個handerThread線程中創建
用來管理每個窗口的事件輸入.也就是把輸入事件轉發到正確的窗口
能獲取顯示系統的同步信號.用來驅動動畫的渲染
所有窗口動畫的總管,在mChoreographer的驅動下渲染所有動畫
只有PhoneWindowManager一個實現.定義了很多窗口相關的策略.是最重要的成員,比如負責窗口的zorder順序.
zorder就是各個窗口在z軸的值.越大越在屏幕上層.窗口就是根據zorder值一層一層堆在一起.
可以繪制的屏幕列表.默認是只有1個.
管理所以窗口的顯示令牌token,每個窗口都要屬於一個token.這里的IBinder 是
表示所有Activity的token. AppWindowToken是WindowToken的子類,這個list的順序和AMS中對mHistory列表中activity的順序是一樣的 .反應了系統中activity的疊加順序.也就是說.所有窗口都有WindowToken.而Activity對應的窗口則多了AppWindowToken.
每個窗口都對應一個WindowState.存儲改窗口的狀態信息(這就和AMS中對每個activity抽象成ActivityRecord一樣)
這里的iBinder 是IWIndow類.
Session 是WMS提供給客戶端來與WMS進行交互的,這是匿名binder.為了減輕WMS的負擔.客戶端通過IWindowManager.openSession 拿到他的代理.然後通過代理與WMS交互.每個進程唯一.
客戶端通過IWindowSession.add 來添加窗口. iWindowSession 是同aidl形成的.最終到了WMS.addWindow.
這里總的來說就是確立了客戶窗口的WindowToken.WindowState.和DisplayContent. 並都保存了起來.同時根據layoutparams.type進行了些窗口等級的判斷.
WindowToken將同一個應用組件的窗口安排在一起.一個應用組件可以是Activity,InputMethod.
WindowToken使應用組件在變更窗口時必須與自己的WindowToken匹配.
這里主要是為了處理窗口的層級關系而設立的.
只要是一個binder對象.都可以作為token向wms聲明.wms會把這個binder對應起一個WindowToken.其實就是把客戶端的binder和wms里的一個WindowToken對象進行了綁定.
因為Activity比較復雜,因此WMS為Activity實現了WindowToken的子類 appwindowtoken.同時在AMS啟動Activity的ActivityStack.startActivityLocked里聲明token.
然後在activityStack.realStartActivityLocked里在發給用戶進程,然後用戶在通過這個binder和WMS交互時帶過來.
activity則在 activityStack 線程的handleResumeActivity 里把Activity 對應的窗口,加入到wMS中
取消token 也是在AMS中 ,也就是說, AMS負責avtivity的token向WMS的添加和刪除.
當然.Activity的 r.appToken 是 IApplicationToken.Stub ,他里邊有一系列的窗口相關的通知回調.
這里總結下. AMS在創建Activity的ActivityRecord時,創建了他的appToken,有把appToken傳送給WMS.WMS對應匹配為APPWindowToken,最後還把這個appToken發送給activity.因此AMS就通過ActivityRecord就可有直接操作WMS對該窗口的繪制.如圖.
每個window在WMS里都抽象成了WindowState.他包含一個窗口的所有屬性.WindowState在客戶端對應的則是iWidow.stub類.iWidow.stub有很多窗口通知的回調.
WindowState被保存在mWindowMap里.這是整個系統所有窗口的一個全集.
HashMap<IBinder, WindowToken> mTokenMap .這里是 IApplicationToken(客戶端)和WindowToken的映射
HashMap<IBinder, WindowState> mWindowMap 這里是IWidow(客戶端)和WindowState的映射,並且WMS通過這個IWindow 來回調客戶端的方法.
上圖可以看出.每個activity 只有一個ActivityRecord.也只有一個AppToken,也就只有一個WindowToken.而一個acitvity可能有多個窗口.每個窗口對應一個WindowState.
WindowToken用來和AMS交換. 而WindowState對應的iWindow則是WMS來與客戶端交互的.
窗口顯示次序就是窗口在Z軸的排了.因為窗口是疊加在一起的.因此就需要知道哪些顯示在上邊,哪些在下邊.這個由WindowState構造時確定
可見.分配規則是由WindowManagerPolicy mPolicy來決定的.產生 mBaseLayer和mSubLayer. mBaseLayer決定該窗口和他的子窗口在所有窗口的顯示位置. mSubLayer決定子窗口在同級的兄弟窗口的顯示位置.值越高.顯示約靠上.
WindowState 產生了他自己這個窗口的layer值後.在添加窗口的時候就會把所有窗口按layer排序插入mWindows列表中,在通過 adjustWallpaperWindowsLocked();進行層級調整.
當客戶端通過IWindowsession.add後,客戶端還沒有獲得Surface.只有在執行IWindowsession.relayout後.客戶端才獲得了一塊Surface. IWindowsession.relayout根據客戶端提供的參數,為客戶端提供surface.具體實現是WMS.relayoutWindow
總的來說就是根據用戶傳入的參數,更新WindowState.然後遍歷所有窗口布局.在設置合適的Surface尺寸,在返回給用戶端
會循環調用6次.里邊的邏輯大概如下
這里主要下,因為之前加了鎖.requestTraversalLocked他又會重復執行();因此會重復循環執行布局.
布局這部分就記個原理吧
布局完成後.客戶端的尺寸和surface都得到了.就可以繪制 了.WMS會通知客戶端布局發送變化
總結,WMS 負責管理所有的窗口.包括系統窗口和APP窗口,而窗口必須有一個WindowToken所為標識符.同時WMS為每個窗口創建一個WindowState類,這是窗口在服務端的抽象.WindowState則綁定了一個客戶端的IWindow類,WMS通過這個IWindow 向APP發送消息.
AMS在啟動Activity的時候.把ActivityRecord.token 通過wms.addtoken 注冊到WMS.又把這個token發送到APP端.因此三方可以通過這個token正確找到對應的數據.
WMS負責給所以窗口按ZOrder排序,確定窗口的尺寸,提供繪畫用的surface.
Activity的窗口是先wms.addtoken 建立windowToken關系 . wms.addWindow. 添加串口, WMS.relayout獲取surface. 完成 .
一個windowToken對應一個Activity. 但是可能對應多個windowSatate.也就是對應多個窗口.
是view樹的根實現類是viewRootImpl.但是他不是view.他是用來和WMS進行交流的管理者.viewRootImpl內部有個IWindowSession,是WMS提供的匿名binder,同時還有個iWindow子類,用來讓WMS給viewr發消息. view通過ViewRoot向WMS發消息.WMS在通過IWIndow 向APP發消息. 每個View樹只有一個ViewRoot,每個Activity也只有一個ViewRoot. UI繪制,事件傳遞.都是通過ViewRoot.
.實現類是PhoneWindow . Activity和View的溝通就是通過Window.Activity實現window的各種回調.一個Activity也對應一個PhoneWindow.也對應一個View樹.
Docerview 就是View樹的根.這是一個View. 他由PhoneWindow管理. 下文的WindowManager也由phoneWindow管理.
他還管理window的屬性 WindowManager.layoutparams.
他是一個代理類.他集成自ViewManager.他的實現是WindowManagerImpl.這是每個Activity都有一個.但是他只是把工作委託給了 WindowManagerGlobal來實現. 他負責添加刪除窗口,更新窗口.並控制窗口的補件屬性WindowManager.Layoutparams.
是進程唯一的.負責這個進程的窗口管理.他里邊有三個集合.保存這個進程所有窗口的數據.這里的每個數據根據index得到的是同一個Activity屬性.所有的WindowManager的操作都轉到他這里來.
private final ArrayList<View> mViews 每個view是個跟節點
private final ArrayList<ViewRootImpl> mRoots view對應的viewRoot
private final ArrayList<WindowManager.LayoutParams> mParams 窗口的layoutparams屬性.每個窗口一個
對於一個acitivity對象永遠對應一個PhoneWindow,一個WindowManagerImpl,一個WMS端的APPWindowToken,一個AMS里的ActivityRecord(但是如果一個activity在棧里有多個對象,就有多個ActivityRecord和AppWindowToken),acitvity 的默認窗口的view樹是DocerView.
一個窗口 對應一個ViewRoot,一個View樹.一個WindowManager.LayoutParams,一IWindow(WMS回調app).一個WSM端的WindowSatate.
但是一個Activity可以有多個窗口,因此對應WMS里可能有多個WindowSatate.這些WindowState都對應一個AppWindowToken.
一個Activity可能被載入多次.因此在AMS中可能有多個ActivityRecord對應這個activit的多個對象.
但是一個進程則對應一個WindowManagerGlobal.一個ActivityThread(主線程).一個ApplicationThread(AMS調用app).一個iWindowSession(viewroot向WMS發消息)
這里的區別就是 app與AMS 的交互是以進程之間進行通信.而App與WMS的交互.則是以窗口作為通信基礎.
當Activity由AMS啟動時.ActivityThread 通過handleResumeActivity執行resume相關的操作.這個函數首先是執行activity.resume, 此時activity 對應的view樹已經建立完成(oncreate中建立,PhoneWindow也創建了).需要把activity的窗口添加到WMS中去管理.
這里的wm是WindowManager.是每個activity一個.他內部會調用WindowManagerGlobal.addView
WindowManagerGlobal.addView
這里會為窗口創建ViewRootImpl. 並把view.ViewRootImpl.WindowMa.LayoutParams都保存在WindowManagerGlobal中, 並通過ViewRootImpl向WMS添加窗口
如果這個窗口是子窗口(wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)
就把子窗口的token設為父窗口的token否則就是所屬activity的token.
在來個圖
在這里我們看到.我們通過mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 拿到的並不是遠程的WMS.而是本地的WindowManagerImpl. 他又把請求轉發給WindowManagerGlobal ,而WindowManagerGlobal作為進程單實例.又是吧請求轉給對應窗口的ViewRootImpl.ViewRootImpl通過WMS的IWindowSession 把數據發給WMS.
ViewRootImpl用來溝通View和WMS.並接受WMS的消息.這是雙向的binder通信.作為整個空間樹的根部,控制項的測量,布局,繪制,輸入時間的派發都由ViewRootImpl來觸發.
ViewRootImpl由WindowManagerGlobal創建,是在activityThread.handleResumeActivity時,先執行activity.resume.在調用wm.addView. 就會執行WindowManagerGlobal.addView里.創建ViewRootImpl,此時是在ui線程中.
ViewRootImpl里的mView屬性.host屬性,就是view樹
添加窗口時通過requestLayout();向ui線程發送消息.最後回調到ViewRootImpl.performTraversals.他是整個ui控制項樹,measure.layout.draw的集合.
這里分為五個階段.
預測量階段.進行第一次測量,獲得view.getMeasuredWitdh/Height,此時是控制項樹期望的尺寸.會執行View的onMeasure
布局階段,根據預測量的結果,通過IWindowSession.relayout向WMS請求調整窗口的尺寸這會使WMS對窗口重新布局,並把結果返回給ViewRootImpl.
最終測量階段, 預測量的結果是view樹期望的結果.WMS可能會進行調整,在這里WMS已經把結果通知了ViewRootImpl.因此這里會窗口實際尺寸performTraversals進行布局.view及子類的onMeasure會被回調
布局階段. 測量完成後獲得空間的尺寸,布局要確定控制項的位置,View及子類的onLayout會被回調.
繪制階段,使用WMS提供的surface.進行繪制,View及子類的onDraw會被回調.
通常我們看到的都是 先measure,在layout在draw. 這里看到.其實measure先得到期望值,在和WMS溝通.WMS在調整後,返回確定值,在根據確定值進行mesure.
measureHierarchy里會通過三次協商.執行performMeasure 來確認合適的尺寸.
performMeasure 會調用view 的measure 優會調用onMeasure. 我們可重寫onMeasure來實現測量.而measure 方法是final的.onMeasure 的結果通過setMeasuredDimension方法保存.
對於view. onMeasure.比較容易. 對於ViewGroup.則還要遍歷調用他所以子view的measure. 並且需要考慮padding和子view 的margin. padding是控制項外內邊距. margin 是控制項外邊距.
ViewGroup需要先測量完子view.在根據子view的測量值得到自己的寬高.舉例,如果只有一個子view.那麼ViewGroup的寬= 子view的寬+子view的margin+viewg的padding. 至少是這個值.
繼續回到performTraversals
這里就是提前測量了一下.得到控制項樹希望的尺寸大小,
通過relayoutWindow來布局窗口. ViewRootImpl 通過IWindowSession 來通知WMS進行窗口布局.
這里主要下. 調用WMS後.WMS會調整窗口的尺寸. 同時會生成surface返回給ViewRootImpl. 因此後續的繪畫就有了畫布了.可以看到最後的參數是mSurface.這是本地的surface. 這里會和wms的進行綁定.
接下來繼續performTraversals,綁定WMS返回的surface.然後更新尺寸.
最後進行最終測量. 上邊過程太亂了. 了解下就行.還是看常見的控制項繪制流程.
繪制由viewRootImpl.performTraversals觸發, 抽取出來後,就是這樣
就是直接調用view樹的根的measure方法. 傳入到View
該方法是final .意味著無法重寫.這里又會調用onMeasure.
因此.對於view.在onMeasure中調整好高度,通過setMeasuredDimension設置好自己的測量寬高就可以了.
對應ViewGroup.則在onMeasure中,先要遍歷子view.調用他們的measure(注意一定是調用子類的measure,measure又會調用onMeasure), 子view寬高都知道後,在根據子view的寬高來設置自己.也就是ViewGroup的寬高受子view影響.
可以看到view的measure又調用了onMeasure, 如果是view 則可以直接重新onMeasure來設定大小.而對於ViewGroup, 則需要重寫onMeasure來先遍歷子view.設定大小.然後再設定viewGroup的大小. ViewGroup並沒有重寫onMeasure.因為每個ViewGroup要實現的效果不同,需要自己完成.但ViewGroup提供了幾個方法供ViewGroup的繼承類來遍歷子view.
view的寬高由自己的layoutParams和父view提供的 widthMeasureSpec|heightMeasureSpec共同決定.
View 自己的寬高,是保存在LayoutParams中對,以寬舉例 LayoutParams.width 有三種情況,精確值(就是指定大小),MATCH_PARENT. WRAP_CONTENT,模式則有fuview提供.有 unspecified,exactly,at_most三種.
匹配如下.
其實這個很好理解. 如果子view自己指定了寬高.就用他的值就可以.如果子view是match_parent.那就使用父view提供的寬高. 如果子view是wrap_content,那就不能超過父view的值.
看下ViewGroup為子view繪制而提供的方法,可以看到.ViewGroup會減去padding和margin,來提供子view的寬高.
上步measure過程未完成後,整個view書的 測量寬高都得到了.也就是view.getMeasuredWidth()和getMeasuredHeight()
performLayout中會調用mView.layout. 這樣就把事件從ViewRootImpl傳遞到了view.而layout中又會調用onLayout.ViewGroup需要重寫onLayout為子view進行布局,遍歷調用子view的layout.因此就完成整個view樹的laylut過程.
豎向的實現, 豎向的就行把view從上到下一次排開
這里注意區分.measure過程是先得到子view的測量值,在設定父ViewGroup的值.而layout過程則是先傳入父view的左上右下值,來計運算元view的左上右下的位置值.這里應該具有普遍性.但不知道是否絕對.
performDraw 中的調用draw.又調用mView.draw.然後就進入view樹的繪制了.
view的draw 又會調用onDraw ,viewGroup又調用dispatchDraw()把draw分發到子view里 繪制的畫布就是canvas. 這是從surface.lockCanvas中獲得的一個區域.
而在ViewGroup.dispatchDraw中.重要的一點是getChildDrawingOrder 表示子view的繪制順序.默認是與ziview的添加順序一樣.我們也可以改變他.最後繪制的會顯示在最上邊,而這也影響view的事件傳遞順序.
view.draw. 就是一層一層的畫內容.先畫北京,在onDraw.在畫裝飾什麼的.
canvas.translate(100,300)通過平移坐標系.使之後的內容可以直接在新坐標系中繪制.
這就是ViewGroup在向子view傳遞canvas的時候.方便多了. 會之前先對其ziview的左上角.那麼子view就可以直接從自己坐標軸的(0,0)開始繪制, 繪制完成後ViewGroup在還原原有坐標系.
canvas.save. canvas.restore 用來保存還原坐標系.
view.invalidate.
當某個view發送變化需要重繪時,通過view.invalidate向上通知到ViewRootImpl.從這個view到ViewRootImpl的節點都標記為藏區域.dirty area. ViewRootimpl再次從上到下重繪時,只繪制這些臟區域.效率高.
本來安卓兼容使用鍵盤,也支持,觸摸.二者的輸入事件派發不一樣.使用鍵盤時會有個控制項處於獲得焦點狀態.處於觸摸模式則由用戶決定. 因此控制項分為兩類.任何情況下都能獲得焦點.如輸入文本框.只有在鍵盤操作時才能獲得焦點.如菜單,按鈕.
安卓里有觸摸模式.當發送任意觸摸時進入觸摸模式.當發送方向鍵和鍵盤或者執行View.requestRocusFromTouch時,退出觸摸模式.
獲取焦點. view.request.
先檢查是否能獲取焦點,
然後設置獲取簡單的標記,
向上傳遞到ViewRootimpl.保證只能有一個控制項獲取焦點.
通知焦點變化的監聽者.
更新view的drawable狀態,
requestChildFocus會把焦點事件層層上報取消原來有焦點的控制項.最後的效果就是從viewrootimpl中.到最終有焦點的view.構成一條 mFoucued 標識的鏈條.來個圖就明白了.每個view的mFocused總是指向他的直接下級.
獲取focus的傳遞是從底層view到頂層的ViewRootImpl.而取消focus測試從頂層的ViewRootimpl到底層原來那個獲得焦點的view.
而如果是ViewGroup請求獲取焦點,會根據FLAG_MASK_FOCUSABILITY特性來做不同方式,分別有先讓自己獲取焦點,或者安卓view的索引遞增或者遞減來匹配view.
ViewRootImpl 中的.WindowInputEventReceiver接受輸入事件.他會把事件包裝成一個QueuedInputEvent.然後追加到一個單鏈表的末尾.接著重頭到尾的處理輸入事件,並通過deliverInputEvent完成分發.這里會把單鏈表所有事件都處理完.
deliverInput中又會把觸摸事件執行到通過 ViewPreImeInputStage.processKeyEvent. 轉入mView.dispatchPointerEvent(event).這里又進入 dispatchTouchEvent
MotionEvent是觸摸事件的封裝.getAction可以拿到動作的類型和觸控點索引號.
getX(),getY().拿到動作的位置信息.通過getPointID拿到觸控點的id. 動作以down 開頭.跟多個move.最後是up.
,當事件返回true.表示事件被消費掉了.
⑦ 深入理解android 卷1 2 3 有什麼區別
卷1主要講的一些Android世界的由來和一些學習底層庫所需掌握的知識,然後講了音頻Audiolinger和圖像Surfacelinger,和一些其他的服務,對應用層和框架層開發意義不大。
卷2主要講的是框架層Framework的知識,對系統開發有幫助。
卷3主要講和UI相關的,對應用幫助最大就是這本書了,裡面也講了很多系統開發相關的知識。