一、Activity
(1)Activity的生命周期
- onCreate() 表示Activity正在被創(chuàng)建做初始化工作。
- onStart() 表示Activity正在啟動(dòng),這時(shí)Activity可見但不在前臺(tái)。無法與用戶交互。
- onResume() 表示Activity獲取焦點(diǎn),可見且在前臺(tái)活動(dòng)??梢耘c用戶交互。
- onPause() 表示Activity正在停止,可做數(shù)據(jù)存儲(chǔ),停止動(dòng)畫等操作。
- onStop() 表示Activity即將停止,可做取消網(wǎng)絡(luò)連接、注銷廣播接收器等。
- onDestroy() 表示Activity即將銷毀,可做釋放資源等操作。
- onRestart() 表示Activity重新啟動(dòng),由不可見到可見時(shí)會(huì)調(diào)用,
(2)onStart()和onResume()/onPause()和onStop()的區(qū)別
- onStart()與onStop()是從Activity是否可見這個(gè)角度調(diào)用
- onResume()和onPause()是從Activity是否顯示在前臺(tái)這個(gè)角度來回調(diào)
(3)Activity A啟動(dòng)另一個(gè)Activity B會(huì)回調(diào)哪些方法?如果Activity B是完全透明呢?如果啟動(dòng)的是一個(gè)Dialog呢?
- 會(huì)調(diào)用A 的onPause() -> B 的onCreate() -> onStart() -> onResume() -> A的onStop()
- 不會(huì)調(diào)用A 的onStop()方法,Dialog同理。
(4)談?wù)刼nSaveInstanceState()方法?何時(shí)會(huì)調(diào)用?
- 當(dāng)非人為終止Activity時(shí)會(huì)調(diào)用onSavaInstanceState()來保存狀態(tài)。
- 該方法在onStop()方法之前調(diào)用,與onPause()沒有時(shí)序關(guān)系。
(5)onSaveInstanceState()與onPause()的區(qū)別?
- onSaveInstanceState()適用于非人為終止Activity時(shí)臨時(shí)性狀態(tài)的保存。
- onPause() 適用于對(duì)數(shù)據(jù)的持久化保存,停止動(dòng)畫等。
(6)說下Activity的四種啟動(dòng)模式?
- standard標(biāo)準(zhǔn)模式:每次啟動(dòng)一個(gè)Activity都會(huì)創(chuàng)建一個(gè)新的實(shí)例,并放入棧頂位置。
- singleTop棧頂復(fù)用模式:如果啟動(dòng)的Activity已經(jīng)位于任務(wù)棧的棧頂,就不會(huì)重新創(chuàng)建實(shí)例,而是調(diào)用onNewIntent(intent)方法。反之創(chuàng)建新的實(shí)例加入棧中。
- singleTask棧內(nèi)復(fù)用模式:只要該Activity在一個(gè)任務(wù)棧中存在,就不會(huì)重新創(chuàng)建新的實(shí)例,并把棧中在其之上的其他Activity Destroy掉,調(diào)用onNewIntent(intent)方法。如果不存在,創(chuàng)建新的實(shí)例并入棧。
- singleInstance單實(shí)例模式:Activity只能單獨(dú)位于一個(gè)任務(wù)棧中,并且這個(gè)任務(wù)棧只存在這一個(gè)實(shí)例。
(7)談?wù)剆ingleTop和singleTask的區(qū)別以及應(yīng)用場(chǎng)景
- singleTop允許同個(gè)Activity在棧中可以有多個(gè)實(shí)例,即可以重復(fù)創(chuàng)建;為防止快速點(diǎn)擊時(shí)多次startActivity,可以將目標(biāo)Activity設(shè)置為singleTop。
- singleTask同個(gè)Activity在棧中只有一個(gè)實(shí)例,即不存在重復(fù)創(chuàng)建;常用于主頁和登陸頁
(8)了解哪些Activity啟動(dòng)模式的標(biāo)記位?
- Intent.FLAG_ACTIVITY_NEW_TASK:使用一個(gè)新的Task來啟動(dòng)Activity
- Intent.FLAG_ACTIVITY_SINGLE_TOP:類似singleTop
- Intent.FLAG_ACTIVITY_CLEAR_TOP:類似singleTask
- Intent.FLAG_ACTIVITY_NO_HISTORY:使用這種模式啟動(dòng)Activity,當(dāng)該Activity啟動(dòng)其他Activity后,該Activity就消失了,不會(huì)保留在Task棧中。
(9)onNewIntent()調(diào)用時(shí)機(jī)?
- 啟動(dòng)模式為singleTop或singleTask的Activity會(huì)出現(xiàn)回調(diào)onNewIntent()的情況
(10)如何避免配置改變時(shí)Activity重建?
- 在AndroidManifest.xml中對(duì)應(yīng)的Activity中設(shè)置android:configChanges="orientation|keyboardHidden|screenSize"。此時(shí)發(fā)生屏幕旋轉(zhuǎn),該Activity不會(huì)被系統(tǒng)殺死和重建,會(huì)調(diào)用onConfigurationChanged方法。
(11)優(yōu)先級(jí)低的Activity在內(nèi)存不足被回收后怎樣做可以恢復(fù)到銷毀前狀態(tài)?
- 低優(yōu)先級(jí)的Activity在內(nèi)存不足被回收后重新啟動(dòng)會(huì)引發(fā)Activity重建,會(huì)調(diào)用onRestoreInstanceState方法,并將onSaveInstanceState方法保存的Bunble對(duì)象作為參數(shù)傳到onRestoreInstanceState 和 onCreate方法中。因此可通過onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)來判斷Activity是否被重建,并取出數(shù)據(jù)進(jìn)行恢復(fù)。在onCreate中需要判斷savedInstanceState是否為空。
(12)如何啟動(dòng)其他應(yīng)用的Activity?
- 在保證有權(quán)限訪問的情況下,通過隱式Intent進(jìn)行目標(biāo)Activity的IntentFilter匹配
(13)如何修改activity進(jìn)入和退出動(dòng)畫
通過調(diào)用overridePendingTransition() 可以實(shí)時(shí)修改Activity的切換動(dòng)畫。該函數(shù)必須在調(diào)用startActivity()或者finish()后立即調(diào)用,且只有效一次。
(14)Activity創(chuàng)建和Dialog創(chuàng)建過程的異同?
- 創(chuàng)建WindowDialog。和Activity類似,同樣是通過PolicyManager.makeNewWindow()來實(shí)現(xiàn)。
- 初始化DecorView并將Dialog的視圖添加到DecorView中去。和Activity類似,同樣是通過Window.setContentView() 來實(shí)現(xiàn)。
- 將DecorView添加到Window中顯示。和Activity一樣,都是在自身要出現(xiàn)在前臺(tái)時(shí)才會(huì)將添加Window。
- Dialog.show()方法:完成DecorView的顯示。
- WindowManager.remoteViewImmediate()方法:當(dāng)Dialog被dismiss時(shí)移除DecorView。
(15)Activity的啟動(dòng)過程?
- execStartActivity調(diào)用ActivityManager.getService().startActivity方法,基于Binder機(jī)制,通過調(diào)用代理對(duì)象IActivityManager的方法,使得系統(tǒng)服務(wù)ActivityManagerService對(duì)應(yīng)的startActivity方法被調(diào)用。
- ActivityManagerService.startActivity方法經(jīng)過重重方法會(huì)通過app.thread調(diào)用scheduleLaunchActivity方法,通過Binder機(jī)制,會(huì)使ApplicationThread.scheduleLaunchActivity方法被調(diào)用。
- 基于Binder機(jī)制,實(shí)現(xiàn)了一次進(jìn)程間的通信,將啟動(dòng)Activity的操作交給了ApplicationThread類。
- scheduleLaunchActivity方法將啟動(dòng)Activity的消息發(fā)送并交由Handler H處理。Handler H對(duì)消息的處理會(huì)調(diào)用handleLaunchActivity()->performLaunchActivity()得以完成Activity對(duì)象的創(chuàng)建和啟動(dòng)。
二、Fragment
(1)談一談Fragment的生命周期?
- Fragment生命周期方法:onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
- onAttach() 當(dāng)Fragment和Activity建立關(guān)聯(lián)時(shí)調(diào)用
- onCreateView() 當(dāng)Fragment創(chuàng)建視圖時(shí)調(diào)用
- onActivityCreated() 當(dāng)與Fragment相關(guān)聯(lián)的Activity完成onCreate()之后調(diào)用
- onDestroyView() 在Fragment中的布局被移除時(shí)調(diào)用
- onDetach() 當(dāng)Fragment和Activity解除關(guān)聯(lián)時(shí)調(diào)用
(2)Activity和Fragment的異同?
- 相同點(diǎn):它們都可包含布局、可有自己的生命周期,F(xiàn)ragment可看似迷你活動(dòng)。
- 不同點(diǎn):Fragment是依附在Activity上的,多了些和宿主Activity相關(guān)的生命周期方法,如onAttach()、onCreateView()、onActivityCreated()、onDestroyView()、onDetach();另外,F(xiàn)ragment的生命周期方法是由宿主Activity而不是操作系統(tǒng)調(diào)用的。
(3)Activity和Fragment的關(guān)系?
- 它可作為Activity界面的組成部分,可在Activity運(yùn)行中實(shí)現(xiàn)動(dòng)態(tài)地加入、移除和交換。
- 一個(gè)Activity中可同時(shí)出現(xiàn)多個(gè)Fragment,一個(gè)Fragment也可在多個(gè)Activity中使用。
- Activity的FragmentManager負(fù)責(zé)調(diào)用隊(duì)列中Fragment的生命周期方法,只要Fragment的狀態(tài)與Activity的狀態(tài)保持了同步,宿主Activity的FragmentManager便會(huì)繼續(xù)調(diào)用其他生命周期方法以繼續(xù)保持Fragment與Activity的狀態(tài)一致。
(4)何時(shí)會(huì)考慮使用Fragment?
- 用兩個(gè)Fragment封裝兩個(gè)界面模塊,這樣只使一套代碼就能適配兩種設(shè)備,達(dá)到兩種界面效果
- 單一場(chǎng)景切換時(shí)使用Fragment更輕量化,如ViewPager和Fragment搭配使用
(5)Fragment與Activity之間通信
- Fragment中設(shè)置回調(diào)接口,將事件處理交給實(shí)現(xiàn)接口的Activity實(shí)現(xiàn)。
- 如果你Activity中包含自己管理的Fragment的引用??梢酝ㄟ^引用直接訪問所有的Fragment的public方法
- 在Fragment中可以通過getActivity得到當(dāng)前綁定的Activity的實(shí)例,然后進(jìn)行操作。
三、Service
(1)談一談Service的生命周期?
- onCreate():服務(wù)第一次被創(chuàng)建時(shí)調(diào)用
- onStartCommand():服務(wù)啟動(dòng)時(shí)調(diào)用
- onBind():服務(wù)被綁定時(shí)調(diào)用
- onUnBind():服務(wù)被解綁時(shí)調(diào)用
- onDestroy():服務(wù)停止時(shí)調(diào)用
(2)Service的兩種啟動(dòng)方式?區(qū)別在哪?
- 第一種:startService()方法可以啟動(dòng)一個(gè)Service,并回調(diào)服務(wù)中的onStartCommand()方法。如果該服務(wù)之前還沒創(chuàng)建,那么回調(diào)的順序是onCreate() -> onStartCommand();如果服務(wù)是開啟狀態(tài),在次調(diào)用startService()不會(huì)回調(diào)onCreate()方法。服務(wù)啟動(dòng)了之后會(huì)一直保持運(yùn)行狀,直到 stopService() 或 stopSelf() 方法被調(diào)用,服務(wù)停止并回調(diào)onDestroy()(無論調(diào)用多少次startService()方法,只需調(diào)用一次stopService()或stopSelf()方法,服務(wù)就會(huì)停止了)。
- 第二種:bindService()方法可以綁定一個(gè)Service,并回調(diào)服務(wù)中的onBind()方法。如果該服務(wù)之前還沒創(chuàng)建,那么回調(diào)的順序是onCreate() -> onBind();如果服務(wù)是開啟狀態(tài),在次調(diào)用startService()不會(huì)回調(diào)onCreate()方法。調(diào)用方可以獲取到onBind()方法里返回的IBinder對(duì)象的實(shí)例,從而實(shí)現(xiàn)和服務(wù)進(jìn)行通信。只要調(diào)用方和服務(wù)之間的連接沒有斷開,服務(wù)就會(huì)一直保持運(yùn)行狀態(tài),直到調(diào)用了 unbindService()方法服務(wù)會(huì)停止,回調(diào)順序onUnBind() -> onDestroy()。
(3)一個(gè)Activty先start一個(gè)Service后,再bind時(shí)會(huì)回調(diào)什么方法?此時(shí)如何做才能回調(diào)Service的onDestroy()方法?
- 這兩種啟動(dòng)方法并不沖突,startService()啟動(dòng)Service之后,再bindService()綁定,此時(shí)只會(huì)回調(diào)onBind()方法
- 需要同時(shí)調(diào)用 stopService()和 unbindService()方法才能讓服務(wù)銷毀掉。
(4)Service如何和Activity進(jìn)行通信?
- 通過bindService()可以實(shí)現(xiàn)Activity調(diào)用Service中的方法。
- 通過廣播實(shí)現(xiàn)Service向Activity發(fā)送消息。
(5)用過哪些系統(tǒng)Service?
- WINDOW_SERVICE 管理打開的窗口程序
- LAYOUT_INFLATER_SERVICE 取得XML里定義的View
- ACTIVITY_SERVICE 管理應(yīng)用程序的系統(tǒng)狀態(tài)
- POWER_SERVICE 電源服務(wù)
- ALARM_SERVICE 鬧鐘服務(wù)
- NOTIFICATION_SERVICE 狀態(tài)欄服務(wù)
- KEYAUARD_SERVICE 鍵盤鎖服務(wù)
(6)是否能在Service進(jìn)行耗時(shí)操作?如果非要可以怎么做?
- Service默認(rèn)并不會(huì)運(yùn)行在子線程中,也不運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中,它同樣執(zhí)行在主線程中(UI線程)。
- 手動(dòng)打開一個(gè)子線程,否則有可能出現(xiàn)主線程被阻塞(ANR)的情況。
(7)前臺(tái)服務(wù)是什么?和普通服務(wù)的不同?如何去開啟一個(gè)前臺(tái)服務(wù)?
- 前臺(tái)服務(wù)的服務(wù)狀態(tài)可以被用戶看到。它和普通服務(wù)最大的區(qū)別是,前者會(huì)一直有一個(gè)正在運(yùn)行的圖標(biāo)在系統(tǒng)的狀態(tài)欄顯示,下拉狀態(tài)欄后可以看到更加詳細(xì)的信息,且當(dāng)系統(tǒng)內(nèi)存不足服務(wù)被殺死時(shí),通知會(huì)被移除。
- 創(chuàng)建一個(gè)Notification實(shí)例,調(diào)用startForeground()方法,不需要NotificationManager將通知顯示出來。
(8)是否了解ActivityManagerService,談?wù)勊l(fā)揮什么作用?
- ActivityManagerService是Android中最核心的服務(wù) , 主要負(fù)責(zé)系統(tǒng)中四大組件的啟動(dòng)、切換、調(diào)度及應(yīng)用進(jìn)程的管理和調(diào)度等工作。
(9)如何保證Service不被殺死?
- 在Service的onStartCommand()中設(shè)置flages值為START_STICKY,使得Service被殺死后嘗試再次啟動(dòng)Service
- 提升Service優(yōu)先級(jí),比如設(shè)置為一個(gè)前臺(tái)服務(wù)
- 在Activity的onDestroy()通過發(fā)送廣播,并在廣播接收器的onReceive()中啟動(dòng)Service。
(10)Service啟動(dòng)過程
- startService調(diào)用startServiceCommon方法內(nèi)部ActivityManager.getService()是IActivityManager的代理對(duì)象,調(diào)用代理對(duì)象的startService方法,會(huì)向系統(tǒng)服務(wù)ActivityManagerService發(fā)起請(qǐng)求,基于Binder機(jī)制,調(diào)用ActivityManagerService的startService方法。
- ActivityManagerService.startService方法經(jīng)過重重方法會(huì)通過app.thread調(diào)用scheduleCreateService方法,通過Binder機(jī)制,會(huì)使ApplicationThread.scheduleCreateService方法被調(diào)用。
- scheduleCreateService方法將啟動(dòng)Service的消息發(fā)送并交由Handler H處理。Handler H對(duì)消息的處理會(huì)調(diào)用handleCreateService啟動(dòng)Service。
四、BroadcastReceiver
(1)廣播有幾種形式?什么特點(diǎn)?
- 普通廣播:一種完全異步執(zhí)行的廣播,在廣播發(fā)出之后,所有的廣播接收器幾乎都會(huì)在同一時(shí)刻接收到這條廣播消息,因此它們接收的先后是隨機(jī)的。
- 有序廣播:sendOrderedBroadcast一種同步執(zhí)行的廣播,在廣播發(fā)出之后,同一時(shí)刻只會(huì)有一個(gè)廣播接收器能夠收到這條廣播消息,當(dāng)這個(gè)廣播接收器中的邏輯執(zhí)行完畢后,廣播才會(huì)繼續(xù)傳遞,所以此時(shí)的廣播接收器是有先后順序的,且優(yōu)先級(jí)(priority)高的廣播接收器會(huì)先收到廣播消息。有序廣播可以被接收器截?cái)嗍沟煤竺娴慕邮掌鳠o法收到它。
- 本地廣播:LocalBroadcastManager發(fā)出的廣播只能夠在應(yīng)用程序的內(nèi)部進(jìn)行傳遞,并且廣播接收器也只能接收本應(yīng)用程序發(fā)出的廣播。
- 粘性廣播:sendStickyBroadcast這種廣播會(huì)一直滯留,當(dāng)有匹配該廣播的接收器被注冊(cè)后,該接收器就會(huì)收到此條廣播。
(2)廣播的兩種注冊(cè)形式?區(qū)別在哪?
- 靜態(tài)注冊(cè):無需擔(dān)憂廣播接收器是否被關(guān)閉,只要設(shè)備是開啟狀態(tài),廣播接收器也是打開著的。
- 動(dòng)態(tài)注冊(cè):廣播接收器必須要在程序啟動(dòng)之后才能接收到廣播。當(dāng)用來注冊(cè)廣播的Activity關(guān)掉后,廣播也就失效了
(3)廣播的啟動(dòng)過程
registerReceiver調(diào)用registerReceiverInternal方法內(nèi)部ActivityManager.getService()是IActivityManager的代理對(duì)象,調(diào)用代理對(duì)象的registerReceiver方法,會(huì)向系統(tǒng)服務(wù)ActivityManagerService發(fā)起請(qǐng)求,基于Binder機(jī)制,調(diào)用ActivityManagerService的registerReceiver方法。
五、ContentProvider & 數(shù)據(jù)存儲(chǔ)
(1)ContentProvider了解多少?
- 通過ContentResolver覆蓋增刪改查方法實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的訪問。
- ContentProvider可以讓不同應(yīng)用程序之間進(jìn)行數(shù)據(jù)共享,它還可以選擇只對(duì)哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,從而保證程序中的隱私數(shù)據(jù)不會(huì)有泄漏風(fēng)險(xiǎn)。
(2)Android中提供哪些數(shù)據(jù)持久存儲(chǔ)的方法?
- File 文件存儲(chǔ):Context類中提供了openFileInput()和openFileOutput()方法來打開數(shù)據(jù)文件里的文件IO流。
- SharedPreferences存儲(chǔ):一種輕型的數(shù)據(jù)存儲(chǔ)方式,常用來存儲(chǔ)一些簡(jiǎn)單的配置信息,基于XML文件存儲(chǔ)key-value鍵值對(duì)數(shù)據(jù)。
- SQLite數(shù)據(jù)庫存儲(chǔ):一款輕量級(jí)的關(guān)系型數(shù)據(jù)庫,它的運(yùn)算速度非常快,占用資源很少,在存儲(chǔ)大量復(fù)雜的關(guān)系型數(shù)據(jù)的時(shí)可以使用。
- ContentProvider:四大組件之一,用于數(shù)據(jù)的存儲(chǔ)和共享,不僅可以讓不同應(yīng)用程序之間進(jìn)行數(shù)據(jù)共享,還可以選擇只對(duì)哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,可保證程序中的隱私數(shù)據(jù)不會(huì)有泄漏風(fēng)險(xiǎn)。
(3)Java中的I/O流讀寫怎么做?
//填入文件名和操作模式
FileOutputStream fos = openFileOutput("a.txt", MODE_PRIVATE);
//通過write()函數(shù)寫入數(shù)據(jù)。
fos.write(string.getBytes());
fos.close();
FileInputStream fis = openFileInput("a.txt");
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[1024];
int len = 0;
//讀取字節(jié)
while ((len = fis.read(buffer)) != -1) {
sb.append(new String(buffer, 0, len));
}
string = sb.toString();
fis.close();
(4)SharePreferences適用情形?使用中需要注意什么?
- SharePreferences是一種輕型的數(shù)據(jù)存儲(chǔ)方式,適用于存儲(chǔ)一些簡(jiǎn)單的配置信息,如int、string、boolean、float和long。由于系統(tǒng)對(duì)SharedPreferences的讀/寫有一定的緩存策略,即在內(nèi)存中有一份該文件的緩存,因此在多進(jìn)程模式下,其讀/寫會(huì)變得不可靠,甚至丟失數(shù)據(jù)。
(5)SharePreferences的注意事項(xiàng)
- SP文件不宜過大,如果SP文件需要存儲(chǔ)的內(nèi)容過多,可以根據(jù)不同的功能劃分成多個(gè)文件;
- 如果可以的話盡可能早的調(diào)用getSharedPreferences,這樣在調(diào)用put和get操作時(shí),文件已經(jīng)被讀取到內(nèi)存中了;
- 不要多次調(diào)用edit(), 應(yīng)該調(diào)用一次edit(),因?yàn)槊看握{(diào)用edit()都會(huì)新建一個(gè)Editor;
- 不要多次調(diào)用commit()或apply(),如果多次存入值,應(yīng)該在最后一次調(diào)用。
(6)SharePreferences的優(yōu)缺點(diǎn)
- SharedPreferences對(duì)象本身只能獲取數(shù)據(jù),而不支持存儲(chǔ)和修改,存儲(chǔ)和修改是通過SharedPreferences.edit()獲取的內(nèi)部類接口Editor對(duì)象實(shí)現(xiàn)。
- SharedPreferences對(duì)象與SQLite數(shù)據(jù)庫相比顯得格外輕量級(jí),免去了創(chuàng)建數(shù)據(jù)庫,創(chuàng)建表,寫SQL語句等諸多操作,相對(duì)而言更加方便,簡(jiǎn)潔。
- 只能存儲(chǔ)boolean,int,float,long和String五種簡(jiǎn)單的數(shù)據(jù)類型。無法進(jìn)行條件查詢等。
(7)SharedPreferences源碼實(shí)現(xiàn)
- 通過getSharedPreferences方法獲取file文件并返回SharedPreferences實(shí)例對(duì)象
- SharedPreferencesImpl構(gòu)造方法備份file
- startLoadFromDisk方法開啟一個(gè)線程從磁盤讀取數(shù)據(jù),將xml文件轉(zhuǎn)成map。
- 修改值只會(huì)將其存到mModified的map中去,所以在編輯器中所做的所有更改都會(huì)批處理,直到我們調(diào)用commit或apply才會(huì)設(shè)置到mMap和xml文件中。
(8)了解SQLite中的事務(wù)處理嗎?是如何做的?
- SQLite在做CRDU操作時(shí)都默認(rèn)開啟了事務(wù),然后把SQL語句翻譯成對(duì)應(yīng)的SQLiteStatement并調(diào)用其相應(yīng)的CRUD方法,此時(shí)整個(gè)操作還是在rollback journal這個(gè)臨時(shí)文件上進(jìn)行,只有操作順利完成才會(huì)更新.db數(shù)據(jù)庫,否則會(huì)被回滾。
(9)使用SQLite做批量操作有什么好的方法嗎?
- 使用SQLiteDatabase的beginTransaction()方法開啟一個(gè)事務(wù),將批量操作SQL語句轉(zhuǎn)化成SQLiteStatement并進(jìn)行批量操作,結(jié)束后endTransaction()
(10)如果現(xiàn)在要?jiǎng)h除SQLite中表的一個(gè)字段如何做?
- SQLite數(shù)據(jù)庫只允許增加表字段而不允許修改和刪除表字段,只能采取復(fù)制表思想,即創(chuàng)建一個(gè)新表保留原表想要的字段、再將原表刪除。
(11)使用SQLite時(shí)會(huì)有哪些優(yōu)化操作?
- 使用事務(wù)做批量操作。
- 及時(shí)關(guān)閉Cursor,避免內(nèi)存泄漏
- 耗時(shí)操作異步化:數(shù)據(jù)庫的操作屬于本地IO,通常比較耗時(shí),建議將這些耗時(shí)操作放入異步線程中處理
- ContentValues的容量調(diào)整:ContentValues內(nèi)部采用HashMap來存儲(chǔ)Key-Value數(shù)據(jù),ContentValues初始容量為8,擴(kuò)容時(shí)翻倍。因此建議對(duì)ContentValues填入的內(nèi)容進(jìn)行估量,設(shè)置合理的初始化容量,減少不必要的內(nèi)部擴(kuò)容操作
- 使用索引加快檢索速度:對(duì)于查詢操作量級(jí)較大、業(yè)務(wù)對(duì)查詢要求較高的推薦使用索引
(12)SQLite創(chuàng)建源碼解析
- SQLiteOpenHelper主要完成SQLiteDatabase 的創(chuàng)建、關(guān)閉、升/降級(jí)、配置等相關(guān)輔助管理
- SQLiteDatabase.openDatabase經(jīng)過層層調(diào)用創(chuàng)建當(dāng)前SQLiteDatabase的數(shù)據(jù)庫連接池SQLiteConnectionPoo
- SQLiteConnectionPoo中通過setupIdleConnectionHandler超時(shí)管理的Handler
六、集合
(1)ArrayList集合
- ArrayList是基于數(shù)組實(shí)現(xiàn)的list類,底層的數(shù)據(jù)結(jié)構(gòu)使用的是數(shù)組結(jié)構(gòu),數(shù)據(jù)也是有序的,按照插入的順序來排序。
- ArrayList容量可實(shí)現(xiàn)動(dòng)態(tài)增長(zhǎng)其實(shí)是對(duì)數(shù)組的動(dòng)態(tài)擴(kuò)充,不指定的話長(zhǎng)度默認(rèn)為10,默認(rèn)每次擴(kuò)容為原來的1.5倍加一。
- 增加元素可能會(huì)進(jìn)行擴(kuò)容,而刪除元素卻不會(huì)進(jìn)行縮容。合理使用減少擴(kuò)容的次數(shù),提高性能,list.ensureCapacity();擴(kuò)充原有的數(shù)組
- 修改或查詢數(shù)據(jù)相對(duì)快。添加或刪除數(shù)據(jù)相對(duì)慢,因?yàn)樾枰苿?dòng)大量的數(shù)據(jù)。ArrayList是線程不安全的。
(2)LinkedList集合
- LinkedList是基于鏈表實(shí)現(xiàn)的雙向鏈表數(shù)據(jù)結(jié)構(gòu)。它每一個(gè)節(jié)點(diǎn)都包含三方面的內(nèi)容:節(jié)點(diǎn)本身的數(shù)據(jù)、前一個(gè)節(jié)點(diǎn)的信息、下一個(gè)節(jié)點(diǎn)的信息。
- 增加或刪除數(shù)據(jù)相對(duì)快。修改或查詢數(shù)據(jù)相對(duì)慢,因?yàn)樾枰闅v查找
- 單向鏈表:?jiǎn)蜗蜴湵砭褪峭ㄟ^每個(gè)結(jié)點(diǎn)的指針指向下一個(gè)結(jié)點(diǎn)從而鏈接起來的結(jié)構(gòu),最后一個(gè)節(jié)點(diǎn)的next指向null。
- 單向循環(huán)鏈表:?jiǎn)蜗蜓h(huán)鏈表和單向列表的不同是,最后一個(gè)節(jié)點(diǎn)的next不是指向null,而是指向head節(jié)點(diǎn)。
- 雙向鏈表:雙向鏈表是包含兩個(gè)指針的,pre指向前一個(gè)節(jié)點(diǎn),next指向后一個(gè)節(jié)點(diǎn),但是第一個(gè)節(jié)點(diǎn)head的pre指向null,最后一個(gè)節(jié)點(diǎn)的next指向null。
- 雙向循環(huán)鏈表:雙向循環(huán)鏈表和雙向鏈表的不同在于,第一個(gè)節(jié)點(diǎn)的pre指向最后一個(gè)節(jié)點(diǎn),最后一個(gè)節(jié)點(diǎn)的next指向第一個(gè)節(jié)點(diǎn)。
(3)Vector集合
- Vector底層結(jié)構(gòu)是數(shù)組,比ArrayList開銷更大,訪問更慢;默認(rèn)初始容量為10,默認(rèn)每次擴(kuò)容為原來的2倍。
- synchronized實(shí)現(xiàn)同步,保證線程安全,Vector是線程安全的ArrayList。
(4)HashSet集合
- HashSet無序不允許集合元素的重復(fù),可以存儲(chǔ)null。底層數(shù)據(jù)結(jié)構(gòu)是哈希表。根據(jù)hashCode來決定存儲(chǔ)位置的。非線程安全,存取速度快。
- 通過hashCode和equals來保證元素的唯一性。如果元素的hashCode相同才會(huì)判斷equals是否為true。如果hashCoed的值不同,則不會(huì)調(diào)用equals。
(5)TreeSet
- TreeSet不允許集合元素的重復(fù)。底層數(shù)據(jù)結(jié)構(gòu)是二叉樹,線程不安全??梢詫?duì)Set集合中的元素進(jìn)行排序
- TreeSet的排序讓元素自身具備比較性,元素需要實(shí)現(xiàn)comparable接口,覆蓋compareTo方法,如果兩個(gè)對(duì)象通過compareTo()方法比較相等,那么新的元素將無法添加到TreeSet集合中。
- 當(dāng)元素本身不具備比較性,或者具備的比較性不是所需的,就需要讓集合自身具備比較性,實(shí)現(xiàn)Comparator接口,實(shí)現(xiàn)compare方法。將實(shí)現(xiàn)類傳入TreeSet。
(6)LinkedHashSet集合
- LinkedHashSet不允許集合元素的重復(fù)。集合也是根據(jù)元素的hashCode值來決定元素的存儲(chǔ)位置,但和hashSet不同的是它同時(shí)使用鏈表維護(hù)元素的次序,這樣使得元素看起來是以插入的順序保存的。linkedHashSet按元素的添加順序來訪問集合里的元素。(LinkedHashSet需要維護(hù)元素的插入順序,因此性能略低于HashSet的性能,但是在遍歷Set里面的全部元素將有很好的性能)。
(7)TreeMap集合
- TreeMap是平衡排序二叉樹(紅黑樹)結(jié)構(gòu),按自然排序或比較器存入元素以保證元素順序。TreeMap的鍵保唯一性,取決于比較方法和比較器。非線程安全。
(8)HashMap
- HashMap是鏈表散列的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合體,非線程安全,不保證存取順序,允許null鍵或者null值。HashMap的鍵保持唯一性,取決于hashCode以及equals方法。效率較高。
- 初始容量為16,擴(kuò)容時(shí)是當(dāng)前容量翻倍。
(9)HashTable
- HashTable時(shí)哈希表結(jié)構(gòu),不保證存取順序,不允許null鍵或者null值,線程安全,效率較低,已經(jīng)被HashMap替代
- 初始容量為11,擴(kuò)容為原來的2倍加1
(10)LinkedHashMap
- LinkedHashMap是帶雙向鏈表的哈希表結(jié)構(gòu),保持存取順序,允許null鍵和null值,非線程安全。
- 內(nèi)部通過Entry維護(hù)了一個(gè)雙向鏈表,負(fù)責(zé)維護(hù)Map的迭代順序。
(11)數(shù)組、集合、泛型的關(guān)系,并比較
- 數(shù)組元素可以是基本類型,也可以是對(duì)象;數(shù)組長(zhǎng)度限定;數(shù)組只能存儲(chǔ)一種類型的數(shù)據(jù)元素
- 集合元素只能是對(duì)象;集合長(zhǎng)度可變;集合可存儲(chǔ)不同種的數(shù)據(jù)元素
- 泛型相比與集合的好處在于它安全簡(jiǎn)單。提供編譯時(shí)的強(qiáng)類型檢查,而不用等到運(yùn)行;可避免類類型強(qiáng)制轉(zhuǎn)換
(12)ArrayList和LinkList的區(qū)別?
- ArrayList底層結(jié)構(gòu)是數(shù)組,可用索引實(shí)現(xiàn)快速查找和修改;容量可實(shí)現(xiàn)動(dòng)態(tài)增長(zhǎng)。
- LinkedList底層結(jié)構(gòu)是雙向循環(huán)鏈表,增刪速度快。
**(13)ArrayList和Vector的區(qū)別?
- ArrayList非線程安全,默認(rèn)初始容量為10,每次擴(kuò)容為原來的1.5倍。
- Vector使用了synchronized關(guān)鍵字,是線程安全的,比ArrayList開銷更大,訪問更慢;默認(rèn)初始容量為10,默認(rèn)每次擴(kuò)容為原來的2倍。
(14)HashSet和TreeSet的區(qū)別?
- HashSet底層采用HashMap來保存元素;不能保證元素的排列順序;有良好的存取和查找性能;不保證元素的順序,沒有重復(fù)元素的集合,而且HashSet允許使用 null 元素。
- TreeSet采用紅黑樹的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)集合元素;根據(jù)元素實(shí)際值的大小進(jìn)行排序;
- 前者compareTo()比較兩個(gè)元素之間大小關(guān)系,后者compare()比較兩個(gè)元素之間大小關(guān)系。
(15)HashMap和Hashtable的區(qū)別?
- HashMap非線程安全;允許存在一個(gè)為null的key和任意個(gè)為null的value;采用鏈表散列的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合;初始容量為16,填充因子默認(rèn)為0.75,擴(kuò)容時(shí)是當(dāng)前容量翻倍。
- Hashtable線程安全,開銷比HashMap大,不允許使用null作為key和value;底層基于哈希表結(jié)構(gòu);初始容量為11,填充因子默認(rèn)為0.75,擴(kuò)容時(shí)是容量翻倍+1。
(15)HashMap在put、get元素的過程?體現(xiàn)了什么數(shù)據(jù)結(jié)構(gòu)?
- 向HashMap中put元素時(shí),首先判斷key是否為空,為空則直接調(diào)用putForNullKey(),不為空則計(jì)算key的hash值得到該元素在數(shù)組中的下標(biāo)值;如果數(shù)組在該位置處沒有元素,就直接保存;如果有,還要比較是否存在相同的key,存在的話就覆蓋原來key的value,否則將該元素保存在鏈頭,先保存的在鏈尾。
- 從HashMap中g(shù)et元素時(shí),計(jì)算key的hash值找到在數(shù)組中的對(duì)應(yīng)的下標(biāo)值,返回該key對(duì)應(yīng)的value即可,如果有沖突就遍歷該位置鏈表尋找key相同的元素并返回對(duì)應(yīng)的value。
(16)HashMap是有序的嗎?如何實(shí)現(xiàn)有序?
- HashMap是無序的,而LinkedHashMap是有序的HashMap,默認(rèn)為插入順序,還可以是訪問順序,基本原理是其內(nèi)部通過Entry維護(hù)了一個(gè)雙向鏈表,負(fù)責(zé)維護(hù)Map的迭代順序。
(17)HashMap是如何擴(kuò)容的?如何避免擴(kuò)容?
- HashMap幾個(gè)默認(rèn)值,初始容量為16、填充因子默認(rèn)為0.75、擴(kuò)容時(shí)容量翻倍。也就是說當(dāng)HashMap中元素個(gè)數(shù)超過160.75=12時(shí)會(huì)把數(shù)組的大小擴(kuò)展為216=32,然后重新計(jì)算每個(gè)元素在數(shù)組中的位置。
- 由于每次擴(kuò)容還需要重新計(jì)算元素Hash值,損耗性能,所以建議在使用HashMap時(shí),最好先估算Map的大小,設(shè)置初始值,避免頻繁擴(kuò)容。
(18)hashCode()的作用,與equal()有什么區(qū)別?
- equals()比較兩個(gè)對(duì)象的地址值是否相等 ;hashCode()得到的是對(duì)象的存儲(chǔ)位置,可能不同對(duì)象會(huì)得到相同值
- 有兩個(gè)對(duì)象,若equals()相等,則hashcode()一定相等;hashcode()不等,則equals()一定不相等;hashcode()相等,equals()可能相等、可能不等。
- 使用equals()比較兩個(gè)對(duì)象是否相等效率較低,最快辦法是先用hashCode()比較,如果hashCode()不相等,則這兩個(gè)對(duì)象肯定不相等;如果hashCode()相等,此時(shí)再用equal()比較,如果equal()也相等,則這兩個(gè)對(duì)象的確相等。
七、Hander
(1)談?wù)勏C(jī)制Hander?有哪些要素?流程是怎樣的?
負(fù)責(zé)跨線程通信,這是因?yàn)樵谥骶€程不能做耗時(shí)操作,而子線程不能更新UI,所以當(dāng)子線程中進(jìn)行耗時(shí)操作后需要更新UI時(shí),通過Handler將有關(guān)UI的操作切換到主線程中執(zhí)行。
Handler:負(fù)責(zé)發(fā)送(Handler.sendMessage())和處理(Handler.handleMessage())Message消息。按照先進(jìn)先出執(zhí)行,內(nèi)部使用的是單鏈表的結(jié)構(gòu)。
Message:需要被傳遞的消息,消息中包含了消息ID、數(shù)據(jù)、處理消息對(duì)象等數(shù)據(jù)。
MessageQueue:負(fù)責(zé)存儲(chǔ)和管理Handler發(fā)送過來的Message,內(nèi)部通過單鏈表維護(hù)消息隊(duì)列,插入和刪除上有優(yōu)勢(shì)。在next方法中從消息隊(duì)列MessageQueue中阻塞式地取出一個(gè)Message。
Looper:負(fù)責(zé)關(guān)聯(lián)線程以及消息的分發(fā),在該線程下從MessageQueue獲取Message,分發(fā)給Handler,Looper創(chuàng)建的時(shí)候會(huì)創(chuàng)建一個(gè) MessageQueue,調(diào)用loop()方法的時(shí)候消息循環(huán)開始,其中會(huì)不斷調(diào)用messageQueue的next()方法,當(dāng)有消息就處理,否則阻塞在messageQueue的next()方法中。當(dāng)Looper的quit()被調(diào)用的時(shí)候會(huì)調(diào)用messageQueue的quit(),此時(shí)next()會(huì)返回null,然后loop()方法也就跟著退出。
在主線程創(chuàng)建的時(shí)候會(huì)創(chuàng)建一個(gè)Looper,同時(shí)也會(huì)在在Looper內(nèi)部創(chuàng)建一個(gè)消息隊(duì)列。而在創(chuàng)鍵Handler的時(shí)候取出當(dāng)前線程的Looper,并通過該Looper對(duì)象獲得消息隊(duì)列,然后Handler在子線程中通過MessageQueue.enqueueMessage在消息隊(duì)列中添加一條Message。通過Looper.loop() 開啟消息循環(huán)不斷輪詢調(diào)用 MessageQueue.next(),取得對(duì)應(yīng)的Message并且通過Handler.dispatchMessage傳遞給Handler,最終調(diào)用Handler.handlerMessage處理消息。
(2)Message可以如何創(chuàng)建?哪種效果更好,為什么?
- Message msg = new Message();
- Message msg = Message.obtain();
- Message msg = handler.obtainMessage();
- 后兩種方法都是從整個(gè)Messge池中返回一個(gè)新的Message實(shí)例,能有效避免重復(fù)Message創(chuàng)建對(duì)象,因此更鼓勵(lì)這種方式創(chuàng)建Message
(3)Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死?
- 主線程的主要方法就是消息循環(huán),一旦退出消息循環(huán),那么你的應(yīng)用也就退出了,Looer.loop()方法可能會(huì)引起主線程的阻塞,但只要它的消息循環(huán)沒有被阻塞,能一直處理事件就不會(huì)產(chǎn)生ANR異常。
- 導(dǎo)致ANR的不是主線程阻塞,而是主線程的Looper消息處理過程發(fā)生了任務(wù)阻塞,無法響應(yīng)手勢(shì)操作,不能及時(shí)刷新UI。雖然主線程在沒有消息可處理的時(shí)候是阻塞的,但是只要保證有消息的時(shí)候能夠立刻處理,程序是不會(huì)無響應(yīng)的。
(4)為什么系統(tǒng)不建議在子線程訪問UI?
- UI控件不是安全的線程,在多線程中并發(fā)訪問可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)。而不對(duì)UI控件的訪問加上鎖機(jī)制的原因上鎖會(huì)讓UI控件變得復(fù)雜和低效、上鎖后會(huì)阻塞某些進(jìn)程的執(zhí)行。
(5)一個(gè)Thread能否創(chuàng)建多個(gè)Handler,創(chuàng)建多個(gè)Looper,Handler跟Looper之間的對(duì)應(yīng)關(guān)系?
- 一個(gè)Thread只能有一個(gè)Looper,一個(gè)MessageQueen,可以有多個(gè)Handler
MessageQueue可以處理來自多個(gè)Handler的Message - 一個(gè)Looper可以關(guān)聯(lián)多個(gè)Handler,一個(gè)Handler只能關(guān)聯(lián)一個(gè)Looper。
(6)可以在子線程直接new一個(gè)Handler嗎?那該怎么做?
- 不可以,因?yàn)樵谥骶€程中內(nèi)部包含一個(gè)Looper,直接創(chuàng)建Handler會(huì)自動(dòng)關(guān)聯(lián)Looper,處理消息。
- 由于子線程的Looper需要手動(dòng)去創(chuàng)建,所以要在子線程創(chuàng)建Handler要先創(chuàng)建Looper,并開啟Looper循環(huán)
(7)使用Hanlder的postDealy()后消息隊(duì)列會(huì)發(fā)生什么變化?
這個(gè)Message并不是先等待一定時(shí)間再放入到MessageQueue中,而是直接進(jìn)入并阻塞當(dāng)前線程,到時(shí)間就喚醒它。如果此時(shí)要加入新消息,該消息隊(duì)列的隊(duì)頭跟delay時(shí)間相比更長(zhǎng),則插入到頭部,按照觸發(fā)時(shí)間進(jìn)行排序,隊(duì)頭的時(shí)間最小、隊(duì)尾的時(shí)間最大
(8)Handler 引起的內(nèi)存泄露原因以及最佳解決方案
- Handler允許我們發(fā)送延時(shí)消息,如果在延時(shí)期間用戶關(guān)閉了Activity,那么該Activity會(huì)泄露。 這個(gè)泄露是因?yàn)镸essage會(huì)持有Handler,而又因?yàn)?Java 的特性,內(nèi)部類會(huì)持有外部類,使得 Activity會(huì)被Handler持有,這樣最終就導(dǎo)致Activity泄露。
- 將Handler定義成靜態(tài)的內(nèi)部類,在內(nèi)部持有Activity的弱引用,并在Acitivity的onDestroy()中調(diào)用handler.removeCallbacksAndMessages(null)及時(shí)移除所有消息
(9)Android中還了解哪些方便線程切換的類?
- AsyncTask:一種輕量級(jí)的異步任務(wù)類。底層封裝了線程池和Handler,便于執(zhí)行后臺(tái)任務(wù)以及在子線程中進(jìn)行UI操作。
- HandlerThread:一種具有消息循環(huán)的線程,其內(nèi)部可使用Handler。
- IntentService:是一種異步、會(huì)自動(dòng)停止的服務(wù),內(nèi)部采用HandlerThread。
八、線程
(1)什么是線程
- 線程就是進(jìn)程中運(yùn)行的多個(gè)子任務(wù),是操作系統(tǒng)調(diào)用的最小單元。
(2)線程的狀態(tài)
- 新建狀態(tài)(NEW):new出來,還沒有調(diào)用start
- 可運(yùn)行狀態(tài)(RUNNABLE):調(diào)用start進(jìn)入可運(yùn)行狀態(tài),可能運(yùn)行也可能沒有運(yùn)行,取決于操作系統(tǒng)的調(diào)度
- 阻塞狀態(tài)(BLOCKED):被鎖阻塞,暫時(shí)不活動(dòng),阻塞狀態(tài)是線程阻塞在進(jìn)入
- 等待狀態(tài)(WAITING):不活動(dòng),不運(yùn)行任何代碼,等待線程調(diào)度器調(diào)度,wait、sleep用來暫停當(dāng)前線程的執(zhí)行。
- 超時(shí)等待(TIMED_WAITING):在指定時(shí)間自行返回
- 終止?fàn)顟B(tài)(TERMINATED):包括正常終止和異常終止
(3)實(shí)現(xiàn)多線程方式
- Thread:繼承Thread,重寫run方法。在構(gòu)造方法中實(shí)現(xiàn)Runnable,重寫Runnable的run方法。
- AsyncTask:繼承AsyncTask,重寫AsyncTask的五個(gè)核心方法。
- IntentService:繼承IntentService,重寫onHandleIntent方法。
- ThreadHandler:創(chuàng)建HandlerThread實(shí)例,啟動(dòng)HandlerThread,在Handler中關(guān)聯(lián)HandlerThread的Looper。
(4)線程之間是如何通信的?
- 通過Handler機(jī)制
- runOnUiThread(Runnable r)
- View.post(Runnable r)
- 共享變量
- 廣播
(5)如何將一個(gè)Thread線程變成Looper線程?Looper線程有哪些特點(diǎn)?
- 通過Looper.prepare()可將一個(gè)Thread線程轉(zhuǎn)換成Looper線程。
- Looper線程和普通Thread不同,它通過MessageQueue來存放消息和事件、Looper.loop()進(jìn)行消息輪詢。
(6)這里的ThreadLocal有什么作用?
- ThreadLocal類可實(shí)現(xiàn)線程本地存儲(chǔ)的功能,把共享數(shù)據(jù)的可見范圍限制在同一個(gè)線程之內(nèi),無須同步就能保證線程之間不出現(xiàn)數(shù)據(jù)爭(zhēng)用的問題,這里可理解為ThreadLocal幫助Handler找到本線程的Looper。
(7)請(qǐng)說出與線程同步以及線程調(diào)度相關(guān)的方法
- wait():使一個(gè)線程處于等待(阻塞)狀態(tài),并且釋放所持有的對(duì)象的鎖。
- sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),調(diào)用此方法要處理InterruptedException異常。
- notify():?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程,當(dāng)然在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無關(guān)。
- notityAll():?jiǎn)拘阉刑幱诘却隣顟B(tài)的線程,該方法并不是將對(duì)象的鎖給所有線程,而是讓它們競(jìng)爭(zhēng),只有獲得鎖的線程才能進(jìn)入就緒狀態(tài);
(8)Thread類的sleep()方法和對(duì)象的wait()方法都可以讓線程暫停執(zhí)行,它們有什么區(qū)別?
- sleep()方法是線程類的靜態(tài)方法,調(diào)用此方法會(huì)讓當(dāng)前線程暫停執(zhí)行指定的時(shí)間,將執(zhí)行機(jī)會(huì)讓給其他線程,但是對(duì)象的鎖依然保持,因此在休眠結(jié)束后會(huì)自動(dòng)回復(fù)
- wait()是object類的方法,調(diào)用對(duì)象的wait()方法導(dǎo)致當(dāng)前線程放棄對(duì)象的鎖(線程暫停執(zhí)行),進(jìn)入對(duì)象的等待池,只有調(diào)用對(duì)象的notify()方法時(shí)太能喚醒等待池中的線程進(jìn)入等鎖池,如果線程重新獲得對(duì)象的鎖就可以進(jìn)入就緒狀態(tài)。
(9)為什么線程通信的方法wait(), notify()和notifyAll()被定義在Object類里?
Java的每個(gè)對(duì)象中都有一個(gè)鎖(monitor,也可以成為監(jiān)視器) 并且wait(),notify()等方法用于等待對(duì)象的鎖或者通知其他線程對(duì)象的監(jiān)視器可用。在Java的線程中并沒有可供任何對(duì)象使用的鎖和同步器。這就是為什么這些方法是Object類的一部分,這樣Java的每一個(gè)類都有用于線程間通信的基本方法
(10)如何確保線程安全?
- synchronized關(guān)鍵字
- Lock關(guān)鍵字
(11)什么是死鎖(Deadlock)?如何分析和避免死鎖?
- 線程死鎖是指由于兩個(gè)或者多個(gè)線程互相持有對(duì)方所需要的資源,導(dǎo)致這些線程處于等待狀態(tài),無法前往執(zhí)行。
- 我們需要查看Java應(yīng)用程序的線程轉(zhuǎn)儲(chǔ)。我們需要找出那些狀態(tài)為BLOCKED的線程和他們等待的資源。每個(gè)資源都有一個(gè)唯一的id,用這個(gè)id我們可以找出哪些線程已經(jīng)擁有了它的對(duì)象鎖。
- 避免嵌套鎖,只在需要的地方使用鎖和避免無限期等待是避免死鎖的通常辦法
**(12)什么是線程調(diào)度器和時(shí)間分片?
- 線程調(diào)度器是一個(gè)操作系統(tǒng)服務(wù),它負(fù)責(zé)為Runnable狀態(tài)的線程分配CPU時(shí)間。一旦我們創(chuàng)建一個(gè)線程并啟動(dòng)它,它的執(zhí)行便依賴于線程調(diào)度器的實(shí)現(xiàn)。
- 時(shí)間分片是指將可用的CPU時(shí)間分配給可用的Runnable線程的過程。分配CPU時(shí)間可以基于線程優(yōu)先級(jí)或者線程等待的時(shí)間。
(13)什么是線程池?線程池的優(yōu)點(diǎn)?如何創(chuàng)建一個(gè)Java線程池?線程池的種類?
- 線程池的工作原理:線程池可以減少創(chuàng)建和銷毀線程的次數(shù),從而減少系統(tǒng)資源的消耗,當(dāng)一個(gè)任務(wù)提交到線程池時(shí),首先判斷核心線程池中的線程是否已經(jīng)滿了,如果沒滿,則創(chuàng)建一個(gè)核心線程執(zhí)行任務(wù),否則進(jìn)入下一步判斷工作隊(duì)列是否已滿,沒有滿則加入工作隊(duì)列,否則執(zhí)行下一步判斷線程數(shù)是否達(dá)到了最大值,如果不是,則創(chuàng)建非核心線程執(zhí)行任務(wù),否則執(zhí)行飽和策略,默認(rèn)拋出異常。
- 重用線程池中的線程,避免線程的創(chuàng)建和銷毀帶來的性能消耗;有效控制線程池的最大并發(fā)數(shù),避免大量的線程之間因互相搶占系統(tǒng)資源而導(dǎo)致阻塞現(xiàn)象;
- 通過ThreadPoolExecutor構(gòu)造方法創(chuàng)建線程池參數(shù):
corePoolSize:核心池的大小
maximumPoolSize:線程池最大線程數(shù)
keepAliveTime:表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止
Unit:參數(shù)keepAliveTime的時(shí)間單位
workQueue:一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù)
threadFactory:線程工廠,主要用來創(chuàng)建線程
handler:表示當(dāng)拒絕處理任務(wù)時(shí)的策略 - FixedThreadPool:可重用固定線程數(shù)的線程池,只有核心線程,沒有非核心線程,核心線程不會(huì)被回收,有任務(wù)時(shí),有空閑的核心線程就用核心線程執(zhí)行,沒有則加入隊(duì)列排隊(duì)。
- SingleThreadExecutor:?jiǎn)尉€程線程池,只有一個(gè)核心線程,沒有非核心線程,當(dāng)任務(wù)到達(dá)時(shí),如果沒有運(yùn)行線程,則創(chuàng)建一個(gè)線程執(zhí)行,如果正在運(yùn)行則加入隊(duì)列等待,可以保證所有任務(wù)在一個(gè)線程中按照順序執(zhí)行。
- CachedThreadPool:按需創(chuàng)建的線程池,沒有核心線程,非核心線程有Integer.MAX_VALUE個(gè)。
- ScheduledThreadPoolExecutor:繼承自ThreadPoolExecutor,用于延時(shí)執(zhí)行任務(wù)或定期執(zhí)行任務(wù),核心線程數(shù)固定,線程總數(shù)為Integer.MAX_VALUE
(14)什么是線程安全?保障線程安全有哪些手段?
- 線程安全就是多線程訪問同一代碼,不會(huì)產(chǎn)生不確定結(jié)果。
- 原子性:提供互斥訪問,同一時(shí)刻只能有一個(gè)線程對(duì)數(shù)據(jù)進(jìn)行操作(synchronized)
- 可見性:一個(gè)線程對(duì)主內(nèi)存的修改可以及時(shí)地被其他線程看到(synchronized,volatile)
- 有序性:程序代碼按照指令順序執(zhí)行
(15)ReentrantLock和synchronized的區(qū)別?
- synchronized執(zhí)行時(shí)如果拋出異常交給jvm處理,會(huì)釋放鎖。
- ReentrantLock 可以用來替換synchronized ReentrantLock手動(dòng)開鎖,必須要必須要必須要手動(dòng)釋放鎖,因?yàn)橐謩?dòng)釋放鎖,所以報(bào)異常時(shí)并不會(huì)釋放鎖。
- ReentrantLock可以使用tryLock嘗試拿下鎖,根據(jù)是否拿到鎖進(jìn)行不同的業(yè)務(wù)處理,也可以指定允許等待鎖的時(shí)間。
- ReentrantLock默認(rèn)構(gòu)造器為非公平鎖,構(gòu)造器傳true可以指定為公平鎖。
- ReentrantLock還可以打斷拿鎖,嘗試拿鎖,指定線程在哪個(gè)條件上等待
(16)synchronized和volatile的區(qū)別?
- synchronized能保證操作的原子性,而volatile不可以,假設(shè)線程A和線程B同時(shí)讀取到變量a值,A修改a后將值更新到主內(nèi)存,同時(shí)B也修改a值會(huì)覆蓋A的修改操作
- synchronized可修飾變量、方法和類,而volatile只能修飾變量
- synchronized可能會(huì)造成線程阻塞,而volatile不會(huì)造成線程的阻塞
(17)synchronized同步代碼塊還有同步方法本質(zhì)上鎖住的是誰?為什么?
- 本質(zhì)上鎖住的是對(duì)象。在java虛擬機(jī)中,每個(gè)對(duì)象和類在邏輯上都和一個(gè)監(jiān)視器相關(guān)聯(lián),synchronized本質(zhì)上是對(duì)一個(gè)對(duì)象監(jiān)視器的獲取。當(dāng)執(zhí)行同步代碼塊或同步方法時(shí),執(zhí)行方法的線程必須先獲得該對(duì)象的監(jiān)視器,才能進(jìn)入同步代碼塊或同步方法;而沒有獲取到的線程將會(huì)進(jìn)入阻塞隊(duì)列,直到成功獲取對(duì)象監(jiān)視器的線程執(zhí)行結(jié)束并釋放鎖后,才會(huì)喚醒阻塞隊(duì)列的線程,使其重新嘗試對(duì)對(duì)象監(jiān)視器的獲取。
(18)線程和進(jìn)程有什么區(qū)別?
- 地址空間:同一進(jìn)程的線程共享本進(jìn)程的地址空間,而進(jìn)程之間則是獨(dú)立的地址空間。
- 資源擁有:同一進(jìn)程內(nèi)的線程共享本進(jìn)程的資源如內(nèi)存、I/O、cpu等,但是進(jìn)程之間的資源是獨(dú)立的。
- 健壯性:一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其他進(jìn)程產(chǎn)生影響,而線程間共享地址空間,線程有自己的堆棧和局部變量,一個(gè)線程崩潰會(huì)導(dǎo)致整個(gè)進(jìn)程崩潰掉。
- 資源占用:進(jìn)程切換時(shí),消耗的資源大,效率不高。所以涉及到頻繁的切換時(shí),使用線程要好于進(jìn)程。
- 執(zhí)行過程:每個(gè)獨(dú)立的進(jìn)程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序入口。但是線程不能獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
- 進(jìn)程是操作系統(tǒng)分配和管理資源的單位,線程是CPU調(diào)度和管理的單位,是CPU調(diào)度的最小單元。
九、AsyncTask、HandlerThread、IntentService
(1)AsyncTask相比Handler有什么優(yōu)點(diǎn)?不足呢?
Handler的缺點(diǎn):代碼相對(duì)臃腫;多任務(wù)同時(shí)執(zhí)行時(shí)不易精確控制線程。
AsyncTask的優(yōu)點(diǎn):創(chuàng)建異步任務(wù)更簡(jiǎn)單,直接繼承AsyncTask便可實(shí)現(xiàn)后臺(tái)異步任務(wù)的執(zhí)行和進(jìn)度的回調(diào)更新UI,而無需編寫任務(wù)線程和Handler實(shí)例就能完成相同的任務(wù)。
(2)使用AsyncTask需要注意什么?
- 不要直接調(diào)用onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()和onCancelled()方法
- 開始和結(jié)束異步任務(wù)的方法必須在主線程調(diào)用,一個(gè)異步對(duì)象只能調(diào)用一次execute()方法,
(3)AsyncTask中的五個(gè)核心方法
- onPreExecute():運(yùn)行在主線程,在異步任務(wù)執(zhí)行之前被調(diào)用??捎糜谶M(jìn)行一些界面上的初始化操作。
- doInBackground():運(yùn)行在子線程,用于處理所有耗時(shí)任務(wù),更新UI需調(diào)用 publishProgress()方法。任務(wù)一旦完成就通過return語句將任務(wù)的執(zhí)行結(jié)果返回。
- onProgressUpdate():運(yùn)行在主線程,在后臺(tái)任務(wù)中調(diào)用publishProgress()之后該方法會(huì)被調(diào)用,可利用方法中攜帶的參數(shù)如Progress來對(duì)UI進(jìn)行相應(yīng)地更新。
- onPostExecute():運(yùn)行在主線程,在異步任務(wù)執(zhí)行完畢并通過return語句返回時(shí)被調(diào)用,可利用方法中返回的數(shù)據(jù)來進(jìn)行一些UI操作。
- onCancelled():運(yùn)行在主線程,當(dāng)異步任務(wù)被取消時(shí)調(diào)用,用于界面取消的更新。
(4)AsyncTask中使用的線程池大???
- SerialExecutor:用于任務(wù)的排隊(duì),默認(rèn)是串行的線程池,在3.0以前核心線程數(shù)為5、線程池大小為128,而3.0以后變?yōu)橥粫r(shí)間只能處理一個(gè)任務(wù)
- THREAD_POOL_EXECUTOR:用于真正執(zhí)行任務(wù)。
(5)HandlerThread有什么特點(diǎn)?
- HandlerThread是一個(gè)線程類,它繼承自Thread。與普通Thread不同,HandlerThread具有消息循環(huán)的效果,這是因?yàn)樗鼉?nèi)部HandlerThread.run()方法中有Looper,能通過Looper.prepare()來創(chuàng)建消息隊(duì)列,并通過Looper.loop()來開啟消息循環(huán)。
(6)快速實(shí)現(xiàn)子線程使用Handler
- 實(shí)例化一個(gè)HandlerThread對(duì)象,參數(shù)是該線程名稱;
- 通過handlerThread.start()啟動(dòng)線程;
- 實(shí)例化一個(gè)Handler并傳入handlerThread中l(wèi)ooper對(duì)象,使得與HandlerThread綁定;
- 利用handler即可執(zhí)行異步任務(wù);
- 當(dāng)不需要HandlerThread時(shí),通過quit()或quitSafely()方法來終止線程的執(zhí)行。
(7)IntentService的工作原理?
- 在IntentService.onCreate()方法中實(shí)例化一個(gè)HandlerThread,利用其內(nèi)部的Looper會(huì)實(shí)例化一個(gè)ServiceHandler對(duì)象;
- 任務(wù)請(qǐng)求的Intent會(huì)被封裝到Message并通過ServiceHandler發(fā)送給Looper的MessageQueue,最終在HandlerThread中執(zhí)行;
- 在ServiceHandler.handleMessage()中會(huì)調(diào)用IntentService.onHandleIntent()和stopSelf(),可在onHandleIntent方法中處理后臺(tái)任務(wù)的邏輯,stopSelf方法終止服務(wù)。
(8)IntentService的特點(diǎn)?
- 相比于線程:由于是服務(wù),優(yōu)先級(jí)比線程高,更不容易被系統(tǒng)殺死。因此較適合執(zhí)行一些高優(yōu)先級(jí)的后臺(tái)任務(wù)。
- 相比于普通Service:可自動(dòng)創(chuàng)建子線程來執(zhí)行任務(wù),且任務(wù)執(zhí)行完畢后自動(dòng)退出。
(9)為何不用bindService方式創(chuàng)建IntentService?
- 當(dāng)有Intent任務(wù)請(qǐng)求時(shí)會(huì)把Intent封裝到Message,然后ServiceHandler會(huì)把消息發(fā)送出,而發(fā)送消息是在onStartCommand()完成的,只能通過startService()才可走該生命周期方法,因此不能通過bindService創(chuàng)建IntentService。
十、序列化
(1)什么是序列化?
序列化表示將一個(gè)對(duì)象轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)臓顟B(tài)。序列化后的對(duì)象可以在網(wǎng)絡(luò)上進(jìn)行傳輸,也可以存儲(chǔ)到本地,還可以實(shí)現(xiàn)跨進(jìn)程傳輸。
(2)Serializable接口和Parcelable接口的區(qū)別?為何推薦使用后者?
- Serializable:Java序列化接口,將一個(gè)對(duì)象轉(zhuǎn)化成可存儲(chǔ)或可傳輸?shù)臓顟B(tài),操作簡(jiǎn)單、效率低、開銷大,ObjectOutputStream和ObjectInputStream過程都需要大量的I/O操作。適合將對(duì)象序列化到存儲(chǔ)設(shè)備或?qū)?duì)象序列化后通過網(wǎng)絡(luò)設(shè)備傳輸。
- Serializable的實(shí)現(xiàn),只需要實(shí)現(xiàn)Serializable即可。這只是給對(duì)象打了一個(gè)標(biāo)記,系統(tǒng)會(huì)自動(dòng)將其序列化。
- Parcelable:Android序列化接口,將一個(gè)對(duì)象進(jìn)行分解,且分解后的每一個(gè)部分都是傳遞可支持的類型。操作比較麻煩、效率高。主要用在內(nèi)存的序列化。
- Parcelabel 的實(shí)現(xiàn),需要在類中添加一個(gè)靜態(tài)成員變量CREATOR,這個(gè)變量需要繼承 Parcelable.Creator 接口。