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