版權宣告:本文爲博主原創文章,首發簡書。未經博主允許不得轉載。
https://www.jianshu.com/u/3348b92f77a4
續上2020上半年百度Android崗(初級到高階)面試真題全收錄+解析,備戰金九銀十!(上篇)
本文是百度2020上半年網友分享以及我個人收錄的面試真題大全。並且花了大量時間爲大家尋找到了最佳的答案解析。希望可以收到幫助到大家。喜歡的朋友可以點個贊支援一下,謝謝。
BATJ大廠面試真題收錄大全PDF電子書已上傳在石墨文件:【BATJ面試大全】需要的小夥伴自取就好了。別忘了給文章點個贊~
1.1、OnLowMemory 和 OnTrimMemory 的區別比較?
1、OnLowMemory 被回撥時,已經沒有後台進程;而 onTrimMemory 被回撥時,還有後台
進程。
2、OnLowMemory 是在最後一個後臺進程被殺時呼叫,一般情況是 low memory killer 殺進程後觸發;而 OnTrimMemory 的觸發更頻繁,每次計算進程優先順序時,只要滿足條件,都會觸發。
3、通過一鍵清理後,OnLowMemory 不會被觸發,而 OnTrimMemory 會被觸發一次。OnTrimMemory 的參數是一個 int 數值,代表不同的記憶體狀態:
TRIM_MEMORY_COMPLETE:記憶體不足,並且該進程在後台進程列表最後一個,馬上就要被
清理TRIM_MEMORY_MODERATE:記憶體不足,並且該進程在後台進程列表的中部。
TRIM_MEMORY_BACKGROUND:記憶體不足,並且該進程是後臺進程。
TRIM_MEMORY_UI_HIDDEN:記憶體不足,並且該進程的 UI 已經不可見了。
1.2、Application 的生命週期
相比 Activity ,Application 的生命週期簡直不要太簡單。首先建立的時候會呼叫建構函式,然後系統準備好 ContextImpl 通過 attachBaseContext( Context ) 方法注入到 Application,接着呼叫我們最熟悉的 onCreate 方法。API 裡還有一個 onTerminate 方法在進程被殺死的時候會回撥,不過僅在模擬器生效,就不需要關注了。
1.3、說一下 Application 的初始化流程
Application 的初始化是在應用進程建立完成後:
ActivityThread 呼叫 AMS 的 Binder 物件( IActivityManager )的 attachApplication 方法
AMS 收到請求後再去呼叫 ActivityThread 的 bindApplication 方法
ActivityThread 這邊收到請求再組裝一個 AppBindData 物件,把所有參數封裝進去,再通過 handler 發到主執行緒執行
主執行緒 loop 到這條訊息,呼叫 handleBindApplication 來真正處理初始化 Application
handleBindApplication 和我們談 「Context」 那次,Activity 的初始化差不多。回顧一下:
ClassLoader 載入 Application 類,範例化
初始化 Applicaction 用的 ContextImpl
通過 Application.attach( Context ) 方法,呼叫 attachBaseContext( Context ) 將 ContextImpl 注入到 Application
最後呼叫 Application.OnCreate()
這樣 Application 就初始化完成了
2.1、Context理解
1、Activity和Service以及Application的Context是不一樣的,Activity繼承自ContextThemeWraper.其他的繼承自ContextWrapper。
2、每一個Activity和Service以及Application的Context是一個新的ContextImpl物件。
3、getApplication()用來獲取Application範例的,但是這個方法只有在Activity和Service中才能 纔能呼叫的到。那也許在絕大多數情況下我們都是在Activity或者Servic中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的範例,這時就可以藉助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域會更廣一些,任何一個Context的範例,只要呼叫getApplicationContext()方法都可以拿到我們的Application物件。
4、建立對話方塊時不可以用Application的context,只能用Activity的context。
5、Context的數量等於Activity的個數 + Service的個數 +1,這個1爲Application。
2.2、ApplicationContext和ActivityContext的區別
這是兩種不同的context,也是最常見的兩種.第一種中context的生命週期與Application的生命週期相關的,context隨着Application的銷燬而銷燬,伴隨application的一生,與activity的生命週期無關.第二種中的context跟Activity的生命週期是相關的,但是對一個Application來說,Activity可以銷燬幾次,那麼屬於Activity的context就會銷燬多次.至於用哪種context,得看應用場景。還有就是,在使用context的時候,小心記憶體泄露,防止記憶體泄露,注意一下幾個方面:
- 不要讓生命週期長的物件參照activity context,即保證參照activity的物件要與activity本身生命週期是一樣的。
- 對於生命週期長的物件,可以使用application context。
- 避免非靜態的內部類,儘量使用靜態類,避免生命週期問題,注意內部類對外部物件參照導致的生命週期變化。
3.1、Activity和Fragment生命週期有哪些?
3.2、橫豎屏切換時候Activity的生命週期
1)、Android 3.2 (API 13) 之前:
- 不設定 Activity 的 android:configChanges 時,切屏會重新呼叫生命週期,切橫屏會呼叫一次,切豎屏>會呼叫兩次。
- 設定 Activity 的 android:configChanges=「orientation」 時,切屏會重新呼叫生命週期,且橫豎屏都是調>用一次生命週期。
- 設定 Activity 的 android:configChanges=「orientation|keyboardHidden」 時,切屏不會重新呼叫 Activity >的生命週期,但是會呼叫 onConfigurationChanges() 方法。
2)、從Android 3.2 (API 13) 開始
- 不設定 Activity 的 android:configChanges 時、設定 Activity 的 android:configChanges=「orientation」
- 設定 Activity 的 android:configChanges="orientaion|keyboardHidden"時切換橫屏和豎屏都會重新呼叫>一次生命週期。
*設定 Activity 的 android:configChanges="orientation|screenSize"時不會重新呼叫 Activity 的生命週期,>但是會呼叫 onConfigurationChanges() 方法。
3.3、activity的startActivity和context的startActivity區別?
(1)、從Activity中啓動新的Activity時可以直接mContext.startActivity(intent)就好
(2)、如果從其他Context中啓動Activity則必須給intent設定Flag:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ;
mContext.startActivity(intent);
3.4、怎麼加速啓動Activity?
1、onCreate() 中不執行耗時操作 把頁面顯示的 View 細分一下,放在 AsyncTask 裡逐步顯示,用 Handler 更好。這樣使用者的看到的就是有層次有步驟的一個個的 View 的展示,不會是先看到一個黑畫面,然後一下顯示所有 View。最好做成動畫,效果更自然。
2、利用多執行緒的目的就是儘可能的減少 onCreate() 和 onReume() 的時間,使得使用者能儘快看到頁面,操作頁面。
3、減少主執行緒阻塞時間。
4、提高 Adapter 和 AdapterView 的效率。
5、優化佈局檔案。
3.5、直接在Activity中建立一個thread跟在service中建立一個thread之間的區別?
- 在Activity中被建立:該Thread的就是爲這個Activity服務的,完成這個特定的Activity交代的任務,主動通知該Activity一些訊息和事件,Activity銷燬後,該Thread也沒有存活的意義了。
- 在Service中被建立:這是保證最長生命週期的Thread的唯一方式,只要整個Service不退出,Thread就可以一直在後台執行,一般在Service的onCreate()中建立,在onDestroy()中銷燬。所以,在Service中建立的Thread,適合長期執行一些獨立於APP的後臺任務,比較常見的就是:在Service中保持與伺服器端的長連線。
3.6、Activity 與 Service 通訊的四種方式
1、Binder
2、Intent
3、介面 Interface
4、Broadcast 廣播接收
3.7、Activity 之間的幾種通訊方式
1、Intent
2、藉助類的靜態變數
3、藉助全域性變數/Application
4、藉助外部工具
5、 藉助 SharedPreference
6、使用 Android 數據庫 SQLite
7、 赤裸裸的使用 File
8、Android 剪下板
9、藉助 Service
4.1、服務啓動一般有幾種,服務和activty之間怎麼通訊,服務和服務之間怎麼通訊
方式:
1、startService:
onCreate()—>onStartCommand() —> onDestory()
如果服務已經開啓,不會重複的執行onCreate(), 而是會呼叫onStartCommand()。一旦服務開啓跟呼叫者(開啓者)就沒有任何關係了。 開啓者退出了,開啓者掛了,服務還在後臺長期的執行。 開啓者不能呼叫服務裏面的方法。
2、bindService:
onCreate() —>onBind()—>onunbind()—>onDestory()
bind的方式開啓服務,系結服務,呼叫者掛了,服務也會跟着掛掉。 系結者可以呼叫服務裏面的方法。
通訊:
1、通過Binder物件。
2、通過broadcast(廣播)。
4.2、如何保證Service不被殺死?
Android 進程不死從3個層面入手:
A.提供進程優先順序,降低進程被殺死的概率
方法一:監控手機鎖屏解鎖事件,在螢幕鎖屏時啓動1個畫素的 Activity,在使用者解鎖時將 Activity 銷燬掉。
方法二:啓動前臺service。
方法三:提升service優先順序:
在AndroidManifest.xml檔案中對於intent-filter可以通過android:priority = "1000"這個屬性設定最高優先順序,1000是最高值,如果數位越小則優先順序越低,同時適用於廣播。
B. 在進程被殺死後,進行拉活
方法一:註冊高頻率廣播接收器,喚起進程。如網路變化,解鎖螢幕,開機等
方法二:雙進程相互喚起。
方法三:依靠系統喚起。
方法四:onDestroy方法裡重新啓動service:service + broadcast 方式,就是當service走ondestory的時候,發送一個自定義的廣播,當收到廣播的時候,重新啓動service;
C. 依靠第三方
根據終端不同,在小米手機(包括 MIUI)接入小米推播、華爲手機接入華爲推播;其他手機可以考慮接入騰訊信鴿或極光推播與小米推播做 A/B Test。
5.1、廣播註冊一般有幾種,各有什麼優缺點?
第一種是常駐型(靜態註冊):當應用程式關閉後如果有資訊廣播來,程式也會被系統呼叫,自己執行。
第二種不常駐(動態註冊):廣播會跟隨程式的生命週期。
動態註冊
優點: 在android的廣播機制 機製中,動態註冊優先順序高於靜態註冊優先順序,因此在必要情況下,是需要動態註冊廣播接收者的。
缺點: 當用來註冊的 Activity 關掉後,廣播也就失效了。
靜態註冊
優點: 無需擔憂廣播接收器是否被關閉,只要裝置是開啓狀態,廣播接收器就是開啓着的。
6.1、activty和Fragmengt之間怎麼通訊,Fragmengt和Fragmengt怎麼通訊?
(一)Handler
(二)廣播
(三)事件匯流排:EventBus、RxBus、Otto
(四)介面回撥
(五)Bundle和setArguments(bundle)
7.1、自定義view效率高於xml定義嗎?說明理由。
自定義view效率高於xml定義:
1、少瞭解析xml。
2.、自定義View 減少了ViewGroup與View之間的測量,包括父量子,子量自身,子在父中位置擺放,當子view變化時,父的某些屬性都會跟着變化。
7.2、ListView卡頓原因
Adapter的getView方法裏面convertView沒有使用setTag和getTag方式;
在getView方法裏面ViewHolder初始化後的賦值或者是多個控制元件的顯示狀態和背景的顯示沒有優化好,抑或是裏面含有複雜的計算和耗時操作;
在getView方法裏面 inflate的row 巢狀太深(佈局過於複雜)或者是佈局裏面有大圖片或者背景所致;
Adapter多餘或者不合理的notifySetDataChanged;
listview 被多層巢狀,多次的onMessure導致卡頓,如果多層巢狀無法避免,建議把listview的高和寬設定爲match_parent. 如果是程式碼繼承的listview,那麼也請你別忘記爲你的繼承類新增上LayoutPrams,注意高和寬都mactch_parent的;
7.3、LinearLayout、FrameLayout、RelativeLayout效能對比,爲什麼?
RelativeLayout會讓子View呼叫2次onMeasure,LinearLayout 在有weight時,也會呼叫子 View 2次onMeasure
RelativeLayout的子View如果高度和RelativeLayout不同,則會引發效率問題,當子View很複雜時,這個問題會更加嚴重。如果可以,儘量使用padding代替margin。
在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。
8.1Bunder傳遞物件爲什麼需要序列化?Serialzable和Parcelable的區別?
因爲bundle傳遞數據時只支援基本數據型別,所以在傳遞物件時需要序列化轉換成可儲存或可傳輸的本質狀態(位元組流)。序列化後的物件可以在網路、IPC(比如啓動另一個進程的Activity、Service和Reciver)之間進行傳輸,也可以儲存到本地。
Serializable(Java自帶):
Serializable 是序列化的意思,表示將一個物件轉換成儲存或可傳輸的狀態。序列化後的物件可以在網路上進傳輸,也可以儲存到本地。
Parcelable(android專用):
除了Serializable之外,使用Parcelable也可以實現相同的效果,不過不同於將物件進行序列化,Parcelable方式的實現原理是將一個完整的物件進行分解,而分解後的每一部分都是Intent所支援的數據型別,這也就實現傳遞物件的功能了。
8.2、android中有哪幾種解析xml的類,官方推薦哪種?以及它們的原理和區別?
DOM解析
優點:
1.XML樹在記憶體中完整儲存,因此可以直接修改其數據結構.
2.可以通過該解析器隨時存取XML樹中的任何一個節點.
3.DOM解析器的API在使用上也相對比較簡單.
缺點:
如果XML文件體積比較大時,將文件讀入記憶體是非消耗系統資源的.
使用場景:
- DOM 是與平臺和語言無關的方式表示 XML文件的官方 W3C 標準.
- DOM 是以層次結構組織的節點的集合.這個層次結構允許開人員在樹中尋找特定資訊.分析該結構通常需要載入整個文件和構造層次結構,然後才能 纔能進行任何工作.
- DOM 是基於物件層次結構的.
SAX解析
優點:
SAX 對記憶體的要求比較低,因爲它讓開發人員自己來決定所要處理的標籤.特別是當開發人員只需要處理文件中包含的部分數據時,SAX 這種擴充套件能力得到了更好的體現.
缺點:
用SAX方式進行XML解析時,需要順序執行,所以很難存取同一文件中的不同數據.此外,在基於該方式的解析編碼程式也相對複雜.
使用場景:
對於含有數據量十分巨大,而又不用對文件的所有數據行遍歷或者分析的時候,使用該方法十分有效.該方法不將整個文件讀入記憶體,而只需讀取到程式所需的文件標記處即可.
Xmlpull解析
android SDK提供了xmlpullapi,xmlpull和sax類似,是基於流(stream)操作檔案,後者根據節點事件回撥開發者編寫的處理程式.因爲是基於流的處理,因此xmlpull和sax都比較節約記憶體資源,不會像dom那樣要把所有節點以物件樹的形式展現在記憶體中.xmpull比sax更簡明,而且不需要掃描完整個流.
9.1、android中進程的優先順序?
1.前臺進程:
即與使用者正在互動的Activity或者Activity用到的Service等,如果系統記憶體不足時前臺進程是最晚被殺死的
2.可見進程:
可以是處於暫停狀態(onPause)的Activity或者系結在其上的Service,即被使用者可見,但由於失了焦點而不能與使用者互動
3.服務進程:
其中執行着使用startService方法啓動的Service,雖然不被使用者可見,但是卻是使用者關心的,例如使用者正在非音樂介面聽的音樂或者正在非下載頁面下載的檔案等;當系統要空間執行,前兩者進程纔會被終止
4.後臺進程:
其中執行着執行onStop方法而停止的程式,但是卻不是使用者當前關心的,例如後臺掛着的QQ,這時的進程系統一旦沒了有記憶體就首先被殺死
5.空進程:
不包含任何應用程式的進程,這樣的進程系統是一般不會讓他存在的
9.2、Android中跨進程通訊的幾種方式
1:存取其他應用程式的Activity 如呼叫系統通話應用
2:Content Provider 如存取系統相簿
3:廣播(Broadcast) 如顯示系統時間
4:AIDL服務
9.3、爲什麼要用多進程?有哪些方式?怎麼使用多進程
我們都知道,android 平臺對應用都有記憶體限制,其實這個理解有點問題,應該是說 android
平臺對每個進程有記憶體限制,比如某機型對對進程限制是 24m,如果應用有兩個進程,則該
應該的總記憶體限制是 2*24m。使用多進程就可以使得我們一個 apk 所使用的記憶體限制加大
幾倍。所以可以藉此圖片平臺對應用的記憶體限制,比如一些要對圖片、視訊、大檔案進程處理的好
記憶體的應用可以考慮用多進程來解決應用操作不流暢問題。
開啓多進程模式: 在 Android 中 使 用多 進程 只 有 一 種方 法,那 就是 在 AndroidManifest 中 給 四 大 元件(Activity,Service,Receiver,ContentProvider)指定 android:process 屬性.除此之外沒有其他的辦法,也就是說我們無法給一個執行緒活一個實體類指定其執行時所在的進程.其實還有另一種非常規的多進程方法,那就是通過 JNI 在 native 層去 fork 一個新的進程,但這種方法屬於特殊情況,並不是常用的建立多進程的方式,所以我們也暫不考慮這種情況進程名以":「開頭的進程屬於當前應用的私有進程,其他應用的元件不可以和它跑在同一個進程中,而進程名不以」:"開頭的進程屬於全域性進程,其他應用通過 ShareUID 方式可以和它跑在同一個進程中.用多進程的好處與壞處
好處
1)分擔主進程的記憶體壓力。
當應用越做越大,記憶體越來越多,將一些獨立的元件放到不同的進程,它就不佔用主進程的
記憶體空間了。當然還有其他好處,有心人會發現
2)使應用常駐後臺,防止主進程被殺守護行程,守護行程和主進程之間相互監視,有一方
被殺就重新啓動它。Android 後臺進程裡有很多應用是多個進程的,因爲它們要常駐後臺,特別是即時通訊或者社交應用,不過現在多進程已經被用爛了。
典型用法是在啓動一個不可見的輕量級私有進程,在後臺收發訊息,或者做一些耗時的事情,
或者開機啓動這個進程,然後做監聽等。
壞處:消耗使用者的電量。
多佔用了系統的空間,若所有應用都這樣佔用,系統記憶體很容易佔滿而導致卡頓。
應用程式架構會變得複雜,因爲要處理多進程之間的通訊。這裏又是另外一個問題了。
多進程的缺陷進程間的記憶體空間是不可見的。開啓多進程後,會引發以下問題:
1)Application 的多次重建。
2)靜態成員的失效。
3)檔案共用問題。
4)斷點偵錯問題。
10.1、Android5.0新特性
1.MaterialDesign設計風格
2.支援64位元ART虛擬機器(5.0推出的ART虛擬機器,在5.0之前都是Dalvik。他們的區別是:
Dalvik,每次執行,位元組碼都需要通過即時編譯器轉換成機器碼(JIT)。
ART,第一次安裝應用的時候,位元組碼就會預先編譯成機器碼(AOT))
3.通知詳情可以使用者自己設計
10.2、Android6.0新特性
1.動態許可權管理
2.支援快速充電的切換
3.支援資料夾拖拽應用
4.相機新增專業模式
10.3、Android7.0新特性
1.多視窗支援
2.V2簽名
3.增強的Java8語言模式
4.夜間模式
10.4、Android8.0(O)新特性
1.通知渠道 (Notification Channel)
通知標誌
休眠
通知超時
通知設定
通知清除
2.畫中畫模式:清單中Activity設定android:supportsPictureInPicture
3.後臺限制
4.自動填充框架
5.系統優化
6.等等優化很多
10.5、Android9.0§新特性
1.室內WIFI定位
2.「劉海」螢幕支援
3.安全增強
10.6、Android10.0新特性
夜間模式:包括手機上的所有應用都可以爲其設定暗黑模式。
桌面模式:提供類似於PC的體驗,但是遠遠不能代替PC。
螢幕錄製:通過長按「電源」選單中的"螢幕快照"來開啓。
11.1、Bitmap 使用時候注意什麼?
1、要選擇合適的圖片規格(bitmap型別)
2、降低採樣率。BitmapFactory.Options 參數inSampleSize的使用,先把options.inJustDecodeBounds設爲true,只是去讀取圖片的大小,在拿到圖片的大小之後和要顯示的大小做比較通過calculateInSampleSize()函數計算inSampleSize的具體值,得到值之後。options.inJustDecodeBounds設爲false讀圖片資源。
3、複用記憶體。即,通過軟參照(記憶體不夠的時候纔會回收掉),複用記憶體塊,不需要再重新給這個bitmap申請一塊新的記憶體,避免了一次記憶體的分配和回收,從而改善了執行效率。
4、使用recycle()方法及時回收記憶體。
5、壓縮圖片。
11.2、如何計算一個Bitmap佔用記憶體的大小,怎麼保證載入Bitmap不產生記憶體溢位?
在Bitmap裡有兩個獲取記憶體佔用大小的方法。
(1)getByteCount():API12 加入,代表儲存 Bitmap 的畫素需要的最少記憶體。 getAllocationByteCount():API19 加入,代表在記憶體中爲 Bitmap 分配的記憶體大小,代替了 getByteCount() 方法。 在不復用 Bitmap 時,getByteCount() 和 getAllocationByteCount 返回的結果是一樣的。在通過複用 Bitmap 來解碼圖片時,那麼 getByteCount() 表示新解碼圖片佔用記憶體的大 小,getAllocationByteCount() 表示被複用 Bitmap 真實佔用的記憶體大小(即 mBuffer 的長度)。
(2)BitmapFactory.Options.inPreferredConfig:將ARGB_8888改爲RGB_565,改變編碼方式,節約記憶體。 BitmapFactory.Options.inSampleSize:縮放比例,可以參考Luban那個庫,根據圖片寬高計算出合適的縮放比例。 BitmapFactory.Options.inPurgeable:讓系統可以記憶體不足時回收記憶體。
11.3、一張圖片載入到手機記憶體中真正的大小是怎麼計算的
Activity.runOnUiThread(Runnable)
View.post(Runnable),View.postDelay(Runnable, long)(可以理解爲在當前操作檢視UI執行緒新增佇列)
Handler
AsyncTask
Rxjava
LiveData
在Android上,如果你的應用程式有一段時間響應不夠靈敏,系統會向使用者顯示一個對話方塊,這個對話方塊稱作應用程式無響應(ANR:Application NotResponding)對話方塊。使用者可以選擇讓程式繼續執行,但是,他們在使用你的應用程式時,並不希望每次都要處理這個對話方塊。因此,在程式裡對響應效能的設計很重要這樣,這樣系統就不會顯示ANR給使用者。
AsyncTask是什麼?
AsyncTask是一種輕量級的非同步任務類,它可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並在主執行緒中更新UI。
AsyncTask是一個抽象的泛型類,它提供了Params、Progress和Result這三個泛型參數,其中Params表示參數的型別,Progress表示後臺任務的執行進度和型別,而Result則表示後臺任務的返回結果的型別,如果AsyncTask不需要傳遞具體的參數,那麼這三個泛型參數可以用Void來代替。
關於執行緒池:
AsyncTask對應的執行緒池ThreadPoolExecutor都是進程範圍內共用的,且都是static的,所以是Asynctask控制着進程範圍內所有的子類範例。由於這個限制的存在,當使用預設執行緒池時,如果執行緒數超過執行緒池的最大容量,執行緒池就會爆掉(3.0後預設序列執行,不會出現個問題)。針對這種情況,可以嘗試自定義執行緒池,配合Asynctask使用。
關於預設執行緒池:
AsyncTask裏面執行緒池是一個核心執行緒數爲CPU + 1,最大執行緒數爲CPU * 2 + 1,工作佇列長度爲128的執行緒池,執行緒等待佇列的最大等待數爲28,但是可以自定義執行緒池。執行緒池是由AsyncTask來處理的,執行緒池允許tasks並行執行,需要注意的是併發情況下數據的一致性問題,新數據可能會被老數據覆蓋掉。所以
希望tasks能夠序列執行的話,使用SERIAL_EXECUTOR。
AsyncTask在不同的SDK版本中的區別:
呼叫AsyncTask的execute方法不能立即執行程式的原因及改善方案通過查閱官方文件發現,AsyncTask首次引入時,非同步任務是在一個獨立的執行緒中順序的執行,也就是說一次只執行一個任務,不能並行的執行,從1.6開始,AsyncTask引入了執行緒池,支援同時執行5個非同步任務,也就是說只能有5個執行緒執行,超過的執行緒只能等待,等待前的執行緒直到某個執行完了才被排程和執行。換句話說,如果進程中的AsyncTask範例個數超過5個,那麼假如前5都執行很長時間的話,那麼第6個只能等待機會了。這是AsyncTask的一個限制,而且對於2.3以前的版本無法解決。如果你的應用需要大量的後臺執行緒去執行任務,那麼只能放棄使用AsyncTask,自己建立執行緒池來管理Thread。不得不說,雖然AsyncTask較Thread使用起來方便,但是它最多隻能同時執行5個執行緒,這也大大侷限了它的作用,你必須要小心設計你的應用,錯開使用AsyncTask時間,盡力做到分時,或者保證數量不會大於5個,否就會遇到上面提到的問題。可能是Google意識到了AsynTask的侷限性了,從Android 3.0開始對AsyncTask的API做出了一些調整:每次只啓動一個執行緒執行一個任務,完了之後再執行第二個任務,也就是相當於只有一個後臺執行緒在執行所提交的任務。
一些問題:
1.生命週期
很多開發者會認爲一個在Activity中建立的AsyncTask會隨着Activity的銷燬而銷燬。然而事實並非如此。AsynTask會一直執行,直到doInBackground()方法執行完畢,然後,如果cancel(boolean)被呼叫,那麼onCancelled(Result result)方法會被執行;否則,執行onPostExecute(Result result)方法。如果我們的Activity銷燬之前,沒有取消AsyncTask,這有可能讓我們的應用崩潰(crash)。因爲它想要處理的view已經不存在了。所以,我們是必須確保在銷燬活動之前取消任務。總之,我們使用AsyncTask需要確保AsyncTask正確的取消。
2.記憶體漏失
如果AsyncTask被宣告爲Activity的非靜態內部類,那麼AsyncTask會保留一個對Activity的參照。如果Activity已經被銷燬,AsyncTask的後臺執行緒還在執行,它將繼續在記憶體裡保留這個參照,導致Activity無法被回收,引起記憶體漏失。
3.結果丟失
螢幕旋轉或Activity在後台被系統殺掉等情況會導致Activity的重新建立,之前執行的AsyncTask會持有一個之前Activity的參照,這個參照已經無效,這時呼叫onPostExecute()再去更新介面將不再生效。
4.並行還是序列
在Android1.6之前的版本,AsyncTask是序列的,在1.6之後的版本,採用執行緒池處理並行任務,但是從Android 3.0開始,爲了避免AsyncTask所帶來的併發錯誤,又採用一個執行緒來序列執行任務。可以使用executeOnExecutor()方法來並行地執行任務。
AsyncTask原理
AsyncTask中有兩個執行緒池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中執行緒池SerialExecutor用於任務的排隊,而執行緒池THREAD_POOL_EXECUTOR用於真正地執行任務,InternalHandler用於將執行環境從執行緒池切換到主執行緒。
InternalHandler是一個靜態的Handler物件,爲了能夠將執行環境切換到主執行緒,這就要求sHandler這個物件必須在主執行緒建立。由於靜態成員會在載入類的時候進行初始化,因此這就變相要求AsyncTask的類必須在主執行緒中載入,否則同一個進程中的AsyncTask都將無法正常工作。
上篇2020上半年百度Android崗(初級到高階)面試真題全收錄+解析,備戰金九銀十!(上篇)
寫着寫着發現篇幅太長,又寫不下了,所以放到下一篇講了,喜歡的小夥伴可以點個收藏關注,插個眼,下集在我的主頁,馬上更新~
BATJ大廠面試真題收錄大全PDF電子書已上傳在石墨文件:【BATJ面試大全】需要的小夥伴自取就好了。別忘了給文章點個贊~
參考
https://juejin.im/post/6844904079160770568#heading-147
https://juejin.im/post/6844904087566155784