8/30-9/3
一週總結報告
8/30-9/3這一週主要是學習了事件的分發機制、Activity啟動模式、編譯系統和Android.mk、Android.bp檔案。
一、 事件分發機制
- 分發物件
被分發的物件是使用者觸控式螢幕幕而產生的點選事件,事件主要包括:按下、滑動、抬起與取消。這些事件被封裝成MotionEvent物件。該物件中的主要事件如下表所示:
事件 |
觸發場景 |
單次事件流中觸發的次數 |
MotionEvent.ACTION_DOWN |
在螢幕按下時 |
1次 |
MotionEvent.ACTION_MOVE |
在螢幕上滑動時 |
0次或多次 |
MotionEvent.ACTION_UP |
在螢幕抬起時 |
0次或1次 |
MotionEvent.ACTION_CANCLE |
滑動超出控制元件邊界時 |
0次或1次 |
按下、滑動、抬起、取消這幾種事件組成了一個事件流。事件流以按下為開始,中間可能有若干次滑動,以抬起或取消作為結束。(在安卓對事件分發的處理過程中,主要是對按下事件作分發,進而找到能夠處理按下事件的元件。對於事件流中後續的事件(如滑動、抬起等),則直接分發給能夠處理按下事件的元件。)
- 分發事件的元件
分發事件者:Activity、ViewGroup、View;
關係:Activity包括了ViewGroup,ViewGroup又可以包含多個View。
- 分發的核心方法
dispatchTouchEvent()分發:處理觸控事件分發,事件(多數情況)從Activity的dispatchTouchEvent開始的,執行super.dispatchTouchEvent(ev),事件向下分發。
onInterceptTouchEvent()攔截:由於viewGroup下還包含子View,所以預設返回值為false,即不攔截此ACTION_DOWN事件。如果返回false,則ACTION_DOWN事件繼續傳遞給其子view。如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return true,那麼後續的move, up等事件將不再傳遞給onInterceptTouchEvent(),而是和down事件一樣傳遞給該ViewGroup的onTouchEvent()處理,注意,目標view將接收不到任何事件。
onTouchEvent()處理:ViewGroup提供的方法,預設返回false,返回true表示攔截。返回true表示該View能處理該事件,事件將終止向上傳遞(傳遞給其父View);返回false表示不能處理,則把事件傳遞給其父View的onTouchEvent()方法來處理
- 事件傳遞
Android中預設情況下事件傳遞是由最終的view的接收到,傳遞過程是從父佈局到子佈局。dispatchTouchEvent()返回true,後續事件(ACTION_MOVE、ACTION_UP)會再傳遞,如果返回false,dispatchTouchEvent()就接收不到ACTION_UP、ACTION_MOVE。
當觸控事件ACTION_DOWN發生之後,先呼叫Activity中的dispatchTouchEvent函式進行處理,緊接著ACTION_DOWN事件傳遞給ViewGroup中的dispatchTouchEvent函式,接著ACTION_DOWN事件傳遞到呼叫ViewGroup中的onInterceptTouchEvent函式,此函式負責攔截ACTION_DOWN事件。由於viewGroup下還包含子View,所以預設返回值為false,即不攔截此ACTION_DOWN事件。如果返回false,則ACTION_DOWN事件繼續傳遞給其子view。由於子view不是viewGroup的控制元件,所以ACTION_DOWN事件接著傳遞到onTouchEvent進行處理事件。此時訊息的傳遞基本上結束。從上可以分析,motionEvent事件的傳遞是採用隧道方式傳遞。隧道方式,即從根元素依次往下傳遞直到最內層子元素或在中間某一元素中由於某一條件停止傳遞。
二、 Activity啟動模式
standard(標準模式)
每次啟動一個Activity都會重新建立一個新的例項,不管這個例項是否已經存在
singleTop(棧頂複用模式)
如果新的Activity的例項已經存在於棧頂,那麼新的Activity不會被建立,系統通過呼叫其onNewIntent()方法將Intent傳遞到該例項。只要不在棧頂,都會建立例項。
singleTask(棧內複用模式)
只要Activity在一個棧中存在,那麼多次啟動該Activity都不會重新建立例項。如果在棧中已經有該Activity的例項,就重用該例項(會呼叫例項的onNewIntent())。重用時,會讓該例項回到棧頂,因此在它上面的例項將會被移除棧。
singleInstance(單例項模式) 只會返回一次
無論從哪個Task中啟動目標Activity,只會建立一個目標Activity例項,並會使用一個全新的Task棧來載入該Activity例項,並且這個例項獨立執行在一個activity任務棧中,這個task只有這個例項,不允許有別的Activity存在
建立4個activity,分別採用四中啟動模式,4個activity可以相互跳轉:在每個activity中定義三個按鈕,為這三個按鈕新增點選事件,點選按鈕時通過intent隱式啟動activity分別跳轉其它三個activity;在AndroidManifest.xml檔案中將四個activity分別設定為採用四種啟動模式啟動。通過back返回時觀察activity頁面,結合上述知識理解,更加深刻。
三、 Android編譯系統
- Android.mk和Android.bp
1) 簡介
Android.mk本質是一個makefile檔案,可以將原始檔分組為模組。Android.bp的出現就是為了替換掉Android.mk。Android.bp只是一個純粹的配置檔案,不包括分支、迴圈語句等控制流程,本質上就是一個json配置檔案。Android.bp通過Blueprint+soong轉換成ninja的構建規則檔案build.ninja,再使用ninja來進行構建工作。
2) mk與bp檔案的相互轉換(課後作業)
命令列輸入lunch報錯,則首先需要安裝python-lunch,然後重新執行lunch,在m10/out/soong/host/linux-x86/bin目錄下新建一個Android.mk檔案,寫入Android.mk內容,輸入命令“./androidmkAndroid.mk>Android.bp”實現轉換。
3) Android.mk的編寫
a) Android.mk檔案首先需要指定LOCAL_PATH變數,用於查詢原始檔。由於一般情況下Android.mk和需要編譯的原始檔在同一目錄下,所以定義成如下形式:LOCAL_PATH:=$(callmy-dir)
上面的語句的意思是將LOCAL_PATH變數定義成本檔案所在目錄路徑。
b)
LOCAL_MODULE_TAGS:=userengtestsoptional
user:指該模組只在user版本下才編譯
eng:指該模組只在eng版本下才編譯
tests:指該模組只在tests版本下才編譯
optional:指該模組在所有版本下都編譯
c) 在Android.mk檔案中,還可以用LOCAL_MODULE_PATH和LOCAL_UNSTRIPPED_PATH指定最後的目標安裝路徑.
d)
不同的檔案系統路徑用以下的巨集進行選擇:
TARGET_ROOT_OUT:表示根檔案系統。
TARGET_OUT:表示system檔案系統。
TARGET_OUT_DATA:表示data檔案系統。
一些模組詳解:https://www.jianshu.com/p/b23905bdcb3a
- 將Jgrep、cgrep、mangrep的function內容抽出來,做成shell指令碼將指令碼檔案所在的路徑增加到環境變數:
步驟:Jgrep:
1.(新建檔案命名為jgrep)
gedit jgrep;
2.(將jgrep指令碼內容貼上)
#!/bin/bash
find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" -print0 | xargs -0 grep --color -n "$@";
3.(新增環境變數:修改 /home/.bashrc,在檔案的最後新增)
export PATH=/home/liu/test:$PATH;
4.(使環境變數生效)
source ~/.bashrc;
- 使用自定義指令碼
jgrep "public",即完成。
cgrep的指令碼內容:#!/bin/bash
find . -name .repo -prune -o -name .git -prune -o -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) -print0 | xargs -0 grep --color -n "$@"
mangrep的指令碼內容:#!/bin/bash
find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' -exec grep --color -n "$@" {} +
其他步驟與Jgrep相同。
- 專案整編的方式
谷歌原生的編譯方式:1.編譯環境初始化:source build/envsetup.sh;2.選擇編譯目標:lunch aosp_arm-eng ;3.執行編譯:make -j8 。還可單編某個模組:前兩步相同,第三步改為“mm/mma/mmm 需要編譯的模組 ”。
SCM封裝的編譯方式:1.在project根目錄開啟終端輸入./mk,終端會顯示scm封裝好的整編和單編的方式;2.根據需要選擇整編和單編:(1)整編:./mk -p HS60 -s -v userdebug -f -m n -o all (2)單編:./mk -p HS50 -s -f -v userdebug --platform-bit 32 -m mma -o packages/apps/SamSungCamera3/ -j8
- 模組單編指令
m:編譯整個安卓系統,構建模組,在只修改了部分原始碼的情況下使用,構建速度比mm/mma快。
mm:構建模組,但對於任何依賴項,它將構建錯誤。編譯當前目錄下的模組,當前目錄下需要有Android.mk這個makefile檔案,否則就往上找最近的Android.mk檔案。
mma:構建模組,它可以先構建依賴項,然後再構建模組。當前目錄新增或刪除檔案後,可以用mma重新編譯。