一,基礎部分
1,四大組件是什麼
1)Activity:用戶可操作的可視化界面,爲用戶提供一個完成操作指令的窗口。一個Activity通常是一個單獨的屏幕,Activity通過Intent來進行通信。Android中會維持一個Activity Stack,當一個新Activity創建時,它就會放到棧頂,這個Activity就處於運行狀態。
2)Service:服務,運行在手機後臺,適合執行不需和用戶交互且還需長期運行的任務。
3)ContentProvider:內容提供者,使一個應用程序的指定數據集提供給其他應用程序,其他應用可通過ContentResolver類從該內容提供者中獲取或存入數據。它提供了一種跨進程數據共享的方式,當數據被修改後,ContentResolver接口的notifyChange函數通知那些註冊監控特定URI的ContentObserver對象。
如果ContentProvider和調用者在同一進程中,ContentProvider的方法(query/insert/update/delete等)和調用者在同一線程中;如果ContentProvider和調用者不在同一進程,ContentProvider方法會運行在它自身進程的一個Binder線程中。
4)Broadcast Receiver: 廣播接收者,運用在應用程序間傳輸信息,可以使用廣播接收器來讓應用對一個外部事件做出響應。
2,四大組件的生命週期和簡單用法
1)Activity:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
onCreate():爲Activity設置佈局,此時界面還不可見;
onStart(): Activity可見但還不能與用戶交互,不能獲得焦點
onRestart(): 重新啓動Activity時被回調
onResume(): Activity可見且可與用戶進行交互
onPause(): 當前Activity暫停,不可與用戶交互,但還可見。在新Activity啓動前被系統調用保存現有的Activity中的持久數據、停止動畫等。
onStop(): 當Activity被新的Activity覆蓋不可見時被系統調用
onDestory(): 當Activity被系統銷燬殺掉或是由於內存不足時調用
2)Service
a) onBind方式綁定的:onCreate->onBind->onUnBind->onDestory(不管調用bindService幾次,onCreate只會調用一次,onStart不會被調用,建立連接後,service會一直運行,直到調用unBindService或是之前調用的bindService的Context不存在了,系統會自動停止Service,對應的onDestory會被調用)
b) startService啓動的:onCreate->onStartCommand->onDestory(start多次,onCreate只會被調用一次,onStart會調用多次,該service會在後臺運行,直至被調用stopService或是stopSelf)
c) 又被啓動又被綁定的服務,不管如何調用onCreate()只被調用一次,startService調用多少次,onStart就會被調用多少次,而unbindService不會停止服務,必須調用stopService或是stopSelf來停止服務。必須unbindService和stopService(stopSelf)同時都調用了纔會停止服務。
3)BroadcastReceiver
a) 動態註冊:存活週期是在Context.registerReceiver和Context.unregisterReceiver之間,BroadcastReceiver每次收到廣播都是使用註冊傳入的對象處理的。
b) 靜態註冊:進程在的情況下,receiver會正常收到廣播,調用onReceive方法;生命週期只存活在onReceive函數中,此方法結束,BroadcastReceiver就銷燬了。onReceive()只有十幾秒存活時間,在onReceive()內操作超過10S,就會報ANR。
進程不存在的情況,廣播相應的進程會被拉活,Application.onCreate會被調用,再調用onReceive。
4)ContentProvider:應該和應用的生命週期一樣,它屬於系統應用,應用啓動時,它會跟着初始化,應用關閉或被殺,它會跟着結束。
3,Android 進程通訊方式
- bundle :
由於Activity,Service,Receiver都是可以通過Intent來攜帶Bundle傳輸數據的,所以我們可以在一個進程中通過Intent將攜帶數據的Bundle發送到另一個進程的組件。(bundle只能傳遞三種類型,一是鍵值對的形式,二是鍵爲String類型,三是值爲Parcelable類型) - ContentProvider
:ContentProvider是Android四大組件之一,以表格的方式來儲存數據,提供給外界,即Content
Provider可以跨進程訪問其他應用程序中的數據 - 文件
:兩個進程可以到同一個文件去交換數據,我們不僅可以保存文本文件,還可以將對象持久化到文件,從另一個文件恢復。要注意的是,當併發讀/寫時可能會出現併發的問題。 - Broadcast :Broadcast可以向android系統中所有應用程序發送廣播,而需要跨進程通訊的應用程序可以監聽這些廣播。
- AIDL :AIDL通過定義服務端暴露的接口,以提供給客戶端來調用,AIDL使服務器可以並行處理。
- Messager :Messenger封裝了AIDL之後只能串行運行,所以Messenger一般用作消息傳遞
- Socket通信
4,Activity各種情況下的生命週期
1)兩個Activity(A->B)切換(B正常的Activity)的生命週期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)->oStop(A)
這時如果按回退鍵回退到A onPause(B)->onRestart(A)->onStart(A)->onResume(A)->oStop(B)
如果在切換到B後調用了A.finish(),則會走到onDestory(A),這時點回退鍵會退出應用
2) 兩個Activity(A->B)切換(B透明主題的Activity或是Dialog風格的Acivity)的生命週期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)
這時如果回退到A onPause(B)->onResume(A)->oStop(B)->onDestory(B)
3) Activity(A)啓動後點擊Home鍵再回到應用的生命週期:onPause(A)->oStop(A)->onRestart(A)->onStart(A)->onResume(A)
5,橫豎屏切換的時候,Activity 各種情況下的生命週期
1)切換橫屏時:onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume
2) 切換豎屏時:會打印兩次相同的log
onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume->onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume
3)如果在AndroidMainfest.xml中修改該Activity的屬性,添加android:configChanges=“orientation”
橫豎屏切換,打印的log一樣,同1)
4)如果AndroidMainfest.xml中該Activity中的android:configChanges=“orientation|keyboardHidden”,則只會打印onConfigurationChanged
6,Activity與Fragment之間生命週期比較
Fragment生命週期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
切換到該Fragment:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume
按下Power鍵:onPause->onSaveInstanceState->onStop
點亮屏幕解鎖:onStart->onRestoreInstanceState->onResume
切換到其他Fragment: onPause->onStop->onDestoryView
切回到該Fragment: onCreateView->onActivityCreated->onStart->onResume
退出應用:onPause->onStop->onDestoryView->onDestory->onDetach
7,Activity上有Dialog的時候按Home鍵時的生命週期
AlertDialog並不會影響Activity的生命週期,按Home鍵後纔會使Activity onPause->onStop,AlertDialog只是一個組件,並不會使Activity進入後臺。
8,兩個Activity 之間跳轉時必然會執行的是哪幾個方法
前一個Activity的onPause,後一個Activity的onResume
9,前臺切換後臺,再回到前臺,Activity生命週期回調方法。彈出Dialog,生命值週期回調方法。
1)前臺切換到後臺,會執行onPause->onStop,再回到前臺,會執行onRestart->onStart->onResume
2)彈出Dialog,並不會影響Activity生命週期。
10,Activity的四種啓動模式對比
1)standard:標準啓動模式(默認),每啓動一次Activity,都會創建一個實例,即使從ActivityA startActivity ActivityA,也會再次創建A的實例放於棧頂,當回退時,回到上一個ActivityA的實例。
2)singleTop:棧頂複用模式,每次啓動Activity,如果待啓動的Activity位於棧頂,則不會重新創建Activity的實例,即不會走onCreate->onStart,會直接進入Activity onPause->onNewIntent->onResume方法
3)singleInstance: 單一實例模式,整個手機操作系統裏只有一個該Activity實例存在,沒有其他Actvity,後續請求均不會創建新的Activity。若task中存在實例,執行實例的onNewIntent()。應用場景:鬧鐘、瀏覽器、電話。
4)singleTask:棧內複用,啓動的Activity如果在指定的taskAffinity的task棧中存在相應的實例,則會把它上面的Activity都出棧,直到當前Activity實例位於棧頂,執行相應的onNewIntent()方法。如果指定的task不存在,創建指定的taskAffinity的task,taskAffinity的作用,進入指寫taskAffinity的task,如果指定的task存在,將task移到前臺,如果指定的task不存在,創建指定的taskAffinity的task. 應用場景:應用的主頁面。
11,Activity狀態保存於恢復
Activity被主動回收時,如按下Back鍵,系統不會保存它的狀態,只有被動回收時,雖然這個Activity實例已被銷燬,但系統在新建一個Activity實例時,會帶上先前被回收Activity的信息。在當前Activity被銷燬前調用onSaveInstanceState(onPause和onStop之間保存),重新創建Activity後會在onCreate後調用onRestoreInstanceState(onStart和onResume之間被調用),它們的參數Bundle用來數據保存和讀取的。保存View狀態有兩個前提:View的子類必須實現了onSaveInstanceState; 必須要設定Id,這個ID作爲Bundle的Key
12,fragment各種情況下的生命週期
正常情況下的生命週期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
1)Fragment在Activity中replace onPause(舊)->onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onStop(舊)->onDestoryView(舊)
2)如果添加到backStack中,調用remove()方法fragment的方法會走到onDestoryView,但不會執行onDetach(),即fragment本身的實例是存在的,成員變量也存在,但是view被銷燬了。如果新替換的Fragment已在BackStack中,則不會執行onAttach->onCreate
13,說說ContentProvider、ContentResolver、ContentObserver 之間的關係
ContentProvider實現各個應用程序間數據共享,用來提供內容給別的應用操作。如聯繫人應用中就使用了ContentProvider,可以在自己應用中讀取和修改聯繫人信息,不過需要獲取相應的權限。它也只是一箇中間件,真正的數據源是文件或SQLite等。
ContentResolver內容解析者,用於獲取內容提供者提供的數據,通過ContentResolver.notifyChange(uri)發出消息
ContentObserver內容監聽者,可以監聽數據的改變狀態,觀察特定Uri引起的數據庫變化,繼而做一些相應的處理,類似於數據庫中的觸發器,當ContentObserver所觀察的Uri發生變化時,便會觸發它。
14,AlertDialog,popupWindow,Activity區別
1)Popupwindow在顯示之前一定要設置寬高,Dialog無此限制。
2)Popupwindow默認不會響應物理鍵盤的back,除非顯示設置了popup.setFocusable(true);而在點擊back的時候,Dialog會消失。
3)Popupwindow不會給頁面其他的部分添加蒙層,而Dialog會。
4)Popupwindow沒有標題,Dialog默認有標題,可以通過dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)取消標題
5)二者顯示的時候都要設置Gravity。如果不設置,Dialog默認是Gravity.CENTER。
6)二者都有默認的背景,都可以通過setBackgroundDrawable(new ColorDrawable(android.R.color.transparent))去掉。
7)Popupwindow彈出後,取得了用戶操作的響應處理權限,使得其他UI控件不被觸發。而AlertDialog彈出後,點擊背景,AlertDialog會消失。
二,高級部分
1,Activity啓動流程(從Launcher開始)
①,第一階段: Launcher通知AMS要啓動新的Activity(在Launcher所在的進程執行)
- Launcher.startActivitySafely //首先Launcher發起啓動Activity的請求
- Activity.startActivity
- Activity.startActivityForResult
- Instrumentation.execStartActivity //交由Instrumentation代爲發起請求
- ActivityManager.getService().startActivity //通過IActivityManagerSingleton.get()得到一個AMP代理對象
- ActivityManagerProxy.startActivity //通過AMP代理通知AMS啓動activity
②,第二階段:AMS先校驗一下Activity的正確性,如果正確的話,會暫存一下Activity的信息。然後AMS會通知Launcher程序pause Activity(在AMS所在進程執行)
- ActivityManagerService.startActivity
- ActivityManagerService.startActivityAsUser
- ActivityStackSupervisor.startActivityMayWait
- ActivityStackSupervisor.startActivityLocked // 檢查有沒有在AndroidManifest中註冊
- ActivityStackSupervisor.startActivityUncheckedLocked
- ActivityStack.startActivityLocked // 判斷是否需要創建一個新的任務來啓動Activity。
- ActivityStack.resumeTopActivityLocked // 獲取棧頂的activity,並通知Launcher應該pause掉這個Activity以便啓動新的activity。
- ActivityStack.startPausingLocked
- ApplicationThreadProxy.schedulePauseActivity
③,pause Launcher的Activity,並通知AMS已經paused(在Launcher所在進程執行)
- ApplicationThread.schedulePauseActivity
- ActivityThread.queueOrSendMessage
- H.handleMessage
- ActivityThread.handlePauseActivity
- ActivityManagerProxy.activityPaused
④,檢查activity所在進程是否存在,如果存在,就直接通知這個進程,在該進程中啓動Activity;不存在的話,會調用Process.start創建一個新進程(執行在AMS進程)
- ActivityManagerService.activityPaused
- ActivityStack.activityPaused
- ActivityStack.completePauseLocked
- ActivityStack.resumeTopActivityLocked
- ActivityStack.startSpecificActivityLocked
- ActivityManagerService.startProcessLocked
- Process.start // 在這裏創建了新進程,新的進程會導入ActivityThread類,並執行它的main函數。
⑤,創建ActivityThread實例,執行一些初始化操作,並綁定Application。如果Application不存在,會調用LoadedApk.makeApplication創建一個新的Application對象。之後進入Loop循環。(執行在新創建的app進程)
- ActivityThread.main
- ActivityThread.attach(false) //聲明不是系統進程
- ActivityManagerProxy.attachApplication
⑥,處理新的應用進程發出的創建進程完成的通信請求,並通知新應用程序進程啓動目標Activity組件(執行在AMS進程)
- ActivityManagerService.attachApplication // AMS綁定本地ApplicationThread對象,後續通過ApplicationThreadProxy來通信。
- ActivityManagerService.attachApplicationLocked
- ActivityStack.realStartActivityLocked //真正要啓動Activity了!
- ApplicationThreadProxy.scheduleLaunchActivity //AMS通過ATP通知app進程啓動Activity
⑦,加載MainActivity類,調用onCreate聲明週期方法(執行在新啓動的app進程)
- ApplicationThread.scheduleLaunchActivity //ApplicationThread發消息給AT
- ActivityThread.queueOrSendMessage
- H.handleMessage //AT的Handler來處理接收到的LAUNCH_ACTIVITY的消息
- ActivityThread.handleLaunchActivity
- ActivityThread.performLaunchActivity
- Instrumentation.newActivity //調用Instrumentation類來新建一個Activity對象
- Instrumentation.callActivityOnCreate
- MainActivity.onCreate
- ActivityThread.handleResumeActivity
- AMP.activityResumed
- AMS.activityResumed(AMS進程)
2,進程保活
此處延伸:進程的優先級是什麼
當前業界的Android進程保活手段主要分爲** 黑、白、灰 **三種,其大致的實現思路如下:
黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)
白色保活:啓動前臺Service
灰色保活:利用系統的漏洞啓動前臺Service
黑色保活
所謂黑色保活,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:
場景1:開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app
場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3
場景3:假如你手機裏裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差不多)
白色保活
白色保活手段非常簡單,就是調用系統api啓動一個前臺的Service進程,這樣會在系統的通知欄生成一個Notification,用來讓用戶知道有這樣一個app在運行着,哪怕當前的app退到了後臺。如下方的LBE和QQ音樂這樣:
灰色保活
灰色保活,這種保活手段是應用範圍最廣泛。它是利用系統的漏洞來啓動一個前臺的Service進程,與普通的啓動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行着一個後臺Service進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行着一個前臺進程(因爲看不到Notification),但你的進程優先級又是高於普通後臺進程的。那麼如何利用系統的漏洞呢,大致的實現思路和代碼如下:
思路一:API < 18,啓動前臺Service時直接傳入new Notification();
思路二:API >= 18,同時啓動兩個id相同的前臺Service,然後再將後啓動的Service做stop處理
熟悉Android系統的童鞋都知道,系統出於體驗和性能上的考慮,app在退到後臺時系統並不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,後臺緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app。這套殺進程回收內存的機制就叫 Low Memory Killer ,它是基於Linux內核的 OOM Killer(Out-Of-Memory killer)機制誕生。
進程的重要性,劃分5級:
前臺進程 (Foreground process)
可見進程 (Visible process)
服務進程 (Service process)
後臺進程 (Background process)
空進程 (Empty process)
瞭解完 Low Memory Killer,再科普一下oom_adj。什麼是oom_adj?它是linux內核分配給每個系統進程的一個值,代表進程的優先級,進程回收機制就是根據這個優先級來決定是否進行回收。對於oom_adj的作用,你只需要記住以下幾點即可:
進程的oom_adj越大,表示此進程優先級越低,越容易被殺回收;越小,表示進程優先級越高,越不容易被殺回收
普通app進程的oom_adj>=0,系統進程的oom_adj纔可能<0
有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、陌陌都在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運,爲了儘量避免被殺,還是老老實實去做好優化工作吧。
所以,進程保活的根本方案終究還是回到了性能優化上,進程永生不死終究是個徹頭徹尾的僞命題!
3,View,ViewGroup事件分發
1), Touch事件分發中只有兩個主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。
2),ViewGroup和View組成了一個樹狀結構,根節點爲Activity內部包含的一個ViwGroup。
3),觸摸事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸摸事件中,Down和Up都只有一個,Move有若干個,可以爲0個。
4),當Acitivty接收到Touch事件時,將遍歷子View進行Down事件的分發。ViewGroup的遍歷可以看成是遞歸的。分發的目的是爲了找到真正要處理本次完整觸摸事件的View,這個View會在onTouchuEvent結果返回true。
5),當某個子View返回true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進行處理。由於子View是保存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup保存的會是真實處理事件的View所在的ViewGroup對象:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將被保存在ViewGroup1中,而ViewGroup1也會返回true,被保存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。
6),當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是調用super.dispatchTouchEvent函數,即父類View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。
7),onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕獲Up和Move事件。
4,AIDL理解
AIDL: 每一個進程都有自己的Dalvik VM實例,都有自己的一塊獨立的內存,都在自己的內存上存儲自己的數據,執行着自己的操作,都在自己的那片狹小的空間裏過完自己的一生。而aidl就類似與兩個進程之間的橋樑,使得兩個進程之間可以進行數據的傳輸,跨進程通信有多種選擇,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 佔用的系統資源比較多,如果頻繁的跨進程通信顯然是不可取的;Messenger 進行跨進程通信時請求隊列是同步進行的,無法併發執行。
Binder機制簡單理解:
在Android系統的Binder機制中,是有Client,Service,ServiceManager,Binder驅動程序組成的,其中Client,service,Service Manager運行在用戶空間,Binder驅動程序是運行在內核空間的。而Binder就是把這4種組件粘合在一塊的粘合劑,其中核心的組件就是Binder驅動程序,Service Manager提供輔助管理的功能,而Client和Service正是在Binder驅動程序和Service Manager提供的基礎設施上實現C/S 之間的通信。其中Binder驅動程序提供設備文件/dev/binder與用戶控件進行交互,
Client、Service,Service Manager通過open和ioctl文件操作相應的方法與Binder驅動程序進行通信。而Client和Service之間的進程間通信是通過Binder驅動程序間接實現的。而Binder Manager是一個守護進程,用來管理Service,並向Client提供查詢Service接口的能力。