Activity是什么
Activity是四大組件之一,它提供一個界面讓用戶點擊和各種滑動操作
Activity棧先進后出
四種狀態(tài):Running狀態(tài):一個新的Activity啟動入棧后,它在屏幕最前端,處于棧的最頂端,此時它處于可見并可和用戶交互的激活狀態(tài)。
Paused狀態(tài):當(dāng)Activity被另一個透明或者Dialog樣式的Activity覆蓋時的狀態(tài)。此時它依然與窗口管理器保持連接,系統(tǒng)繼續(xù)維護其內(nèi)部狀態(tài),它仍然可見,但它已經(jīng)失去了焦點,故不可與用戶交互。
Stopped狀態(tài):當(dāng)Activity不可見時,Activity處于Stopped狀態(tài)。當(dāng)Activity處于此狀態(tài)時,一定要保存當(dāng)前數(shù)據(jù)和當(dāng)前的UI狀態(tài),否則一旦Activity退出或關(guān)閉時,當(dāng)前的數(shù)據(jù)和UI狀態(tài)就丟失了。
Killed狀態(tài):Activity被殺掉以后或者被啟動以前,處于Killed狀態(tài)。這是Activity已從Activity堆棧中移除,需要重新啟動才可以顯示和使用。
生命周期:onCreate(),onStart(),onResume(),onPause(),onStop(),onDestroy(),onRestart()
啟動模式:standard ??默認模式,可以不用寫配置。在這個模式下,都會默認創(chuàng)建一個新的實例。
? ? ? ? ? ? ? ? ?singleTop ?可以有多個實例,但是不允許多個相同Activity疊加。
? ? ? ? ? ? ? ? ?singleTask 只有一個實例。
? ? ? ? ? ? ? ? ?singleinstance? 只有一個實例,并且這個實例獨立運行在一個task中
Context、Activity、Application之間有什么區(qū)別?
Activity和Application都是Context的子類。Context從字面上理解就是上下文的意思,在實際應(yīng)用中它也確實是起到了管理上下文環(huán)境中各個參數(shù)和變量的總用,方便我們可以簡單的訪問到各種資源。雖然Activity和Application都是Context的子類,但是他們維護的生命周期不一樣。前者維護一個Acitivity的生命周期,所以其對應(yīng)的Context也只能訪問該activity內(nèi)的各種資源。后者則是維護一個Application的生命周期
進程的優(yōu)先級 :前臺進程,可見進程,服務(wù)進程,后臺進程,空進程
Fragment
1、Fragment為什么被稱為第五大組件
Fragment比Activity更節(jié)省內(nèi)存,其切換模式也更加舒適,使用頻率不低于四大組件,且有自己的生命周期,而且必須依附于Activity
2、Activity創(chuàng)建Fragment的方式
靜態(tài)創(chuàng)建
動態(tài)創(chuàng)建
3、FragmentPageAdapter和FragmentPageStateAdapter的區(qū)別
FragmentPageAdapter在每次切換頁面的的時候,是將Fragment進行分離,適合頁面較少的Fragment使用以保存一些內(nèi)存,對系統(tǒng)內(nèi)存不會多大影響
FragmentPageStateAdapter在每次切換頁面的時候,是將Fragment進行回收,適合頁面較多的Fragment使用,這樣就不會消耗更多的內(nèi)存
4、Fragment生命周期
onAttach()
onCreate()
onCreateView()
onActivityCreated()
onStart()
onResume()
onPause()
onStop()
onDestroyView()
onDestroy()
onDetach()
5、Fragment的通信
Fragment調(diào)用Activity中的方法:getActivity
Activity調(diào)用Fragment中的方法:接口回調(diào)
Fragment調(diào)用Fragment中的方法:FragmentManager.findFragmentById
6、Fragment的replace、add、remove方法
replace:替代Fragment的棧頂頁面
add:添加Fragment到棧頂頁面
remove:移除Fragment棧頂頁面
Service
1、Service是什么
Service是四大組件之一,它可以在后臺執(zhí)行長時間運行操作而沒有用戶界面的應(yīng)用組件
2、Service和Thread的區(qū)別
Service是安卓中系統(tǒng)的組件,它運行在獨立進程的主線程中,不可以執(zhí)行耗時操作。Thread是程序執(zhí)行的最小單元,分配CPU的基本單位,可以開啟子線程執(zhí)行耗時操作
Service在不同Activity中可以獲取自身實例,可以方便的對Service進行操作。Thread在不同的Activity中難以獲取自身實例,如果Activity被銷毀,Thread實例就很難再獲取得到
3、Service啟動方式
startService
bindService
4、Service生命周期
startService
onCreate()
onStartCommand()
onDestroy()
bindService
onCreate()
onBind()
onUnbind()
onDestroy()
Broadcast Receiver
1、Broadcast Receiver是什么
Broadcast是四大組件之一,是一種廣泛運用在應(yīng)用程序之間傳輸信息的機制,通過發(fā)送Intent來傳送我們的數(shù)據(jù)
2、Broadcast Receiver的使用場景
同一App具有多個進程的不同組件之間的消息通信
不同App之間的組件之間的消息通信
3、Broadcast Receiver的種類
普通廣播
有序廣播
本地廣播
Sticky廣播
4、Broadcast Receiver的實現(xiàn)
靜態(tài)注冊:注冊后一直運行,盡管Activity、進程、App被殺死還是可以接收到廣播
動態(tài)注冊:跟隨Activity的生命周期
5、Broadcast Receiver實現(xiàn)機制
自定義廣播類繼承BroadcastReceiver,復(fù)寫onReceiver()
通過Binder機制向AMS進行注冊廣播
廣播發(fā)送者通過Binder機制向AMS發(fā)送廣播
AMS查找符合相應(yīng)條件的廣播發(fā)送到BroadcastReceiver相應(yīng)的循環(huán)隊列中
消息隊列執(zhí)行拿到廣播,回調(diào)BroadcastReceiver的onReceiver()
6、LocalBroadcastManager特點
本地廣播只能在自身App內(nèi)傳播,不必擔(dān)心泄漏隱私數(shù)據(jù)
本地廣播不允許其他App對你的App發(fā)送該廣播,不必擔(dān)心安全漏洞被利用
本地廣播比全局廣播更高效
以上三點都是源于其內(nèi)部是用Handler實現(xiàn)的
Handler
1、Handler是什么
Handler通過發(fā)送和處理Message和Runnable對象來關(guān)聯(lián)相對應(yīng)線程的MessageQueue
2、Handler使用方法
post(runnable)
sendMessage(message)
3、Handler工作原理
Android消息機制:Android規(guī)定了只允許UI線程修改Activity里的UI組件,在子線程中修改Activity里的UI組件,會導(dǎo)致UI操作的線程不安全,并報出錯誤。為了保證Android的UI操作是線程安全的,Android提供了Handler消息傳遞機制來解決這個問題
相關(guān)概念:
主線程(UI線程)
定義:當(dāng)程序第一次啟動時,Android會同時啟動一條主線程(Main Thread)
作用:主線程主要負責(zé)處理與UI相關(guān)的事件
Message(消息)
定義:Handler接收和處理的消息對象(Bean對象)
作用:通信時相關(guān)信息的存放和傳遞
ThreadLocal
定義:線程內(nèi)部的數(shù)據(jù)存儲類
作用:負責(zé)存儲和獲取本線程的Looper
Message Queue(消息隊列)
定義:采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息列表
作用:用來存放通過Handler發(fā)過來的Message,按照先進先出執(zhí)行
Handler(處理者)
定義:Message的主要處理者
作用:負責(zé)發(fā)送Message到消息隊列&處理Looper分派過來的Message
Looper(循環(huán)器)
定義:扮演Message Queue和Handler之間橋梁的角色
作用:
消息循環(huán):循環(huán)取出Message Queue的Message
消息派發(fā):將取出的Message交付給相應(yīng)的Handler
關(guān)系:Looper中存放有MessageQueen,MessageQueen中又有很多Message,當(dāng)我們的Handler發(fā)送消息的時候,會獲取當(dāng)前的Looper,并在當(dāng)前的Looper的MessageQueen當(dāng)中存放我們發(fā)送的消息,而我們的MessageQueen也會在Looper的帶動下,一直循環(huán)的讀取Message信息,并將Message信息發(fā)送給Handler,并執(zhí)行HandlerMessage()方法
Looper初始化:
Looper的創(chuàng)建會關(guān)聯(lián)一個MessageQueen的創(chuàng)建
Looper對象只能被創(chuàng)建一次
Looper對象創(chuàng)建后被存放在sThreadLocal中
Looper的循環(huán)過程:
取出Looper和MessageQueen
進入消息循環(huán),有消息則分發(fā)出去
消息資源的回收
Looper中最為重要的方法:
Looper.prepareMainLooper():該方法是Looper對象的初始化
Looper.loop():該方法會循環(huán)取出Message Queue的Message,將取出的Message交付給相應(yīng)的Handler(Looper的作用就體現(xiàn)在這里)
quit():quit會直接退出Looper
quitSafety():quitSafety只是設(shè)定一個退出標(biāo)記,然后把消息隊列中的已有消息處理完畢后退出Looper
MessageQueen:在MessageQueen中會使用enqueueMessage()方法存儲Message,若是在中間插入,則根據(jù)Message創(chuàng)建的時間進行插入,用MessageQueen里面的next()方法拿出
Message就是用來存儲Message中各種信息的Bean對象
Handler發(fā)送消息:
第一種方式:sendMessage(Message msg)? sendMessage()方法的處理方法就是執(zhí)行handleMessage()空方法
第二種方式:post(Ruunable r)? post()方法的處理方法就是將傳進來的Runnable執(zhí)行run()方法
4、Handler引起的內(nèi)存泄漏
原因:非靜態(tài)內(nèi)部類持有外部類的匿名引用,導(dǎo)致Activity無法釋放
解決:
Handler內(nèi)部持有外部Activity的弱引用
Handler改為靜態(tài)內(nèi)部類
Handler.removeCallback()
請解釋下Android通信機制中Message、Handler、MessageQueen、Looper的之間的關(guān)系?
首先,是這個MessageQueen,MessageQueen是一個消息隊列,它可以存儲Handler發(fā)送過來的消息,其內(nèi)部提供了進隊和出隊的方法來管理這個消息隊列,其出隊和進隊的原理是采用單鏈表的數(shù)據(jù)結(jié)構(gòu)進行插入和刪除的,即enqueueMessage()方法和next()方法。這里提到的Message,其實就是一個Bean對象,里面的屬性用來記錄Message的各種信息。
然后,是這個Looper,Looper是一個循環(huán)器,它可以循環(huán)的取出MessageQueen中的Message,其內(nèi)部提供了Looper的初始化和循環(huán)出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper會關(guān)聯(lián)一個MessageQueen,而且將Looper存進一個ThreadLocal中,在loop()方法中,通過ThreadLocal取出Looper,使用MessageQueen的next()方法取出Message后,判斷Message是否為空,如果是則Looper阻塞,如果不是,則通過dispatchMessage()方法分發(fā)該Message到Handler中,而Handler執(zhí)行handlerMessage()方法,由于handlerMessage()方法是個空方法,這也是為什么需要在Handler中重寫handlerMessage()方法的原因。這里要注意的是Looper只能在一個線程中只能存在一個。這里提到的ThreadLocal,其實就是一個對象,用來在不同線程中存放對應(yīng)線程的Looper。
最后,是這個Handler,Handler是Looper和MessageQueen的橋梁,Handler內(nèi)部提供了發(fā)送Message的一系列方法,最終會通過MessageQueen的enqueueMessage()方法將Message存進MessageQueen中。我們平時可以直接在主線程中使用Handler,那是因為在應(yīng)用程序啟動時,在入口的main方法中已經(jīng)默認為我們創(chuàng)建好了Looper。
AsyncTask
AsyncTask是什么:
AsyncTask是一種輕量級的異步任務(wù)類,它可以在線程池中執(zhí)行后臺任務(wù),然后把執(zhí)行的進度和最終結(jié)果傳遞給主線程并主線程中更新UI,通過AsyncTask可以更加方便執(zhí)行后臺任務(wù)以及在主線程中訪問UI,但是AsyncTask并不適合進行特別耗時的后臺任務(wù),對于特別耗時的任務(wù)來說,建議使用線程池
參數(shù):
Params:表示后臺任務(wù)執(zhí)行時的參數(shù)類型(對應(yīng)例子中的URL),該參數(shù)會傳給AysncTask的doInBackground()方法
Progress:表示后臺任務(wù)的執(zhí)行進度的參數(shù)類型(對應(yīng)例子中的Integer),該參數(shù)會作為onProgressUpdate()方法的參數(shù)
Result:表示后臺任務(wù)的返回結(jié)果的參數(shù)類型(對應(yīng)例子中的Long),該參數(shù)會作為onPostExecute()方法的參數(shù)
常用的AsyncTask繼承的方法
onPreExecute():異步任務(wù)開啟之前回調(diào),在主線程中執(zhí)行
doInBackground():執(zhí)行異步任務(wù),在線程池中執(zhí)行
onProgressUpdate():當(dāng)doInBackground中調(diào)用publishProgress時回調(diào),在主線程中執(zhí)行
onPostExecute():在異步任務(wù)執(zhí)行之后回調(diào),在主線程中執(zhí)行
onCancelled():在異步任務(wù)被取消時回調(diào)
主分支
首先,execute()方法,開啟異步任務(wù)
接著,onPreExecute()方法,異步任務(wù)開啟前
接著,doInBackground()方法,異步任務(wù)正在執(zhí)行
最后,onPostExecute()方法,異步任務(wù)完成
次分支
onProgressUpdate()方法,異步任務(wù)更新UI
onCancelled()方法,異步任務(wù)取消
AsyncTask引起的內(nèi)存泄漏
原因:非靜態(tài)內(nèi)部類持有外部類的匿名引用,導(dǎo)致Activity無法釋放
解決:
AsyncTask內(nèi)部持有外部Activity的弱引用
AsyncTask改為靜態(tài)內(nèi)部類
AsyncTask.cancel()
5、AsyncTask生命周期
在Activity銷毀之前,取消AsyncTask的運行,以此來保證程序的穩(wěn)定
6、AsyncTask結(jié)果丟失
由于屏幕旋轉(zhuǎn)、Activity在內(nèi)存緊張時被回收等情況下,Activity會被重新創(chuàng)建,此時,舊的AsyncTask持有舊的Activity引用,這個時候會導(dǎo)致AsyncTask的onPostExecute()對UI更新無效
7、AsyncTask并行or串行
AsyncTask在Android 2.3之前默認采用并行執(zhí)行任務(wù),AsyncTask在Android 2.3之后默認采用串行執(zhí)行任務(wù)
如果需要在Android 2.3之后采用并行執(zhí)行任務(wù),可以調(diào)用AsyncTask的executeOnExecutor()
HandlerThread
1、HandlerThread產(chǎn)生背景
當(dāng)系統(tǒng)有多個耗時任務(wù)需要執(zhí)行時,每個任務(wù)都會開啟一個新線程去執(zhí)行耗時任務(wù),這樣會導(dǎo)致系統(tǒng)多次創(chuàng)建和銷毀線程,從而影響性能。為了解決這一問題,Google提供了HandlerThread,HandlerThread是在線程中創(chuàng)建一個Looper循環(huán)器,讓Looper輪詢消息隊列,當(dāng)有耗時任務(wù)進入隊列時,則不需要開啟新線程,在原有的線程中執(zhí)行耗時任務(wù)即可,否則線程阻塞
2、HanlderThread的特點、
HandlerThread本質(zhì)上是一個線程,繼承自Thread
HandlerThread有自己的Looper對象,可以進行Looper循環(huán),可以創(chuàng)建Handler
HandlerThread可以在Handler的handlerMessage中執(zhí)行異步方法
HandlerThread優(yōu)點是異步不會堵塞,減少對性能的消耗
HandlerThread缺點是不能同時繼續(xù)進行多任務(wù)處理,需要等待進行處理,處理效率較低
HandlerThread與線程池不同,HandlerThread是一個串行隊列,背后只有一個線程
IntentService
1、IntentService是什么
IntentService是繼承自Service并處理異步請求的一個類,其內(nèi)部采用HandlerThread和Handler實現(xiàn)的,在IntentService內(nèi)有一個工作線程來處理耗時操作,其優(yōu)先級比普通Service高。當(dāng)任務(wù)完成后,IntentService會自動停止,而不需要手動調(diào)用stopSelf()。另外,可以多次啟動IntentService,每個耗時操作都會以工作隊列的方式在IntentService中onHandlerIntent()回調(diào)方法中執(zhí)行,并且每次只會執(zhí)行一個工作線程
2、IntentService使用方法
創(chuàng)建Service繼承自IntentService
覆寫構(gòu)造方法和onHandlerIntent()方法
在onHandlerIntent()中執(zhí)行耗時操作
Android事件分發(fā)機制
Android事件分發(fā)機制的發(fā)生在View與View之間或者ViewGroup與View之間具有鑲嵌的視圖上,而且視圖上必須為點擊可用。當(dāng)一個點擊事件產(chǎn)生后,它的傳遞過程遵循如下順序:Activity->Window->View,即事件先傳遞給Activity,再到Window,再到頂級View,才開始我們的事件分發(fā)
Android事件分發(fā)機制主要由三個重要的方法共同完成的
dispatchTouchEvent:用于進行點擊事件的分發(fā)
onInterceptTouchEvent:用于進行點擊事件的攔截
onTouchEvent:用于處理點擊事件
需要注意的是View中是沒有onInterceptTouchEvent()方法的
一、簡要的談?wù)凙ndroid的事件分發(fā)機制?
當(dāng)點擊事件發(fā)生時,首先Activity將TouchEvent傳遞給Window,再從Window傳遞給頂層View。TouchEvent會最先到達最頂層 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法進行分發(fā),如果dispatchTouchEvent返回true ,則交給這個view的onTouchEvent處理,如果dispatchTouchEvent返回 false ,則交給這個 view 的 interceptTouchEvent 方法來決定是否要攔截這個事件,如果 interceptTouchEvent 返回 true ,也就是攔截掉了,則交給它的 onTouchEvent 來處理,如果 interceptTouchEvent 返回 false ,那么就傳遞給子 view ,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發(fā)。如果事件傳遞到某一層的子 view 的 onTouchEvent 上了,這個方法返回了 false ,那么這個事件會從這個 view 往上傳遞,都是 onTouchEvent 來接收。而如果傳遞到最上面的 onTouchEvent 也返回 false 的話,這個事件就會“消失”,而且接收不到下一次事件。
二、為什么View有dispatchTouchEvent方法?
因為View可以注冊很多事件的監(jiān)聽器,如長按、滑動、點擊等,它也需要一個管理者來分發(fā)
三、ViewGroup中可能有很多個子View,如何判斷應(yīng)該分配給哪一個?
根據(jù)源碼可知,它會分配給在點擊范圍內(nèi)的子View
四、當(dāng)點擊時,子View重疊應(yīng)該如何分配?
一般分配給最上層的子View,這是由于安卓的渲染機制導(dǎo)致的
視圖工作機制
Android視圖工作機制按順序分為以下三步:
measure:確定View的寬高
layout:確定View的位置
draw:繪制出View的形狀
相關(guān)概念:
View(照片框):自定義View
measure(尺子):測量View大小
MeasureSpec(尺子刻度):測量View大小的測量單位
layout(照片框的位置):View的具體位置
draw(筆):繪制View
(你會發(fā)現(xiàn),現(xiàn)實中的畫圖步驟和View工作機制步驟是一樣的)
Android視圖工作機制之MeasureSpec
我們知道,自定義View第一步是測量,而測量需要測量規(guī)格(或測量標(biāo)準(zhǔn))才能知道View的寬高,所以在測量之前需要認識MeasureSpec類
MeasureSpec類是決定View的measure過程的測量規(guī)格(比喻:尺子),它由以下兩部分組成
SpecMode:測量模式(比喻:直尺、三角尺等不同類型)
SpecSize:測量模式下的規(guī)格大小(比喻:尺子的刻度)
MeasureSpec的表示形式是32位的int值
高2位(前面2位):表示測量模式,即SpecMode
低30位(后面30位):表示在測量模式下的測量規(guī)格大小,即SpecSize
(其實就是MeasureSpec通過將SpecMode和SpecSize打包成一個int值來避免過多的對象內(nèi)存分配)
SpecMode又分為三種模式:
UNSPECIFIED:父容器不對View有任何大小的限制,這種情況一般用于系統(tǒng)內(nèi)部,表示一種測量狀態(tài)
EXACTLY:父容器檢測出View所需要的精確大小,這時候View的值就是SpecSize
AT_MOST:父容器指定了一個可用大小即SpecSize,View的大小不能大于這個值
LayoutParams有三種情況:MATCH_PARENT、WARP_CONTENT、xxxdp(精確大小)
結(jié)論:子View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams來共同決定的
首先要知道LayoutParams有三種情況:MATCH_PARENT、WARP_CONTENT、100dp(精確大小)
只要子View的MeasureSpec被確定,那么就可以在measure過程中,測量出子View的寬高
通過例子來解釋:
1.假如父容器LinearLayout的MeasureSpec:EXACTLY、AT_MOST的任意一種
子View的LayoutParams:精確大小(100dp)
也就是說:子View必須是指定大小,不管父容器載不載得下子View
所以返回子View的MeasureSpec:EXACTLY
所以返回子View測量出來的大小:子View自身精確大小
2.假如父容器LinearLayout的MeasureSpec:EXACTLY、AT_MOST的任意一種
子View的LayoutParams:MATCH_PARENT
也就是說:子View必須占滿整個父容器,那么父容器多大,子View就多大
所以返回子View的MeasureSpec:跟父容器一致
所以返回子View測量出來的大小:父容器可用大小
3.假如父容器LinearLayout的MeasureSpec:EXACTLY、AT_MOST的任意一種
子View的LayoutParams:WARP_CONTENT
也就是說:子View必須自適應(yīng)父容器,父容器不管多小,你都不能超過它,只能自適應(yīng)的縮小
所以返回子View的MeasureSpec:AT_MOST(不能超過父容器本身)
所以返回子View測量出來的大小:父容器可用大小
還有第四種:父容器是UNSPECIFIED的時候,由于父容器不知道自己多大,而子View又采用MATCH_PARENT、WARP_CONTENT的時候,子View肯定也不知道自己多大,所以只有當(dāng)子View采用EXACTLY的時候,才知道自己多大
measure過程 :ViewGroup源碼中,提供了一個measureChildren的方法來遍歷調(diào)用子View的measure方法,而各個子View再遞歸去執(zhí)行這個過程
View的measure過程:View的源碼中,由于measure方法是個final類型的,所以子類不能重寫此方法,View的measure方法中,會調(diào)用自身的onMeasure方法(平時,自定義View重寫這個方法,就是對自定義的View根據(jù)自己定的規(guī)則來確定測量大小),從onMeasure方法中,有g(shù)etDefaultSize()、getSuggestedMinimumWidth()、getSuggestedMinimumHeight(),如果你自定義不重寫onMeasure()方法的話,那么系統(tǒng)就會采用默認的測量模式來確定你的測量大小,即getDefaultSize(),返回specSize,即View測量后的大,? getSuggestedMinimumWidth和getSuggestedMinimumHeight原理是一樣的,如果View沒有設(shè)置背景,那么View的寬度為mMinWidth,而mMinWidth對應(yīng)的就是android:minWidth這個屬性的值,如果這個屬性不指定,那么mMinWidth默認為0;如果指定了背景,那么View的寬度就是max(mMinWidth, mBackground.getMinimumWidth()),getMinimumWidth是在Drawable類中的,它返回的是Drawable的原始寬度,如果沒有Drawable,則返回0
如果是自定義View的話,就重寫onMeasure方法,將其默認的測量方式改為我們自己規(guī)定的測量方式,最后獲得我們的寬高
layout過程:
繪制背景:drawBackground(canvas)
繪制自己:if (!dirtyOpaque) onDraw(canvas)
繪制children:dispatchDraw(canvas)
繪制裝飾:onDrawForeground(canvas)
(我們常常就是重寫onDraw()方法來繪制我們的自定義View,否則是沒有圖像的,這點在源碼中也是提供了onDraw()的空實現(xiàn)方法給我們?nèi)ダL制圖像)
視圖工作機制中的重繪
invalidate()和requestLayout(),常用于View重繪和更新,其主要區(qū)別如下
invalidate方法只會執(zhí)行onDraw方法
requestLayout方法只會執(zhí)行onMeasure方法和onLayout方法,并不會執(zhí)行onDraw方法。
所以當(dāng)我們進行View更新時,若僅View的顯示內(nèi)容發(fā)生改變且新顯示內(nèi)容不影響View的大小、位置,則只需調(diào)用invalidate方法;若View寬高、位置發(fā)生改變且顯示內(nèi)容不變,只需調(diào)用requestLayout方法;若兩者均發(fā)生改變,則需調(diào)用兩者,按照View的繪制流程,推薦先調(diào)用requestLayout方法再調(diào)用invalidate方法
invalidate方法用于UI線程中重新繪制視圖
postInvalidate方法用于非UI線程中重新繪制視圖,省去使用handler
ListView
ListView是什么:
ListView是能將一個數(shù)據(jù)集合以動態(tài)滾動的方式展示到用戶界面上的View
ListView的優(yōu)化:
重用convertView
使用ViewHolder
圖片三級緩存
監(jiān)聽滑動事件
少用透明View
開啟硬件加速
Android項目構(gòu)建
Androd Studio等IDE則對整個過程進行了一個打包,當(dāng)我們在Run project的時候,底層的打包工具就會被調(diào)用,打包流程都會自動執(zhí)行。然后我們只需要對構(gòu)建文件按照自己的需求進行相應(yīng)的配置,就可以構(gòu)建出自己所需要的項目。
git常用命令
git init:倉庫的初始化
git status:查看當(dāng)前倉庫的狀態(tài)
git diff:查看倉庫與上次修改的內(nèi)容
git add:將文件放進暫存區(qū)
git commit:提交代碼
git clone:克隆代碼
git bransh:查看當(dāng)前分支
git checkout:切換當(dāng)前分支
4、git工作流
fork/clone(主流)
fork:將別人的倉庫代碼fork到自己的倉庫上
clone:克隆下自己倉庫的代碼
update、commit:修改代碼并提交到自己的倉庫
push:提交到自己的倉庫
pull request:請求添加到別人的倉庫
clone
proguard是什么
ProGuard工具是用于壓縮、優(yōu)化和混淆我們的代碼,其主作用是移除或混淆代碼中無用類、字段、方法和屬性
proguard技術(shù)功能:
壓縮
優(yōu)化
混淆
預(yù)檢測
proguard工作原理:
將無用的字段或方法存入到EntryPoint中,將非EntryPoint的字段和方法進行替換
為什么要混淆:
由于Java是一門跨平臺的解釋性語言,其源代碼被編譯成class字節(jié)碼來適應(yīng)其他平臺,而class文件包含了Java源代碼信息,很容易被反編譯
ANR面試題
1、什么是ANR
Application Not Responding,頁面無響應(yīng)的對話框
2、發(fā)生ANR的條件
應(yīng)用程序的響應(yīng)性是由ActivityManager和WindowManager系統(tǒng)服務(wù)監(jiān)視的,當(dāng)ANR發(fā)生條件滿足時,就會彈出ANR的對話框
Activity超過5秒無響應(yīng)
BroadcastReceiver超過10秒無響應(yīng)
Service超過20秒無響應(yīng)
3、造成ANR的主要原因
主線程被IO操作阻塞
Activity的所有生命周期回調(diào)都是執(zhí)行在主線程的
Service默認執(zhí)行在主線程中
BoardcastReceiver的回調(diào)onReceive()執(zhí)行在主線程中
AsyncTask的回調(diào)除了doInBackground,其他都是在主線程中
沒有使用子線程Looper的Handler的handlerMessage,post(Runnable)都是執(zhí)行在主線程中
4、如何解決ANR
使用AsyncTask處理耗時IO操作
使用Thread或HandlerThread提高優(yōu)先級
使用Handler處理工作線程的耗時操作
Activity的onCreate和onResume回調(diào)盡量避免耗時操作
OOM面試題
1、什么是OOM
OOM指Out of memory(內(nèi)存溢出),當(dāng)前占用內(nèi)存加上我們申請的內(nèi)存資源超過了Dalvik虛擬機的最大內(nèi)存限制就會拋出Out of memory異常
2、OOM相關(guān)概念
內(nèi)存溢出:指程序在申請內(nèi)存時,沒有足夠的空間供其使用
內(nèi)存泄漏:指程序分配出去的內(nèi)存不再使用,無法進行回收
內(nèi)存抖動:指程序短時間內(nèi)大量創(chuàng)建對象,然后回收的現(xiàn)象
3、解決OOM
Bitmap相關(guān)
圖片壓縮
加載縮略圖
在滾動時不加載圖片
回收Bitmap
使用inBitmap屬性
捕獲異常
其他相關(guān)
listview重用convertView、使用lru
避免onDraw方法執(zhí)行對象的創(chuàng)建
謹慎使用多進程
Bitmap面試題
1、recycle
在安卓3.0以前Bitmap是存放在堆中的,我們只要回收堆內(nèi)存即可
在安卓3.0以后Bitmap是存放在內(nèi)存中的,我們需要回收native層和Java層的內(nèi)存
官方建議我們3.0以后使用recycle方法進行回收,該方法也可以不主動調(diào)用,因為垃圾回收器會自動收集不可用的Bitmap對象進行回收
recycle方法會判斷Bitmap在不可用的情況下,將發(fā)送指令到垃圾回收器,讓其回收native層和Java層的內(nèi)存,則Bitmap進入dead狀態(tài)
recycle方法是不可逆的,如果再次調(diào)用getPixels()等方法,則獲取不到想要的結(jié)果
2、LruCache原理
LruCache是個泛型類,內(nèi)部采用LinkedHashMap來實現(xiàn)緩存機制,它提供get方法和put方法來獲取緩存和添加緩存,其最重要的方法trimToSize是用來移除最少使用的緩存和使用最久的緩存,并添加最新的緩存到隊列中
三級緩存:網(wǎng)絡(luò)緩存,本地緩存,內(nèi)存緩存
UI卡頓
1、UI卡頓原理
View的繪制幀數(shù)保持60fps是最佳,這要求每幀的繪制時間不超過16ms(1000/60),如果安卓不能在16ms內(nèi)完成界面的渲染,那么就會出現(xiàn)卡頓現(xiàn)象
2、UI卡頓的原因分析
在UI線程中做輕微的耗時操作,導(dǎo)致UI線程卡頓
布局Layout過于復(fù)雜,無法在16ms內(nèi)完成渲染
同一時間動畫執(zhí)行的次數(shù)過多,導(dǎo)致CPU和GPU負載過重
overDraw,導(dǎo)致像素在同一幀的時間內(nèi)被繪制多次,使CPU和GPU負載過重
View頻繁的觸發(fā)measure、layout,導(dǎo)致measure、layout累計耗時過多和整個View頻繁的重新渲染
頻繁的觸發(fā)GC操作導(dǎo)致線程暫停,會使得安卓系統(tǒng)在16ms內(nèi)無法完成繪制
冗余資源及邏輯等導(dǎo)致加載和執(zhí)行緩慢
ANR
3、UI卡頓的優(yōu)化
布局優(yōu)化
使用include、ViewStub、merge
不要出現(xiàn)過于嵌套和冗余的布局
使用自定義View取代復(fù)雜的View
ListView優(yōu)化
復(fù)用convertView
滑動不加載
背景和圖片優(yōu)化
縮略圖
圖片壓縮
避免ANR
不要在UI線程中做耗時操作
內(nèi)存泄漏
1、Java內(nèi)存泄漏引起的主要原因
長生命周期的對象持有短生命周期對象的引用就很可能發(fā)生內(nèi)存泄漏
2、Java內(nèi)存分配策略
靜態(tài)存儲區(qū):又稱方法區(qū),主要存儲全局變量和靜態(tài)變量,在整個程序運行期間都存在
棧區(qū):方法體的局部變量會在棧區(qū)創(chuàng)建空間,并在方法執(zhí)行結(jié)束后會自動釋放變量的空間和內(nèi)存
堆區(qū):保存動態(tài)產(chǎn)生的數(shù)據(jù),如:new出來的對象和數(shù)組,在不使用的時候由Java回收器自動回收
3、Android解決內(nèi)存泄漏的例子
單例造成的內(nèi)存泄漏:在單例中,使用context.getApplicationContext()作為單例的context
匿名內(nèi)部類造成的內(nèi)存泄漏:由于非靜態(tài)內(nèi)部類持有匿名外部類的引用,必須將內(nèi)部類設(shè)置為static
Handler造成的內(nèi)存泄漏:使用static的Handler內(nèi)部類,同時在實現(xiàn)內(nèi)部類中持有Context的弱引用
避免使用static變量:由于static變量會跟Activity生命周期一致,當(dāng)Activity退出后臺被后臺回收時,static變量是不安全,所以也要管理好static變量的生命周期
資源未關(guān)閉造成的內(nèi)存泄漏:比如Socket、Broadcast、Cursor、Bitmap、ListView等,使用完后要關(guān)閉
AsyncTask造成的內(nèi)存泄漏:由于非靜態(tài)內(nèi)部類持有匿名內(nèi)部類的引用而造成內(nèi)存泄漏,可以通過AsyncTask內(nèi)部持有外部Activity的弱引用同時改為靜態(tài)內(nèi)部類或在onDestroy()中執(zhí)行AsyncTask.cancel()進行修復(fù)
內(nèi)存管理
1、Android內(nèi)存管理機制
分配機制
管理機制
2、內(nèi)存管理機制的特點
更少的占用內(nèi)存
在合適的時候,合理的釋放系統(tǒng)資源
在系統(tǒng)內(nèi)存緊張的時候,能釋放掉大部分不重要的資源
能合理的在特殊生命周期中,保存或還原重要數(shù)據(jù)
3、內(nèi)存優(yōu)化方法
Service完成任務(wù)后應(yīng)停止它,或用IntentService(因為可以自動停止服務(wù))代替Service
在UI不可見的時候,釋放其UI資源
在系統(tǒng)內(nèi)存緊張的時候,盡可能多的釋放非重要資源
避免濫用Bitmap導(dǎo)致內(nèi)存浪費
避免使用依賴注入框架
使用針對內(nèi)存優(yōu)化過的數(shù)據(jù)容器
使用ZIP對齊的APK
使用多進程
冷啟動和熱啟動面試題
1、什么是冷啟動和熱啟動
冷啟動:在啟動應(yīng)用前,系統(tǒng)中沒有該應(yīng)用的任何進程信息
熱啟動:在啟動應(yīng)用時,在已有的進程上啟動應(yīng)用(用戶使用返回鍵退出應(yīng)用,然后馬上又重新啟動應(yīng)用)
2、冷啟動和熱啟動的區(qū)別
冷啟動:創(chuàng)建Application后再創(chuàng)建和初始化MainActivity
熱啟動:創(chuàng)建和初始化MainActivity即可
3、冷啟動時間的計算
這個時間值從應(yīng)用啟動(創(chuàng)建進程)開始計算,到完成視圖的第一次繪制為止
4、冷啟動流程
Zygote進程中fork創(chuàng)建出一個新的進程
創(chuàng)建和初始化Application類、創(chuàng)建MainActivity
inflate布局、當(dāng)onCreate/onStart/onResume方法都走完
contentView的measure/layout/draw顯示在界面上
總結(jié):Application構(gòu)造方法->attachBaseContext()->onCreate()->Activity構(gòu)造方法->onCreate()->配置主題中背景等屬性->onStart()->onResume()->測量布局繪制顯示在界面上
5、冷啟動優(yōu)化
減少第一個界面onCreate()方法的工作量
不要讓Application參與業(yè)務(wù)的操作
不要在Application進行耗時操作
不要以靜態(tài)變量的方式在Application中保存數(shù)據(jù)
減少布局的復(fù)雜性和深度
不要在mainThread中加載資源
通過懶加載方式初始化第三方SDK
其他優(yōu)化
1、Android不用靜態(tài)變量存儲數(shù)據(jù)
靜態(tài)變量等數(shù)據(jù)由于進程已經(jīng)被殺死而被初始化
使用其他數(shù)據(jù)傳輸方式:文件/sp/contentProvider
2、SharePreference安全問題
不能跨進程同步
文件不宜過大
3、內(nèi)存對象序列化
Serializeble:是java的序列化方式,Serializeble在序列化的時候會產(chǎn)生大量的臨時對象,從而引起頻繁的GC
Parcelable:是Android的序列化方式,且性能比Serializeble高,Parcelable不能使用在要將數(shù)據(jù)存儲在硬盤上的情況
4、避免在UI線程中做繁重的操作
熱更新
1、熱更新主要流程
線上檢查到Crash
拉出Bugfix分支修復(fù)Crash問題
jenkins構(gòu)建和補丁生成
app通過推送或主動拉取補丁文件
將Bugfix代碼合到master上
2、熱更新主流框架
Dexposed
AndFix
Nuwa
3、熱更新的原理
在ClassLoader創(chuàng)建一個dexElements數(shù)組
將修復(fù)好的dex文件存放在dexElements數(shù)組的最前面
ClassLoader會遍歷dexElements數(shù)組,找到最前面的dex文件優(yōu)先加載
進程保活
1、進程的優(yōu)先級
空進程
后臺進程
服務(wù)進程
可見進程
前臺進程
2、Android進程回收策略
Low memory Killer(定時執(zhí)行):通過一些比較復(fù)雜的評分機制,對進程進行打分,然后將分數(shù)高的進程判定為bad進程,殺死并釋放內(nèi)存
OOM_ODJ:判別進程的優(yōu)先級
3、Android保活方案
利用系統(tǒng)廣播拉活
利用系統(tǒng)Service機制拉活
利用Native進程拉活
利用JobScheduler機制拉活
利用賬號同步機制拉活
常用第三方庫
GsonFormat、Gson、fastjson快速搞定JSON數(shù)據(jù)解析
PullToRefresh下拉刷新和上拉加載
LeakCanary檢測內(nèi)存泄漏
RxJava2解鎖圖片三級緩存框架
Zxing實現(xiàn)二維碼掃描
ShareSDk的使用,實現(xiàn)一鍵分享微信好友、朋友圈、QQ
Lint
1、什么是Android Lint
Android Lint是一個靜態(tài)代碼分析工具,它能夠?qū)δ愕腁ndroid項目中潛在的Bug、可優(yōu)化的代碼、安全性、性能、可用性、可訪問性、國際化等進行檢查
2.配置Lint
創(chuàng)建Lint.xml到根目錄下,自定義Lint安全等級等
在Java文件中可以使用@suppressLint(“NewApi”)來忽視Lint的報錯
在xml文件中可以使用tool:ignore(“UnusedResources”)來忽視Lint的報錯
自定義Lint檢查,可以創(chuàng)建類,繼承Detector和實現(xiàn)JavaPsiScanner
Kotlin
1、什么是Kotlin
Kotlin是一種基于JVM的編程語言
對Java的一種拓展,比Java更簡潔
Kotlin支持函數(shù)式編程
Kotlin類和Java類可以相互調(diào)用