Android面試中你可能會遇到的問題

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類可以相互調用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容