一般來講,啓動方式分爲兩種:冷啓動和熱啓動。html
一、冷啓動:當啓動應用時,後臺沒有該應用的進程,這時系統會從新建立一個新的進程分配給該應用,這個啓動方式就是冷啓動。android
二、熱啓動:當啓動應用時,後臺已有該應用的進程(例:按back鍵、home鍵,應用雖然會退出,可是該應用的進程是依然會保留在後臺,可進入任務列表查看),因此在已有進程的狀況下,這種啓動會從已有的進程中來啓動應用,這個方式叫熱啓動。shell
特色數據庫
一、冷啓動:冷啓動由於系統會從新建立一個新的進程分配給它,因此會先建立和初始化Application類,再建立和初始化MainActivity類(包括一系列的測量、佈局、繪製),最後顯示在界面上。瀏覽器
二、熱啓動:熱啓動由於會從已有的進程中來啓動,因此熱啓動就不會走Application這步了,而是直接走MainActivity(包括一系列的測量、佈局、繪製),因此熱啓動的過程只須要建立和初始化一個MainActivity就好了,而沒必要建立和初始化Application,bash
由於一個應用重新進程的建立到進程的銷燬,Application只會初始化一次。網絡
冷啓動啓動流程:當點擊app的啓動圖標時,安卓系統會從Zygote進程中fork建立出一個新的進程分配給該應用: app
以後會依次建立和初始化Application類、建立MainActivity類、加載主題樣式Theme中的windowBackground等屬性設置給MainActivity以及配置Activity層級上的一些屬性、再inflate佈局、當onCreate/onStart/onResume方法都走完了後最後才進行contentView的measure/layout/draw顯示在界面上,因此直到這裏,應用的第一次啓動纔算完成,這時候咱們看到的界面也就是所說的第一幀。因此,總結一下,應用的啓動流程以下:框架
Application的構造器方法——>attachBaseContext()——>onCreate()——>Activity的構造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測量佈局繪製顯示在界面上。 eclipse
大體流程以下:
一、點擊桌面圖標,Launcher會啓動程序默認的Acticity,以後再按照程序的邏輯啓動各類Activity
二、啓動Activity都須要藉助應用程序框架層的ActivityManagerService服務進程(Service也是由ActivityManagerService進程來啓動的);在Android應用程序框架層中,ActivityManagerService是一個很是重要的接口,
它不但負責啓動Activity和Service,還負責管理Activity和Service。
Step 1. 不管是經過Launcher來啓動Activity,仍是經過Activity內部調用startActivity接口來啓動新的Activity,都經過Binder進程間通訊進入到ActivityManagerService進程中,而且調用ActivityManagerService.startActivity接口;
Step 2. ActivityManagerService調用ActivityStack.startActivityMayWait來作準備要啓動的Activity的相關信息;
Step 3. ActivityStack通知ApplicationThread要進行Activity啓動調度了,這裏的ApplicationThread表明的是調用ActivityManagerService.startActivity接口的進程,對於經過點擊應用程序圖標的情景來講,這個進程就是Launcher了,
而對於經過在Activity內部調用startActivity的情景來講,這個進程就是這個Activity所在的進程了;
Step 4. ApplicationThread不執行真正的啓動操做,它經過調用ActivityManagerService.activityPaused接口進入到ActivityManagerService進程中,看看是否須要建立新的進程來啓動Activity;
Step 5. 對於經過點擊應用程序圖標來啓動Activity的情景來講,ActivityManagerService在這一步中,會調用startProcessLocked來建立一個新的進程,而對於經過在Activity內部調用startActivity來啓動新的Activity來講,這一步是不須要執行的,
由於新的Activity就在原來的Activity所在的進程中進行啓動;
Step 6. ActivityManagerServic調用ApplicationThread.scheduleLaunchActivity接口,通知相應的進程執行啓動Activity的操做;
Step 7. ApplicationThread把這個啓動Activity的操做轉發給ActivityThread,ActivityThread經過ClassLoader導入相應的Activity類,而後把它啓動起來。 連接
一、將背景圖設置成咱們APP的Logo圖,做爲APP啓動的引導,如今市面上大部分的APP也是這麼作的:
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.NoActionBar.Splash">
<item name="android:windowBackground">@drawable/splash_image_1</item>
</style>
複製代碼
二、將背景顏色設置爲透明色,這樣當用戶點擊桌面APP圖片的時候,並不會"當即"進入APP,並且在桌面上停留一會,其實這時候APP已是啓動的了,只是咱們原來黑/白屏windowBackground的顏色設置成透明的:
<style name="AppTheme.NoActionBar.Transparent">
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
複製代碼
adb shell am start -W [-D] [PackageName]/[PackageName.MainActivity] 加-D可等待調試器附加到進程 執行成功後將返回三個測量到的時間:
這裏面涉及到三個時間,ThisTime、TotalTime 和 WaitTime。WaitTime 是 startActivityAndWait 這個方法的調用耗時,ThisTime 是指調用過程當中最後一個 Activity 啓動時間到這個 Activity 的 startActivityAndWait 調用結束。TotalTime 是指調用過程當中第一個 Activity 的啓動時間到最後一個 Activity 的 startActivityAndWait 結束。若是過程當中只有一個 Activity ,則 TotalTime 等於 ThisTime。 總結:若是隻關心某個應用自身啓動耗時,參考TotalTime;若是關心繫統啓動應用耗時,參考WaitTime;若是關心應用有界面Activity啓動耗時,參考ThisTime。 連接: www.jianshu.com/p/a0e242d57…通常而言,熱啓動時時間較長是由於在MainActivity的onCreate()函數中作了太多UI耗時操做,而冷啓動時間較長主要跟Application的onCreate()函數以及MainActivity的onCreate()函數的執行時間有關。 工欲善其事,必先利其器,首先來介紹一下Android測量函數耗時的幾個方法:
一、使用Android Studio 的profiler功能:點擊record錄製app啓動過程當中執行每一個函數及其子函數的CPU耗時,很是直觀的能夠找到啓動過程當中耗時最多的地方。爲了捕獲到完整的冷啓動流程,在開始錄製以前先到Android系統設置菜單中選擇帶調試的應用:
而後點擊應用圖標,系統會等待咱們attach到相應的進程,咱們首先開始CPU耗時錄製而後在attach到進程,這樣子就能夠記錄到Application onCreate函數的執行耗時了。 要想追蹤指定代碼段的操做耗時,能夠在代碼段執行先後跳轉追蹤調試方法:Debug.startMethodTracing(「launch」);
....
代碼
.....
Debug.stopMethodTracing();
複製代碼
隨着程序的運行,在/sdcard/文件夾下會自動建立一個launch.trace的文件,這個文件能夠經過Android Studio profiler和eclipse traceView打開,而後進行分析。
二、使用eclipse traceView打開trace文件:
左上角爲線程面板,右上角爲時間面板,底部是數據分析面板。時間線面板以每一個線程爲一行,右邊是該線程在整個過程當中方法執行的狀況,一行中有不少的小色塊。這些色塊表明採集過程當中方法調用時間線,相同的顏色表明相同的方法,其中的每個小色塊就表明一次方法的調用,色塊的長度表明方法執行時間的長短,左邊爲第一個色塊表明方法執行開始,最右邊色塊表明最後一個方法執行結束,有時候能夠根據色塊長度來作個大體判斷,哪個方法執行時間相對來講比較長,你能夠把鼠標放到色塊上,就會顯示該方法調用的詳細信息。在數據分析面板,你能夠點擊某個函數展開更詳細的信息,展開後,大多數有如下兩個類別:Parents:調用該方法的父類方法
Children:該方法調用的子類方法
若是該方法含有遞歸調用,可能還會多出兩個類別:
Parents while recursive:遞歸調用時所涉及的父類方法
Children while recursive:遞歸調用時所涉及的子類方法
至於數據分析面板紅色框中,各個字段的含義以下:
開發者最關心的數據有: 很重要的指標:Calls + Recur Calls / Total , 最重要的指標: Cpu Time / Call 由於咱們最關心的有兩點,一是調用次數很少,但每次調用卻須要花費很長時間的函數。這個能夠從Cpu Time / Call反映出來。另一個是那些自身佔用時間不長,但調用卻很是頻繁的函數。這個能夠從**Calls + Recur Calls / Total **反映出來。三、使用StrictMode線程策略
StrictMode意思爲嚴格模式,是用來檢測程序中違例狀況的開發者工具,線程策略(ThreadPolicy)檢測的內容有
1)自定義的耗時調用 使用detectCustomSlowCalls()開啓
2)磁盤讀取操做 使用detectDiskReads()開啓
3)磁盤寫入操做 使用detectDiskWrites()開啓
4)網絡操做 使用detectNetwork()開啓
複製代碼
嚴格模式的開啓能夠放在Application或者Activity以及其餘組件的onCreate方法。爲了更好地分析應用中的問題,建議放在Application的onCreate方法中:
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()//開啓全部的detectXX系列方法
.penaltyDialog()//彈出違規提示框
.penaltyLog()//在Logcat中打印違規日誌
.build());
複製代碼
setThreadPolicy()將對當前線程應用該策略。若是不指定檢測函數,也能夠用detectAll()來替代。penaltyLog()表示將警告輸出到LogCat,你也可使用其餘或增長新的懲罰(penalty)函數,例如使用penaltyDeath()的話,一旦StrictMode消息被寫到LogCat後應用就會崩潰。另外虛擬機策略(VmPolicy)不能經過一個對話框提供警告。
在線程策略(ThreadPolicy)檢測的時候,有幾個penalty系列方法。
1)penaltyDeath(),當觸發違規條件時,直接Crash掉當前應用程序。
2)penaltyDeathOnNetwork(),當觸發網絡違規時,Crash掉當前應用程序。
3)penaltyDialog(),觸發違規時,顯示對違規信息對話框。
4)penaltyFlashScreen(),會形成屏幕閃爍,不過通常的設備可能沒有這個功能
複製代碼
四、使用Android Systrace
Systrace 容許在系統級別收集和檢查設備上運行的全部進程的計時信息。 它未來自Android內核的數據(例如CPU調度程序,磁盤活動和應用程序線程)組合起來,以生成HTML報告。若是想分析Android系統或者app的問題,首先咱們須要抓取Systrace文件分析並找出引發系統卡頓,或者app反應慢的緣由,而後在源碼上解決引發慢的問題。首先連接手機,打開Android Device Monitor,選擇要分析的進程,點擊Capture system wide trace using Android。
根據不一樣的需求,配置抓取不一樣的trace 時間(時間請勿過長,不然會致使抓取內容部分丟失),內容等,而後點擊OK,操做要分析系統卡頓或app運行緩慢的部分,系統會自動收集運行時的信息,而後用Chrome 瀏覽器打開生成的trace 文件 : 抓取的Trace報告提供了Android系統進程在特定時間段內的總體狀況。 它檢查捕獲的跟蹤信息,並突出顯示其檢查到的問題,裏面會包含每一個CPU,以及圖形渲染,輸入事件等等內容,例如在顯示運動或動畫時UI粗糙,並提供關於如何解決這些問題的建議。 可是,systrace不會在應用程序進程中收集有關代碼執行的信息。 有關您的應用程序執行哪些方法以及使用多少CPU資源的更多詳細信息,請使用Android Studio的內置Profiler分析器,或生成跟蹤日誌並使用Traceview查看它們。一、頁面佈局:
1)當根佈局文件最外層標籤爲FrameLayout以及使用include標籤引用的佈局文件的最外層標籤
能夠用merge標籤來替換,減小一層佈局深度。
2)對於頁面中不須要當即展現的部件使用ViewStub實現延遲加載的方式
3)合理運用LinearLayout和RelativeLayout,減小布局嵌套,推薦使用ConstraintLayout
4)當時用自定義控件時,onDraw()中及不要作耗時操做
複製代碼
二、應用初始化:
在大部分app開發中,咱們都會去重寫Application類,而後在onCreate()裏進行一些初始化操做,好比建立一些全局單例,第三方SDK以及一些功能模塊的初始化等,若是初始化函數耗時過長,應該放到線程池或者intentService中去操做;涉及到數據庫和文件等I/O操做,放到真正須要的地方再執行。一樣在MainActivity的onCreate()函數中也要注意上述問題,由於只有當Activity生命週期執行到onResume()函數的時候界面纔會真正顯示出來,onCreate()函數執行時間越長,界面等待顯示的時間越長,若是有些初始化流程必需要放在onCreate()當中,可使用DelayLoad的機制:
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
myHandler.post(mLoadingRunnable);
}
});
複製代碼
ViewRootImpl 的 performTraversals 方法是一個很核心的方法,每一幀繪製都會走一遍,調用各類 measure / layout / draw 等 ,最終將要顯示的數據交給 hwui 去進行繪製。Activity 在啓動時,會在第二次執行 performTraversals 纔會去真正的繪製,緣由在於第一次執行 performTraversals 的時候,會走到 Egl 初始化的邏輯,而後會從新執行一次 performTraversals 。在 onCreate 中 Post 的 runnable 對象,在第一個 performTraversals 方法執行的時候被調用,mLoadingRunnable在界面真正繪製的時候纔會執行。