-
1、應(yīng)用程序在什么情況需要?jiǎng)?chuàng)建Context對(duì)象的愿卒?應(yīng)用程序App共有多少個(gè)Context查库?
應(yīng)用程序創(chuàng)建Context實(shí)例的 情況有如下幾種情況:1鼎姊、創(chuàng)建Application 對(duì)象時(shí)屠缭, 而且整個(gè)App共一個(gè)Application對(duì)象 2箍鼓、創(chuàng)建Service對(duì)象時(shí) 3崭参、創(chuàng)建Activity對(duì)象時(shí)
總Context實(shí)例個(gè)數(shù) = Service個(gè)數(shù) + Activity個(gè)數(shù) + 1(Application對(duì)應(yīng)的Context實(shí)例)
-
2呵曹、Context和activity、service何暮、Application關(guān)系
image.png - 3奄喂、Application里面attachBaseContext和onCreate函數(shù)調(diào)用順序
Application-> attachBaseContext ();
ContentProvider:onCreate()
Application:onCreate()
- 4、你是怎么理解多線程間的有序性海洼,可見(jiàn)性和原子性的跨新?
原子性:執(zhí)行一個(gè)或者多個(gè)操作的時(shí)候,要么全部執(zhí)行坏逢,要么都不執(zhí)行域帐,并且中間過(guò)程中不會(huì)被打斷。Java中的原子性可以通過(guò)獨(dú)占鎖和CAS去保證是整。
可見(jiàn)性:指多線程訪問(wèn)同一個(gè)變量的時(shí)候肖揣,一個(gè)線程修改了變量的值,其他線程能夠立刻看得到修改的值浮入。鎖和volatile能夠保證可見(jiàn)性龙优。
有序性:程序執(zhí)行的順序按照代碼先后的順序執(zhí)行。鎖和volatile能夠保證有序性事秀。 - 5彤断、compileSdkVersion, minSdkVersion 和 targetSdkVersion區(qū)別是什么?
compileSdkVersion:SDK編譯版本,即:告訴 Gradle 用哪個(gè) Android SDK 版本編譯你的應(yīng)用
minSdkVersion:App安裝時(shí)要求的設(shè)備的最低android版本易迹。低于該版本宰衙,無(wú)法安裝
targetSdkVersion:一個(gè)用于指定應(yīng)用的目標(biāo)API級(jí)別的整數(shù)。如果未設(shè)置睹欲,默認(rèn)與miniSdkVersion相同菩浙。一個(gè)用于指定應(yīng)用的目標(biāo)API級(jí)別的整數(shù)。如果未設(shè)置句伶,默認(rèn)與miniSdkVersion相同劲蜻。
該屬性用于通知系統(tǒng),您已針對(duì)目標(biāo)版本進(jìn)行測(cè)試考余,并且系統(tǒng)不應(yīng)通過(guò)啟用任何兼容性行為先嬉,以保持您的應(yīng)用與目標(biāo)版本的向前兼容性。應(yīng)用仍可在較低版本上運(yùn)行楚堤。
如果平臺(tái)的 API 級(jí)別高于應(yīng)用 targetSdkVersion 所聲明的版本疫蔓,系統(tǒng)便可通過(guò)啟用兼容性行為含懊,確保應(yīng)用繼續(xù)以您所期望的方式工作(即應(yīng)用的外觀和行為保持為targetSdkVersion的)。
一般來(lái)說(shuō)衅胀,miniSdkVersion要低于targetSdkVersion岔乔。
- 6、String,StringBuffer和StringBuilder的區(qū)別滚躯?
String:String屬于不可變對(duì)象雏门,每次修改都會(huì)生成新的對(duì)象。
StringBuilder:可變對(duì)象掸掏,非多線程安全茁影,效率高于StringBuffer
StringBuffer:可變對(duì)象,多線程安全丧凤。
效率:StringBuilder>StringBuffer>String
- 7募闲、equals()和==的區(qū)別
(1)基本類型比較:
①使用雙等號(hào) == 比較的是值是否相等。
②基本數(shù)據(jù)類型無(wú)equals方法(沒(méi)有意義)愿待。
(2)引用類型比較:
①重寫(xiě)了equals方法浩螺,比如String。
第一種情況:使用==比較的是String的引用是否指向了同一塊內(nèi)存仍侥。
第二種情況:使用equals比較的是String的引用的對(duì)象內(nèi)容是否相等要出。
②沒(méi)有重寫(xiě)equals方法,比如User等自定義類访圃。
==和equals比較的都是引用是否指向了同一塊內(nèi)存厨幻。
-
8、Activity生命周期及橫豎屏切換
image.png
1腿时、新建一個(gè)Activity况脆,并把各個(gè)生命周期打印出來(lái)
2、運(yùn)行Activity批糟,得到如下信息
onCreate-->
onStart-->
onResume-->
3格了、按crtl+f12切換成橫屏?xí)r
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
4、再按crtl+f12切換成豎屏?xí)r徽鼎,發(fā)現(xiàn)打印了兩次相同的log
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
5盛末、修改AndroidManifest.xml,把該Activity添加 android:configChanges="orientation"否淤,執(zhí)行步驟3
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
6悄但、再執(zhí)行步驟4,發(fā)現(xiàn)不會(huì)再打印相同信息石抡,但多打印了一行onConfigChanged
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onConfigurationChanged-->
7檐嚣、把步驟5的android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",執(zhí)行步驟3啰扛,就只打印onConfigChanged
onConfigurationChanged-->
8嚎京、執(zhí)行步驟4
onConfigurationChanged-->
onConfigurationChanged-->
“mcc“ 移動(dòng)國(guó)家號(hào)碼嗡贺,由三位數(shù)字組成,每個(gè)國(guó)家都有自己獨(dú)立的MCC鞍帝,可以識(shí)別手機(jī)用戶所屬國(guó)家诫睬。
“mnc“ 移動(dòng)網(wǎng)號(hào),在一個(gè)國(guó)家或者地區(qū)中帕涌,用于區(qū)分手機(jī)用戶的服務(wù)商摄凡。
“l(fā)ocale“ 所在地區(qū)發(fā)生變化。
“touchscreen“ 觸摸屏已經(jīng)改變宵膨。(這不應(yīng)該常發(fā)生架谎。)
“keyboard“ 鍵盤模式發(fā)生變化炸宵,例如:用戶接入外部鍵盤輸入辟躏。
“keyboardHidden“ 用戶打開(kāi)手機(jī)硬件鍵盤
“navigation“ 導(dǎo)航型發(fā)生了變化。(這不應(yīng)該常發(fā)生土全。)
“orientation“ 設(shè)備旋轉(zhuǎn)捎琐,橫向顯示和豎向顯示模式切換。
“fontScale“ 全局字體大小縮放發(fā)生改變
總結(jié):
1裹匙、不設(shè)置Activity的android:configChanges時(shí)瑞凑,切屏?xí)匦抡{(diào)用各個(gè)生命周期入蛆,切橫屏?xí)r會(huì)執(zhí)行一次哗总,切豎屏?xí)r會(huì)執(zhí)行兩次
2激涤、設(shè)置Activity的android:configChanges="orientation"時(shí)进副,切屏還是會(huì)重新調(diào)用各個(gè)生命周期脏毯,切橫豺总、豎屏?xí)r只會(huì)執(zhí)行一次
3署恍、設(shè)置Activity的android:configChanges="orientation|keyboardHidden"時(shí)兴溜,切屏不會(huì)重新調(diào)用各個(gè)生命周期项鬼,只會(huì)執(zhí)行onConfigurationChanged方法
對(duì)android:configChanges屬性哑梳,一般認(rèn)為有以下幾點(diǎn):
1、不設(shè)置Activity的android:configChanges時(shí)绘盟,切屏?xí)匦抡{(diào)用各個(gè)生命周期鸠真,切橫屏?xí)r會(huì)執(zhí)行一次,切豎屏?xí)r會(huì)執(zhí)行兩次
2龄毡、設(shè)置Activity的android:configChanges="orientation"時(shí)吠卷,切屏還是會(huì)重新調(diào)用各個(gè)生命周期,切橫沦零、豎屏?xí)r只會(huì)執(zhí)行一次
3祭隔、設(shè)置Activity的android:configChanges="orientation|keyboardHidden"時(shí),切屏不會(huì)重新調(diào)用各個(gè)生命周期蠢终,只會(huì)執(zhí)行onConfigurationChanged方法
但是序攘,自從Android 3.2(API 13)茴她,在設(shè)置Activity的android:configChanges="orientation|keyboardHidden"后,還是一樣會(huì)重新調(diào)用各個(gè)生命周期的程奠。因?yàn)閟creen size也開(kāi)始跟著設(shè)備的橫豎切換而改變丈牢。所以,在AndroidManifest.xml里設(shè)置的MiniSdkVersion和 TargetSdkVersion屬性大于等于13的情況下瞄沙,如果你想阻止程序在運(yùn)行時(shí)重新加載Activity己沛,除了設(shè)置"orientation",你還必須設(shè)置"ScreenSize"距境。
- 8-1申尼、onSaveInstanceState()和onRestoreInstanceState()詳解
1、什么情況下執(zhí)行:
1.1垫桂、如果是用戶自動(dòng)按下返回鍵师幕,或程序調(diào)用finish()退出程序,是不會(huì)觸發(fā)onSaveInstanceState()和onRestoreInstanceState()的诬滩。
1.2霹粥、每次用戶旋轉(zhuǎn)屏幕時(shí),您的Activity將被破壞并重新創(chuàng)建疼鸟。當(dāng)屏幕改變方向時(shí)后控,系統(tǒng)會(huì)破壞并重新創(chuàng)建前臺(tái)Activity,因?yàn)槠聊慌渲靡迅目站担腁ctivity可能需要加載替代資源(例如布局)浩淘。即會(huì)執(zhí)行onSaveInstanceState()和onRestoreInstanceState()的。
2吴攒、onRestoreInstanceState()什么情況下執(zhí)行张抄?
2.1、onCreate()您可以選擇執(zhí)行onRestoreInstanceState()舶斧,而不是在系統(tǒng)調(diào)用onStart()方法之后恢復(fù)狀態(tài)欣鳖。
2.2、系統(tǒng)onRestoreInstanceState()只有在存在保存狀態(tài)的情況下才會(huì)恢復(fù)茴厉,因此您不需要檢查是否Bundle為空
- 8-2泽台、android:configChanges屬性
- 9、Service啟動(dòng)方式
1.startService
①.定義一個(gè)類繼承service
②.在manifest.xml文件中配置該service
③.使用context的startService(intent)啟動(dòng)該service
④.不再使用時(shí),調(diào)用stopService(Intent)停止該服務(wù)
2.bindService
①.創(chuàng)建bindService服務(wù)段,繼承自service并在類中,創(chuàng)建一個(gè)實(shí)現(xiàn)binder接口的實(shí)例對(duì)象并提供公共方法給客戶端調(diào)用
②.從onbind()回調(diào)方法返回此binder實(shí)例
③.在客戶端中,從onserviceconnected()回調(diào)方法接收binder,并使用提供的方法調(diào)用綁定服務(wù)
- 10矾缓、Activity的啟動(dòng)模式
①.standard模式
a.Activity的默認(rèn)啟動(dòng)模式
b.每啟動(dòng)一個(gè)Activity就會(huì)在棧頂創(chuàng)建一個(gè)新的實(shí)例怀酷。例如:鬧鐘程序
缺點(diǎn):當(dāng)Activity已經(jīng)位于棧頂時(shí),而再次啟動(dòng)Activity時(shí)還需要在創(chuàng)建一個(gè)新的實(shí)例嗜闻,不能直接復(fù)用蜕依。
②.singleTop模式
特點(diǎn):該模式會(huì)判斷要啟動(dòng)的Activity實(shí)例是否位于棧頂,如果位于棧頂直接復(fù)用,否則創(chuàng)建新的實(shí)例样眠。 例如:瀏覽器的書(shū)簽
?缺點(diǎn):如果Activity并未處于棧頂位置友瘤,則可能還會(huì)創(chuàng)建多個(gè)實(shí)例。
③.singleTask模式
特點(diǎn):使Activity在整個(gè)應(yīng)用程序中只有一個(gè)實(shí)例檐束。每次啟動(dòng)Activity時(shí)系統(tǒng)首先檢查棧中是否存在當(dāng)前Activity實(shí)例辫秧,如果存在
則直接復(fù)用,并把當(dāng)前Activity之上所有實(shí)例全部出棧被丧。例如:瀏覽器主界面
④.singleInstance模式
特點(diǎn):該模式的Activity會(huì)啟動(dòng)一個(gè)新的任務(wù)棧來(lái)管理Activity實(shí)例盟戏,并且該勢(shì)力在整個(gè)系統(tǒng)中只有一個(gè)。無(wú)論從那個(gè)任務(wù)棧中啟動(dòng)該Activity甥桂,都會(huì)是該Activity所在的任務(wù)棧轉(zhuǎn)移到前臺(tái)柿究,從而使Activity顯示。主要作用是為了在不同程序中共享一個(gè)Activity
- 11黄选、Touch事件傳遞機(jī)制
在我們點(diǎn)擊屏幕時(shí)蝇摸,會(huì)有下列事件發(fā)生:
Activity調(diào)用dispathTouchEvent()方法,把事件傳遞給Window糕簿;
Window再將事件交給DecorView(DecorView是View的根布局)探入;
DecorView再傳遞給ViewGroup狡孔;
Activity ——> Window ——> DecorView ——> ViewGroup——> View
事件分發(fā)的主要有三個(gè)關(guān)鍵方法
dispatchTouchEvent() 分發(fā)
onInterceptTouchEvent() 攔截 懂诗,只有ViewGroup獨(dú)有此方法
onTouchEvent() 處理觸摸事件
Activity首先調(diào)用dispathTouchEvent()進(jìn)行分發(fā),接著調(diào)用super向下傳遞
ViewGroup首先調(diào)用dispathTouchEvent()進(jìn)行分發(fā)苗膝,接著會(huì)調(diào)用onInterceptTouchEvent()(攔截事件)殃恒。若攔截事件返回為true,表示攔截辱揭,事件不會(huì)向下層的ViewGroup或者View傳遞离唐;false,表示不攔截问窃,繼續(xù)分發(fā)事件亥鬓。默認(rèn)是false,需要提醒一下域庇,View是沒(méi)有onInterceptTouchEvent()方法的
事件在ViewGroup和ViewGroup嵌戈、ViewGroup和View之間進(jìn)行傳遞,最終到達(dá)View听皿;
View調(diào)用dispathTouchEvent()方法熟呛,然后在OnTouchEvent()進(jìn)行處理事件;OnTouchEvent() 返回true尉姨,表示消耗此事件庵朝,不再向下傳遞;返回false,表示不消耗事件九府,交回上層處理椎瘟。
- 12、介紹下實(shí)現(xiàn)一個(gè)自定義View的基本流程
①.自定義View的屬性 編寫(xiě)attr.xml文件
②.在layout布局文件中引用侄旬,同時(shí)引用命名空間
③.在View的構(gòu)造方法中獲得我們自定義的屬性 降传,在自定義控件中進(jìn)行讀取(構(gòu)造方法拿到attr.xml文件值)
④.重寫(xiě)onMesure
⑥.重寫(xiě)onDraw
- 13勾怒、ANR是什么婆排?怎樣避免和解決ANR
Application Not Responding,即應(yīng)用無(wú)響應(yīng)
出現(xiàn)的原因有三種:
a)KeyDispatchTimeout(5 seconds)主要類型按鍵或觸摸事件在特定時(shí)間內(nèi)無(wú)響應(yīng)
b)BroadcastTimeout(10 seconds)BoradcastReceiver在特定的時(shí)間內(nèi)無(wú)法處理
c)ServiceTimeout(20 seconds)小概率類型Service在特定的時(shí)間內(nèi)無(wú)法處理完成
避免ANR最核心的一點(diǎn)就是在主線程減少耗時(shí)操作笔链。通常需要從那個(gè)以下幾個(gè)方案下手:
a)使用子線程處理耗時(shí)IO操作
b)降低子線程優(yōu)先級(jí)段只,使用Thread或者HandlerThread時(shí),調(diào)用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設(shè)置優(yōu)先級(jí)鉴扫,否則仍然會(huì)降低程序響應(yīng)赞枕,因?yàn)槟J(rèn)Thread的優(yōu)先級(jí)和主線程相同
c)使用Handler處理子線程結(jié)果,而不是使用Thread.wait()或者Thread.sleep()來(lái)阻塞主線程
d)Activity的onCreate和onResume回調(diào)中盡量避免耗時(shí)的代碼
e)BroadcastReceiver中onReceiver代碼也要盡量減少耗時(shí)操作坪创,建議使用intentService處理炕婶。intentService是一個(gè)異步的,會(huì)自動(dòng)停止的服務(wù)莱预,很好解決了傳統(tǒng)的Service中處理完耗時(shí)操作忘記停止并銷毀Service的問(wèn)題
- 14柠掂、內(nèi)存泄漏和內(nèi)存溢出是什么?
內(nèi)存溢出 out of memory:是指程序在申請(qǐng)內(nèi)存時(shí)依沮,沒(méi)有足夠的內(nèi)存空間供其使用涯贞,出現(xiàn)out of memory;比如申請(qǐng)了一個(gè)integer,但給它存了long才能存下的數(shù)危喉,那就是內(nèi)存溢出宋渔。內(nèi)存溢出通俗的講就是內(nèi)存不夠用。
內(nèi)存泄露 memory leak:是指程序在申請(qǐng)內(nèi)存后辜限,無(wú)法釋放已申請(qǐng)的內(nèi)存空間皇拣,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重薄嫡,無(wú)論多少內(nèi)存,遲早會(huì)被占光
- 15 氧急、內(nèi)存泄露原因以及解決:
一、Handler 引起的內(nèi)存泄漏岂座。
解決:將Handler聲明為靜態(tài)內(nèi)部類态蒂,就不會(huì)持有外部類SecondActivity的引用,其生命周期就和外部類無(wú)關(guān)费什,
如果Handler里面需要context的話钾恢,可以通過(guò)弱引用方式引用外部類
二手素、單例模式引起的內(nèi)存泄漏。
解決:Context是ApplicationContext瘩蚪,由于ApplicationContext的生命周期是和app一致的泉懦,不會(huì)導(dǎo)致內(nèi)存泄漏
三、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例引起的內(nèi)存泄漏疹瘦。
解決:把內(nèi)部類修改為靜態(tài)的就可以避免內(nèi)存泄漏了
四崩哩、非靜態(tài)匿名內(nèi)部類引起的內(nèi)存泄漏。
解決:將匿名內(nèi)部類設(shè)置為靜態(tài)的言沐。
五邓嘹、注冊(cè)/反注冊(cè)未成對(duì)使用引起的內(nèi)存泄漏。
注冊(cè)廣播接受器险胰、EventBus等汹押,記得解綁。
六起便、資源對(duì)象沒(méi)有關(guān)閉引起的內(nèi)存泄漏棚贾。
在這些資源不使用的時(shí)候,記得調(diào)用相應(yīng)的類似close()榆综、destroy()妙痹、recycler()、release()等方法釋放鼻疮。
七怯伊、集合對(duì)象沒(méi)有及時(shí)清理引起的內(nèi)存泄漏。
通常會(huì)把一些對(duì)象裝入到集合中陋守,當(dāng)不使用的時(shí)候一定要記得及時(shí)清理集合震贵,讓相關(guān)對(duì)象不再被引用。
- 16水评、圖片加載框架有哪些?他們之間的區(qū)別是什么媚送?
ImageLoader :
優(yōu)點(diǎn):
① 支持下載進(jìn)度監(jiān)聽(tīng)中燥;
② 可以在 View 滾動(dòng)中暫停圖片加載;
③ 默認(rèn)實(shí)現(xiàn)多種內(nèi)存緩存算法這幾個(gè)圖片緩存都可以配置緩存算法塘偎,不過(guò) ImageLoader 默認(rèn)實(shí)現(xiàn)了較多緩存算法疗涉,如 Size 最大先刪除、使用最少先刪除吟秩、最近最少使用咱扣、先進(jìn)先刪除、時(shí)間最長(zhǎng)先刪除等涵防;
④ 支持本地緩存文件名規(guī)則定義闹伪;
缺點(diǎn):
缺點(diǎn)在于不支持GIF圖片加載, 緩存機(jī)制沒(méi)有和http的緩存很好的結(jié)合, 完全是自己的一套緩存機(jī)制
Picasso:
優(yōu)點(diǎn):
① 自帶統(tǒng)計(jì)監(jiān)控功能,支持圖片緩存使用的監(jiān)控,包括緩存命中率偏瓤、已使用內(nèi)存大小杀怠、節(jié)省的流量等。
② 支持優(yōu)先級(jí)處理
③ 支持延遲到圖片尺寸計(jì)算完成加載
④ 支持飛行模式厅克、并發(fā)線程數(shù)根據(jù)網(wǎng)絡(luò)類型而變赔退,手機(jī)切換到飛行模式或網(wǎng)絡(luò)類型變換時(shí)會(huì)自動(dòng)調(diào)整線程池最大并發(fā)數(shù)。
⑤ “無(wú)”本地緩存证舟。Picasso 自己沒(méi)有實(shí)現(xiàn)本地緩存硕旗,而由okhttp 去實(shí)現(xiàn),這樣的好處是可以通過(guò)請(qǐng)求 Response Header 中的 Cache-Control 及 Expired 控制圖片的過(guò)期時(shí)間女责。
缺點(diǎn):
于不支持GIF卵渴,默認(rèn)使用ARGB_8888格式緩存圖片,緩存體積大鲤竹。
Glide:
優(yōu)點(diǎn):
① 圖片緩存->媒體緩存 浪读,支持 Gif、WebP辛藻、縮略圖碘橘。甚至是 Video。
② 支持優(yōu)先級(jí)處理
③ 與 Activity/Fragment 生命周期一致吱肌,支持 trimMemory
④ 支持 okhttp痘拆、Volley。Glide 默認(rèn)通過(guò) UrlConnection 獲取數(shù)據(jù)氮墨,可以配合 okhttp 或是 Volley 使用纺蛆。實(shí)際 ImageLoader、Picasso 也都支持 okhttp规揪、Volley桥氏。
⑤ 內(nèi)存友好,內(nèi)存緩存更小圖片猛铅,圖片默認(rèn)使用默認(rèn) RGB565 而不是 ARGB888
缺點(diǎn):
清晰度差字支,但可以設(shè)置
Fresco:
優(yōu)點(diǎn):
① 圖片存儲(chǔ)在安卓系統(tǒng)的匿名共享內(nèi)存, 而不是虛擬機(jī)的堆內(nèi)存中,所以不會(huì)因?yàn)閳D片加載而導(dǎo)致oom, 同時(shí)也減少垃圾回收器頻繁調(diào)用回收Bitmap導(dǎo)致的界面卡頓,性能更高.
② 漸進(jìn)式加載JPEG圖片, 支持圖片從模糊到清晰加載
③ 圖片可以以任意的中心點(diǎn)顯示在ImageView, 而不僅僅是圖片的中心.
④ JPEG圖片改變大小也是在native進(jìn)行的, 不是在虛擬機(jī)的堆內(nèi)存, 同樣減少OOM
⑤ 很好的支持GIF圖片的顯示
缺點(diǎn):
框架較大, 影響Apk體積,使用較繁瑣
- 17奸忽、網(wǎng)絡(luò)框架有哪些堕伪?他們之間的區(qū)別是什么?
Xutils
這個(gè)框架非常全面栗菜,可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求欠雌,可以進(jìn)行圖片加載處理,可以數(shù)據(jù)儲(chǔ)存疙筹,還可以對(duì)view進(jìn)行注解富俄,使用這個(gè)框架非常方便禁炒,但是缺點(diǎn)也是非常明顯的,使用這個(gè)項(xiàng)目蛙酪,會(huì)導(dǎo)致項(xiàng)目對(duì)這個(gè)框架依賴非常的嚴(yán)重齐苛,一旦這個(gè)框架出現(xiàn)問(wèn)題,那么對(duì)項(xiàng)目來(lái)說(shuō)影響非常大的
OKhttp
Android開(kāi)發(fā)中是可以直接使用現(xiàn)成的api進(jìn)行網(wǎng)絡(luò)請(qǐng)求的桂塞。就是使用HttpClient,HttpUrlConnection進(jìn)行操作凹蜂。okhttp針對(duì)Java和Android程序,封裝的一個(gè)高性能的http請(qǐng)求庫(kù)阁危,支持同步玛痊,異步,而且okhttp又封裝了線程池狂打,封裝了數(shù)據(jù)轉(zhuǎn)換擂煞,封裝了參數(shù)的使用,錯(cuò)誤處理等趴乡。API使用起來(lái)更加的方便对省。但是我們?cè)陧?xiàng)目中使用的時(shí)候仍然需要自己在做一層封裝,這樣才能使用的更加的順手晾捏。
Volley
Volley是Google官方出的一套小而巧的異步請(qǐng)求庫(kù)蒿涎,該框架封裝的擴(kuò)展性很強(qiáng),支持HttpClient惦辛、HttpUrlConnection劳秋, 甚至支持OkHttp,而且Volley里面也封裝了ImageLoader胖齐,所以如果你愿意你甚至不需要使用圖片加載框架玻淑,不過(guò)這塊功能沒(méi)有一些專門的圖片加載框架強(qiáng)大,對(duì)于簡(jiǎn)單的需求可以使用呀伙,稍復(fù)雜點(diǎn)的需求還是需要用到專門的圖片加載框架补履。Volley也有缺陷,比如不支持post大數(shù)據(jù)区匠,所以不適合上傳文件干像。不過(guò)Volley設(shè)計(jì)的初衷本身也就是為頻繁的、數(shù)據(jù)量小的網(wǎng)絡(luò)請(qǐng)求而生驰弄。
Retrofit
Retrofit是Square公司出品的默認(rèn)基于OkHttp封裝的一套R(shí)ESTful網(wǎng)絡(luò)請(qǐng)求框架,RESTful是目前流行的一套api設(shè)計(jì)的風(fēng)格速客, 并不是標(biāo)準(zhǔn)戚篙。Retrofit的封裝可以說(shuō)是很強(qiáng)大,里面涉及到一堆的設(shè)計(jì)模式,可以通過(guò)注解直接配置請(qǐng)求溺职,可以使用不同的http客戶端岔擂,雖然默認(rèn)是用http 位喂,可以使用不同Json Converter 來(lái)序列化數(shù)據(jù),同時(shí)提供對(duì)RxJava的支持乱灵,使用Retrofit + OkHttp + RxJava + Dagger2 可以說(shuō)是目前比較潮的一套框架塑崖,但是需要有比較高的門檻。
Volley VS OkHttp
Volley的優(yōu)勢(shì)在于封裝的更好痛倚,而使用OkHttp你需要有足夠的能力再進(jìn)行一次封裝规婆。而OkHttp的優(yōu)勢(shì)在于性能更高,因?yàn)?OkHttp基于NIO和Okio 蝉稳,所以性能上要比 Volley更快抒蚜。IO 和 NIO這兩個(gè)都是Java中的概念,如果我從硬盤讀取數(shù)據(jù)耘戚,第一種方式就是程序一直等嗡髓,數(shù)據(jù)讀完后才能繼續(xù)操作這種是最簡(jiǎn)單的也叫阻塞式IO,還有一種是你讀你的,程序接著往下執(zhí)行,等數(shù)據(jù)處理完你再來(lái)通知我收津,然后再處理回調(diào)饿这。而第二種就是 NIO 的方式,非阻塞式撞秋, 所以NIO當(dāng)然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基礎(chǔ)上做的一個(gè)更簡(jiǎn)單长捧、高效處理數(shù)據(jù)流的一個(gè)庫(kù)。理論上如果Volley和OkHttp對(duì)比的話部服,更傾向于使用 Volley唆姐,因?yàn)閂olley內(nèi)部同樣支持使用OkHttp,這點(diǎn)OkHttp的性能優(yōu)勢(shì)就沒(méi)了, 而且 Volley 本身封裝的也更易用廓八,擴(kuò)展性更好些奉芦。
OkHttp VS Retrofit
毫無(wú)疑問(wèn),Retrofit 默認(rèn)是基于 OkHttp 而做的封裝剧蹂,這點(diǎn)來(lái)說(shuō)沒(méi)有可比性声功,肯定首選 Retrofit。
Volley VS Retrofit
這兩個(gè)庫(kù)都做了不錯(cuò)的封裝宠叼,但Retrofit解耦的更徹底,尤其Retrofit2.0出來(lái)先巴,Jake對(duì)之前1.0設(shè)計(jì)不合理的地方做了大量重構(gòu), 職責(zé)更細(xì)分冒冬,而且Retrofit默認(rèn)使用OkHttp,性能上也要比Volley占優(yōu)勢(shì)伸蚯,再有如果你的項(xiàng)目如果采用了RxJava ,那更該使用 Retrofit 简烤。所以這兩個(gè)庫(kù)相比剂邮,Retrofit更有優(yōu)勢(shì),在能掌握兩個(gè)框架的前提下該優(yōu)先使用 Retrofit横侦。但是Retrofit門檻要比Volley稍高些挥萌,要理解他的原理绰姻,各種用法,想徹底搞明白還是需要花些功夫的引瀑,如果你對(duì)它一知半解狂芋,那還是建議在商業(yè)項(xiàng)目使用Volley吧。
- 18憨栽、兩個(gè)Activity 之間跳轉(zhuǎn)時(shí)必然會(huì)執(zhí)行的是哪幾個(gè)方法帜矾?
首先定義兩個(gè)Activity,分別為A和B徒像。
當(dāng)我們?cè)贏中激活B時(shí)黍特,A調(diào)用onPause()方法,此時(shí)B出現(xiàn)在屏幕時(shí)锯蛀,B調(diào)用onCreate()灭衷、onStart()、onResume()旁涤。
這個(gè)時(shí)候B【B不是一個(gè)透明的窗體或?qū)υ捒虻男问健恳呀?jīng)覆蓋了A的窗體翔曲,A會(huì)調(diào)用onStop()方法。
- 19劈愚、Android Handler消息機(jī)制
作用:
跨線程通信瞳遍。當(dāng)子線程中進(jìn)行耗時(shí)操作后需要更新UI時(shí),通過(guò)Handler將有關(guān)UI的操作切換到主線程中執(zhí)行菌羽。
四要素:
1掠械、 Message(消息):需要被傳遞的消息,其中包含了消息ID注祖,消息處理對(duì)象以及處理的數(shù)據(jù)等猾蒂,由MessageQueue統(tǒng)一列隊(duì),最終由Handler處理是晨。
2肚菠、MessageQueue(消息隊(duì)列):用來(lái)存放Handler發(fā)送過(guò)來(lái)的消息,內(nèi)部通過(guò)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表罩缴,等待Looper的抽取蚊逢。
3、 Handler(處理者):負(fù)責(zé)Message的發(fā)送及處理箫章。通過(guò) Handler.sendMessage() 向消息池發(fā)送各種消息事件烙荷;通過(guò) Handler.handleMessage() 處理相應(yīng)的消息事件。
4檬寂、 Looper(消息泵):通過(guò)Looper.loop()不斷地從MessageQueue中抽取Message奢讨,按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者。
Handler.sendMessage()發(fā)送消息時(shí)焰薄,會(huì)通過(guò)MessageQueue.enqueueMessage()向MessageQueue中添加一條消息拿诸; 通過(guò)Looper.loop()開(kāi)啟循環(huán)后,不斷輪詢調(diào)用MessageQueue.next()塞茅; 調(diào)用目標(biāo)Handler.dispatchMessage()去傳遞消息亩码,目標(biāo)Handler收到消息后調(diào)用Handler.handlerMessage()處理消息
- 20、synchronized volatile關(guān)鍵字有什么區(qū)別野瘦?以及還有哪些同樣功能的關(guān)鍵字
(1) volatile是變量修飾符描沟,而synchronized則作用于一段代碼或者方法。
(2) volatile只是在線程內(nèi)存和main memory(主內(nèi)存)間同步某個(gè)變量的值鞭光;而synchronized通過(guò)鎖定和解鎖某個(gè)監(jiān)視器同步所有變量的值吏廉。顯然synchronized要比volatile消耗更多資源。
const惰许、final席覆、lock
- 21、界面卡頓的原因有哪些汹买?
UI線程(main)有耗時(shí)操作
視圖渲染時(shí)間過(guò)長(zhǎng)佩伤,導(dǎo)致卡頓
- 22、GC機(jī)制
垃圾回收需要完成兩件事:找到垃圾晦毙,回收垃圾生巡。
找到垃圾一般的話有兩種方法:
? 引用計(jì)數(shù)法:
當(dāng)一個(gè)對(duì)象被引用時(shí),它的引用計(jì)數(shù)器會(huì)加一见妒,垃圾回收時(shí)會(huì)清理掉引用計(jì)數(shù)為0的對(duì)象孤荣。但這種方法有一個(gè)問(wèn)題,比方說(shuō)有兩個(gè)對(duì)象 A 和 B须揣,A 引用了 B盐股,B 又引用了 A,除此之外沒(méi)有別的對(duì)象引用 A 和 B返敬,那么 A 和 B 在我們看來(lái)已經(jīng)是垃圾對(duì)象遂庄,需要被回收,但它們的引用計(jì)數(shù)不為 0劲赠,沒(méi)有達(dá)到回收的條件涛目。正因?yàn)檫@個(gè)循環(huán)引用的問(wèn)題,Java 并沒(méi)有采用引用計(jì)數(shù)法凛澎。
? 可達(dá)性分析法:
我們把 Java 中對(duì)象引用的關(guān)系看做一張圖霹肝,從根級(jí)對(duì)象不可達(dá)的對(duì)象會(huì)被垃圾收集器清除。根級(jí)對(duì)象一般包括 Java 虛擬機(jī)棧中的對(duì)象塑煎、本地方法棧中的對(duì)象沫换、方法區(qū)中的靜態(tài)對(duì)象和常量池中的常量。
回收垃圾的話有這么四種方法:
? 標(biāo)記清除算法:
顧名思義分為兩步最铁,標(biāo)記和清除讯赏。首先標(biāo)記到需要回收的垃圾對(duì)象垮兑,然后回收掉這些垃圾對(duì)象。標(biāo)記清除算法的缺點(diǎn)是清除垃圾對(duì)象后會(huì)造成內(nèi)存的碎片化漱挎。
? 復(fù)制算法:
復(fù)制算法是將存活的對(duì)象復(fù)制到另一塊內(nèi)存區(qū)域中系枪,并做相應(yīng)的內(nèi)存整理工作。復(fù)制算法的優(yōu)點(diǎn)是可以避免內(nèi)存碎片化磕谅,缺點(diǎn)也顯而易見(jiàn)私爷,它需要兩倍的內(nèi)存。
標(biāo)記整理算法:
標(biāo)記整理算法也是分兩步膊夹,先標(biāo)記后整理衬浑。它會(huì)標(biāo)記需要回收的垃圾對(duì)象,清除掉垃圾對(duì)象后會(huì)將存活的對(duì)象壓縮放刨,避免了內(nèi)存的碎片化工秩。
? 分代算法:
分代算法將對(duì)象分為新生代和老年代對(duì)象。那么為什么做這樣的區(qū)分呢宏榕?主要是在Java運(yùn)行中會(huì)產(chǎn)生大量對(duì)象拓诸,這些對(duì)象的生命周期會(huì)有很大的不同,有的生命周期很長(zhǎng)麻昼,有的甚至使用一次之后就不再使用奠支。所以針對(duì)不同生命周期的對(duì)象采用不同的回收策略,這樣可以提高GC的效率抚芦。
新生代對(duì)象分為三個(gè)區(qū)域:Eden 區(qū)和兩個(gè) Survivor 區(qū)倍谜。新創(chuàng)建的對(duì)象都放在 Eden區(qū),當(dāng) Eden 區(qū)的內(nèi)存達(dá)到閾值之后會(huì)觸發(fā) Minor GC叉抡,這時(shí)會(huì)將存活的對(duì)象復(fù)制到一個(gè) Survivor 區(qū)中尔崔,這些存活對(duì)象的生命存活計(jì)數(shù)會(huì)加一。這時(shí) Eden 區(qū)會(huì)閑置褥民,當(dāng)再一次達(dá)到閾值觸發(fā) Minor GC 時(shí)季春,會(huì)將Eden區(qū)和之前一個(gè) Survivor 區(qū)中存活的對(duì)象復(fù)制到另一個(gè) Survivor 區(qū)中,采用的是我之前提到的復(fù)制算法消返,同時(shí)它們的生命存活計(jì)數(shù)也會(huì)加一载弄。
這個(gè)過(guò)程會(huì)持續(xù)很多遍,直到對(duì)象的存活計(jì)數(shù)達(dá)到一定的閾值后會(huì)觸發(fā)一個(gè)叫做晉升的現(xiàn)象:新生代的這個(gè)對(duì)象會(huì)被放置到老年代中撵颊。
老年代中的對(duì)象都是經(jīng)過(guò)多次 GC 依然存活的生命周期很長(zhǎng)的 Java 對(duì)象宇攻。當(dāng)老年代的內(nèi)存達(dá)到閾值后會(huì)觸發(fā) Major GC,采用的是標(biāo)記整理算法倡勇。
- 23逞刷、JVM內(nèi)存區(qū)域的劃分,哪些區(qū)域會(huì)發(fā)生 OOM
JVM 的內(nèi)存區(qū)域可以分為兩類:線程私有和=區(qū)域和線程共有的區(qū)域。
線程私有的區(qū)域:程序計(jì)數(shù)器夸浅、JVM 虛擬機(jī)棧仑最、本地方法棧
線程共有的區(qū)域:堆、方法區(qū)题篷、運(yùn)行時(shí)常量池
1词身、程序計(jì)數(shù)器。 每個(gè)線程有有一個(gè)私有的程序計(jì)數(shù)器番枚,任何時(shí)間一個(gè)線程都只會(huì)有一個(gè)方法正在執(zhí)行,也就是所謂的當(dāng)前方法损敷。程序計(jì)數(shù)器存放的就是這個(gè)當(dāng)前方法的JVM指令地址葫笼。
2、JVM虛擬機(jī)棧拗馒。 創(chuàng)建線程的時(shí)候會(huì)創(chuàng)建線程內(nèi)的虛擬機(jī)棧路星,棧中存放著一個(gè)個(gè)的棧幀,對(duì)應(yīng)著一個(gè)個(gè)方法的調(diào)用诱桂。JVM 虛擬機(jī)棧有兩種操作洋丐,分別是壓棧和出站。棧幀中存放著局部變量表挥等、方法返回值和方法的正秤丫或異常退出的定義等等。
3肝劲、本地方法棧迁客。 跟 JVM 虛擬機(jī)棧比較類似,只不過(guò)它支持的是 Native 方法辞槐。
4掷漱、堆。 堆是內(nèi)存管理的核心區(qū)域榄檬,用來(lái)存放對(duì)象實(shí)例卜范。幾乎所有創(chuàng)建的對(duì)象實(shí)例都會(huì)直接分配到堆上。所以堆也是垃圾回收的主要區(qū)域鹿榜,垃圾收集器會(huì)對(duì)堆有著更細(xì)的劃分海雪,最常見(jiàn)的就是把堆劃分為新生代和老年代。
5犬缨、方法區(qū)喳魏。方法區(qū)主要存放類的結(jié)構(gòu)信息,比如靜態(tài)屬性和方法等等怀薛。
6刺彩、運(yùn)行時(shí)常量池。運(yùn)行時(shí)常量池位于方法區(qū)中昧谊,主要存放各種常量信息魂仍。
其實(shí)除了程序計(jì)數(shù)器,其他的部分都會(huì)發(fā)生 OOM兰怠。
1畦攘、堆霸妹。 通常發(fā)生的 OOM 都會(huì)發(fā)生在堆中,最常見(jiàn)的可能導(dǎo)致 OOM 的原因就是內(nèi)存泄漏知押。
2叹螟、JVM虛擬機(jī)棧和本地方法棧。 當(dāng)我們寫(xiě)一個(gè)遞歸方法台盯,這個(gè)遞歸方法沒(méi)有循環(huán)終止條件罢绽,最終會(huì)導(dǎo)致 StackOverflow 的錯(cuò)誤。當(dāng)然静盅,如果椓技郏空間擴(kuò)展失敗,也是會(huì)發(fā)生 OOM 的蒿叠。
方法區(qū)明垢。方法區(qū)現(xiàn)在基本上不太會(huì)發(fā)生 OOM,但在早期內(nèi)存中加載的類信息過(guò)多的情況下也是會(huì)發(fā)生 OOM 的市咽。
- 24痊银、類加載過(guò)程
Java 中類加載分為 3 個(gè)步驟:加載、鏈接魂务、初始化曼验。
加載。 加載是將字節(jié)碼數(shù)據(jù)從不同的數(shù)據(jù)源讀取到JVM內(nèi)存粘姜,并映射為 JVM 認(rèn)可的數(shù)據(jù)結(jié)構(gòu)鬓照,也就是 生成Class 對(duì)象的過(guò)程。數(shù)據(jù)源可以是 Jar 文件孤紧、Class 文件等等豺裆。如果數(shù)據(jù)的格式并不是 ClassFile 的結(jié)構(gòu),則會(huì)報(bào) ClassFormatError号显。
鏈接臭猜。 鏈接是類加載的核心部分,這一步分為 3 個(gè)步驟:驗(yàn)證押蚤、準(zhǔn)備蔑歌、解析。
驗(yàn)證揽碘。 驗(yàn)證是保證JVM安全的重要步驟次屠。JVM需要校驗(yàn)字節(jié)信息是否符合規(guī)范园匹,避免惡意信息和不規(guī)范數(shù)據(jù)危害JVM運(yùn)行安全。如果驗(yàn)證出錯(cuò)劫灶,則會(huì)報(bào)VerifyError裸违。
準(zhǔn)備。 這一步會(huì)創(chuàng)建靜態(tài)變量本昏,并為靜態(tài)變量開(kāi)辟內(nèi)存空間供汛。
解析。 這一步會(huì)將符號(hào)引用替換為直接引用涌穆。
初始化怔昨。 初始化會(huì)為靜態(tài)變量賦值,并執(zhí)行靜態(tài)代碼塊中的邏輯蒲犬。
- 25朱监、雙親委派模型
類加載器大致分為3類:?jiǎn)?dòng)類加載器、擴(kuò)展類加載器原叮、應(yīng)用程序類加載器。
啟動(dòng)類加載器主要加載 jre/lib下的jar文件巡蘸。
擴(kuò)展類加載器主要加載 jre/lib/ext 下的jar文件奋隶。
應(yīng)用程序類加載器主要加載 classpath 下的文件。
所謂的雙親委派模型就是當(dāng)加載一個(gè)類時(shí)悦荒,會(huì)優(yōu)先使用父類加載器加載唯欣,當(dāng)父類加載器無(wú)法加載時(shí)才會(huì)使用子類加載器去加載。這么做的目的是為了避免類的重復(fù)加載搬味。
- 26境氢、HashMap 的原理
HashMap 的內(nèi)部可以看做數(shù)組+鏈表的復(fù)合結(jié)構(gòu)。數(shù)組被分為一個(gè)個(gè)的桶(bucket)碰纬。哈希值決定了鍵值對(duì)在數(shù)組中的尋址萍聊。具有相同哈希值的鍵值對(duì)會(huì)組成鏈表。需要注意的是當(dāng)鏈表長(zhǎng)度超過(guò)閾值(默認(rèn)是8)的時(shí)候會(huì)觸發(fā)樹(shù)化悦析,鏈表會(huì)變成樹(shù)形結(jié)構(gòu)寿桨。
把握HashMap的原理需要關(guān)注4個(gè)方法:hash、put强戴、get亭螟、resize。
1骑歹、hash方法预烙。 將 key 的 hashCode 值的高位數(shù)據(jù)移位到低位進(jìn)行異或運(yùn)算。這么做的原因是有些 key 的 hashCode 值的差異集中在高位道媚,而哈希尋址是忽略容量以上高位的扁掸,這種做法可以有效避免哈希沖突翘县。
2、put 方法也糊。 put 方法主要有以下幾個(gè)步驟:
- 通過(guò) hash 方法獲取 hash 值炼蹦,根據(jù) hash 值尋址。
- 如果未發(fā)生碰撞狸剃,直接放到桶中掐隐。
- 如果發(fā)生碰撞,則以鏈表形式放在桶后钞馁。
- 當(dāng)鏈表長(zhǎng)度大于閾值后會(huì)觸發(fā)樹(shù)化虑省,將鏈表轉(zhuǎn)換為紅黑樹(shù)。
- 如果數(shù)組長(zhǎng)度達(dá)到閾值僧凰,會(huì)調(diào)用 resize 方法擴(kuò)展容量探颈。
3、get方法训措。 get 方法主要有以下幾個(gè)步驟:
- 通過(guò) hash 方法獲取 hash 值伪节,根據(jù) hash 值尋址。
- 如果與尋址到桶的 key 相等绩鸣,直接返回對(duì)應(yīng)的 value怀大。
- 如果發(fā)生沖突,分兩種情況呀闻。如果是樹(shù)化借,則調(diào)用 getTreeNode 獲取 value;如果是鏈表則通過(guò)循環(huán)遍歷查找對(duì)應(yīng)的 value捡多。
4蓖康、resize 方法。 resize 做了兩件事:
- 將原數(shù)組擴(kuò)展為原來(lái)的 2 倍
- 重新計(jì)算 index 索引值垒手,將原節(jié)點(diǎn)重新放到新的數(shù)組中蒜焊。這一步可以將原先沖突的節(jié)點(diǎn)分散到新的桶中。
- 27淫奔、ConcurrentHashMap山涡、HashMap,HashTable區(qū)別
1. HashMap不是線程安全:
在并發(fā)環(huán)境下唆迁,可能會(huì)形成環(huán)狀鏈表(擴(kuò)容時(shí)可能造成鸭丛,具體原因自行百度google或查看源碼分析),導(dǎo)致get操作時(shí)唐责,cpu空轉(zhuǎn)鳞溉,所以,在并發(fā)環(huán)境中使用HashMap是非常危險(xiǎn)的
2. HashTable是線程安全的:
HashTable和HashMap的實(shí)現(xiàn)原理幾乎一樣鼠哥,
差別:
1.HashTable不允許key和value為null熟菲;
2.HashTable是線程安全的看政。
HashTable線程安全的策略實(shí)現(xiàn)代價(jià)卻比較大,get/put所有相關(guān)操作都是synchronized的抄罕,這相當(dāng)于給整個(gè)哈希表加了一把大鎖允蚣,多線程訪問(wèn)時(shí)候,只要有一個(gè)線程訪問(wèn)或操作該對(duì)象呆贿,那其他線程只能阻塞
3嚷兔、ConcurrentHashMap是線程安全的:
JDK1.7版本: 容器中有多把鎖,每一把鎖鎖一段數(shù)據(jù)做入,這樣在多線程訪問(wèn)時(shí)不同段的數(shù)據(jù)時(shí)冒晰,就不會(huì)存在鎖競(jìng)爭(zhēng)了,這 樣便可以有效地提高并發(fā)效率竟块。這就是ConcurrentHashMap所采用的"分段鎖"思想
DK1.8版本:做了2點(diǎn)修改:
1壶运、取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存數(shù)據(jù)浪秘,采用table數(shù)組元素作為鎖蒋情,從而實(shí)現(xiàn)了對(duì)每一行數(shù)據(jù)進(jìn)行加鎖,并發(fā)控制使用Synchronized和CAS來(lái)操作
2耸携、將原先table數(shù)組+單向鏈表的數(shù)據(jù)結(jié)構(gòu)恕出,變更為table數(shù)組+單向鏈表+紅黑樹(shù)的結(jié)構(gòu).
- 27、sleep 和 wait 的區(qū)別
- sleep 方法是 Thread 類中的靜態(tài)方法违帆,wait 是 Object 類中的方法
- sleep 并不會(huì)釋放同步鎖,而 wait 會(huì)釋放同步鎖
- sleep 可以在任何地方使用金蜀,而 wait 只能在同步方法或者同步代碼塊中使用
- sleep 中必須傳入時(shí)間刷后,而 wait 可以傳,也可以不傳渊抄,不傳時(shí)間的話只有 notify 或者 notifyAll 才能喚醒尝胆,傳時(shí)間的話在時(shí)間之后會(huì)自動(dòng)喚醒
- 28、join 的用法
join 方法通常是保證線程間順序調(diào)度的一個(gè)方法护桦,它是 Thread 類中的方法含衔。比方說(shuō)在線程 A 中執(zhí)行線程 B.join(),這時(shí)線程 A 會(huì)進(jìn)入等待狀態(tài)二庵,直到線程 B 執(zhí)行完畢之后才會(huì)喚醒贪染,繼續(xù)執(zhí)行A線程中的后續(xù)方法。
?join 方法可以傳時(shí)間參數(shù)催享,也可以不傳參數(shù)杭隙,不傳參數(shù)實(shí)際上調(diào)用的是 join(0)。它的原理其實(shí)是使用了 wait 方法因妙,join 的原理如下:
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
- 29痰憎、final票髓、finally、finalize區(qū)別
final 可以修飾類铣耘、變量和方法洽沟。修飾類代表這個(gè)類不可被繼承。修飾變量代表此變量不可被改變蜗细。修飾方法表示此方法不可被重寫(xiě) (override)裆操。
finally 是保證重點(diǎn)代碼一定會(huì)執(zhí)行的一種機(jī)制。通常是使用 try-finally 或者 try-catch-finally 來(lái)進(jìn)行文件流的關(guān)閉等操作鳄乏。
finalize 是 Object 類中的一個(gè)方法跷车,它的設(shè)計(jì)目的是保證對(duì)象在垃圾收集前完成特定資源的回收。finalize 機(jī)制現(xiàn)在已經(jīng)不推薦使用橱野,并且在 JDK 9已經(jīng)被標(biāo)記為 deprecated朽缴。
- 30、Java 中單例模式
Java 中常見(jiàn)的單例模式實(shí)現(xiàn)有這么幾種:餓漢式水援、雙重判斷的懶漢式密强、靜態(tài)內(nèi)部類實(shí)現(xiàn)的單例、枚舉實(shí)現(xiàn)的單例蜗元。
雙重判斷的懶漢式:
public class SingleTon {
//需要注意的是volatile
private static volatile SingleTon mInstance;
private SingleTon() {
}
public static SingleTon getInstance() {
if (mInstance == null) {
synchronized (SingleTon.class) {
if (mInstance == null) {
mInstance=new SingleTon();
}
}
}
return mInstance;
}
}
雙重判斷的懶漢式單例既滿足了延遲初始化或渤,又滿足了線程安全。通過(guò) synchronized 包裹代碼來(lái)實(shí)現(xiàn)線程安全奕扣,通過(guò)雙重判斷來(lái)提高程序執(zhí)行的效率薪鹦。這里需要注意的是單例對(duì)象實(shí)例需要有 volatile 修飾,如果沒(méi)有 volatile 修飾惯豆,在多線程情況下可能會(huì)出現(xiàn)問(wèn)題池磁。原因是這樣的,mInstance=new SingleTon()這一句代碼并不是一個(gè)原子操作楷兽,它包含三個(gè)操作:
- 給 mInstance 分配內(nèi)存
- 調(diào)用 SingleTon 的構(gòu)造方法初始化成員變量
- 將 mInstance 指向分配的內(nèi)存空間(在這一步 mInstance 已經(jīng)不為 null 了)
我們知道 JVM 會(huì)發(fā)生指令重排地熄,正常的執(zhí)行順序是1-2-3,但發(fā)生指令重排后可能會(huì)導(dǎo)致1-3-2芯杀。我們考慮這樣一種情況端考,當(dāng)線程 A 執(zhí)行到1-3-2的3步驟暫停了,這時(shí)候線程 B 調(diào)用了 getInstance揭厚,走到了最外層的if判斷上却特,由于最外層的 if 判斷并沒(méi)有 synchronized 包裹,所以可以執(zhí)行到這一句棋弥,這時(shí)候由于線程 A 已經(jīng)執(zhí)行了步驟3核偿,此時(shí) mInstance 已經(jīng)不為 null 了,所以線程B直接返回了 mInstance顽染。但其實(shí)我們知道漾岳,完整的初始化必須走完這三個(gè)步驟轰绵,由于線程 A 只走了兩個(gè)步驟,所以一定會(huì)報(bào)錯(cuò)的尼荆。
解決的辦法就是使用 volatile 修飾 mInstance左腔,我們知道 volatile 有兩個(gè)作用:保證可見(jiàn)性和禁止指令重排,在這里關(guān)鍵在于禁止指令重排捅儒,禁止指令重排后保證了不會(huì)發(fā)生上述問(wèn)題液样。
靜態(tài)內(nèi)部類實(shí)現(xiàn)的單例
class SingletonWithInnerClass {
private SingletonWithInnerClass() {
}
private static class SingletonHolder{
private static SingletonWithInnerClass INSTANCE=new SingletonWithInnerClass();
}
public SingletonWithInnerClass getInstance() {
return SingletonHolder.INSTANCE;
}
}
由于外部類的加載并不會(huì)導(dǎo)致內(nèi)部類立即加載,只有當(dāng)調(diào)用 getInstance 的時(shí)候才會(huì)加載內(nèi)部類巧还,所以實(shí)現(xiàn)了延遲初始化鞭莽。由于類只會(huì)被加載一次,并且類加載也是線程安全的麸祷,所以滿足我們所有的需求澎怒。靜態(tài)內(nèi)部類實(shí)現(xiàn)的單例也是最為推薦的一種方式。
- 31阶牍、Java中引用類型的區(qū)別喷面,具體的使用場(chǎng)景
Java中引用類型分為四類:強(qiáng)引用、軟引用走孽、弱引用惧辈、虛引用。
強(qiáng)引用: 強(qiáng)引用指的是通過(guò) new 對(duì)象創(chuàng)建的引用磕瓷,垃圾回收器即使是內(nèi)存不足也不會(huì)回收強(qiáng)引用指向的對(duì)象盒齿。
軟引用: 軟引用是通過(guò) SoftRefrence 實(shí)現(xiàn)的,它的生命周期比強(qiáng)引用短困食,在內(nèi)存不足县昂,拋出 OOM 之前,垃圾回收器會(huì)回收軟引用引用的對(duì)象陷舅。軟引用常見(jiàn)的使用場(chǎng)景是存儲(chǔ)一些內(nèi)存敏感的緩存,當(dāng)內(nèi)存不足時(shí)會(huì)被回收审洞。
弱引用: 弱引用是通過(guò) WeakRefrence 實(shí)現(xiàn)的莱睁,它的生命周期比軟引用還短,GC 只要掃描到弱引用的對(duì)象就會(huì)回收芒澜。弱引用常見(jiàn)的使用場(chǎng)景也是存儲(chǔ)一些內(nèi)存敏感的緩存仰剿。
虛引用: 虛引用是通過(guò) FanttomRefrence 實(shí)現(xiàn)的,它的生命周期最短痴晦,隨時(shí)可能被回收南吮。如果一個(gè)對(duì)象只被虛引用引用,我們無(wú)法通過(guò)虛引用來(lái)訪問(wèn)這個(gè)對(duì)象的任何屬性和方法誊酌。它的作用僅僅是保證對(duì)象在 finalize 后部凑,做某些事情露乏。虛引用常見(jiàn)的使用場(chǎng)景是跟蹤對(duì)象被垃圾回收的活動(dòng),當(dāng)一個(gè)虛引用關(guān)聯(lián)的對(duì)象被垃圾回收器回收之前會(huì)收到一條系統(tǒng)通知涂邀。
- 32瘟仿、Exception 和 Error的區(qū)別
Exception 和 Error 都繼承于 Throwable,在 Java 中比勉,只有 Throwable 類型的對(duì)象才能被 throw 或者 catch劳较,它是異常處理機(jī)制的基本組成類型。
Exception 和 Error 體現(xiàn)了 Java 對(duì)不同異常情況的分類浩聋。Exception 是程序正常運(yùn)行中观蜗,可以預(yù)料的意外情況,可能并且應(yīng)該被捕獲衣洁,進(jìn)行相應(yīng)的處理墓捻。
Error 是指在正常情況下,不大可能出現(xiàn)的情況闸与,絕大部分 Error 都會(huì)使程序處于非正常毙替、不可恢復(fù)的狀態(tài)。既然是非正常践樱,所以不便于也不需要捕獲厂画,常見(jiàn)的 OutOfMemoryError 就是 Error 的子類。
Exception 又分為 checked Exception 和 unchecked Exception拷邢。
checked Exception 在代碼里必須顯式的進(jìn)行捕獲袱院,這是編譯器檢查的一部分。
unchecked Exception 也就是運(yùn)行時(shí)異常瞭稼,類似空指針異常忽洛、數(shù)組越界等,通常是可以避免的邏輯錯(cuò)誤环肘,具體根據(jù)需求來(lái)判斷是否需要捕獲欲虚,并不會(huì)在編譯器強(qiáng)制要求。
- 33悔雹、Looper 死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死复哆,會(huì)消耗大量資源嗎?
然后我們分解問(wèn)題腌零,一點(diǎn)點(diǎn)去看這個(gè)問(wèn)題梯找。
## Looper.loop和主線程的關(guān)系
分析Handler和應(yīng)用啟動(dòng)的時(shí)候講過(guò),在創(chuàng)建ActivityThread時(shí)益涧,主線程會(huì)創(chuàng)建Looper锈锤,并且進(jìn)入Loop循環(huán):
<pre class="prism-token token language-javascript" style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; box-sizing: border-box; list-style: inherit; margin: 0.5em 0px; padding: 1em; color: rgb(204, 204, 204); background-color: rgb(80, 85, 107); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; overflow: auto; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; word-wrap: normal; text-align: left; white-space: pre; word-break: normal; line-height: 1.5; tab-size: 4; -webkit-hyphens: none; font-size: 14px;">public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
// 這里sMainLooper賦值
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); // Looper進(jìn)入循環(huán)
throw new RuntimeException("Main thread loop unexpectedly exited");
}</pre>
loop()函數(shù)代碼就不貼了,就是一個(gè)`while(true)`的死循環(huán)。
所以Looper和主線程的關(guān)系是久免,主線程創(chuàng)建的時(shí)候會(huì)創(chuàng)建一個(gè)MainLooper浅辙,并且進(jìn)入循環(huán)。
<pre class="prism-token token language-javascript" style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; box-sizing: border-box; list-style: inherit; margin: 0.5em 0px; padding: 1em; color: rgb(204, 204, 204); background-color: rgb(80, 85, 107); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; overflow: auto; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; word-wrap: normal; text-align: left; white-space: pre; word-break: normal; line-height: 1.5; tab-size: 4; -webkit-hyphens: none; font-size: 14px;">Looper.prepareMainLooper();
Looper.loop();</pre>
## 主線程阻塞
### 關(guān)于死循環(huán)
主線程進(jìn)入一個(gè)死循環(huán)妄壶,是不是就會(huì)被阻塞摔握?
首先,思考一下丁寄,如果我們創(chuàng)建一個(gè)線程做定時(shí)檢查某個(gè)狀態(tài)氨淌,是不是也會(huì)給這個(gè)子線程做一個(gè)死循環(huán),不斷地去循環(huán)檢查狀態(tài)伊磺。當(dāng)不需要這個(gè)線程的時(shí)候盛正,改變flag讓這個(gè)子線程退出循環(huán)并銷毀。
想到這就理解屑埋,主線程也是一個(gè)線程豪筝,它也要維持自己的周期,所以也是需要一個(gè)死循環(huán)的摘能。所以死循環(huán)并不是那么讓人擔(dān)心续崖。
### 關(guān)于阻塞
這就涉及到Looper的作用了,Looper里持有一個(gè)MessageQueue团搞,這個(gè)[消息隊(duì)列](https://cloud.tencent.com/product/cmq?from=10680)存放著外部發(fā)來(lái)的消息严望,當(dāng)有消息過(guò)來(lái)的時(shí)候,Looper就會(huì)按順序把消息一個(gè)一個(gè)拿出來(lái)逻恐,進(jìn)行處理像吻。Looper的loop循環(huán)就是一個(gè)拿消息的循環(huán)。
也就是說(shuō)复隆,如果你給我發(fā)消息拨匆,我會(huì)立即去拿消息并且做響應(yīng)。你不給我消息挽拂,我就會(huì)阻塞惭每,減少CPU消耗(涉及到epoll)。
那么主線程會(huì)響應(yīng)什么消息呢亏栈?在ActivityThread里有一個(gè)命名為H的handler洪鸭,它處理所有Activity生命周期有關(guān)的事件。因?yàn)橹骶€程就是UI線程仑扑,當(dāng)UI發(fā)生變化,相關(guān)消息就會(huì)傳進(jìn)來(lái)置鼻,Looper就會(huì)處理消息镇饮。
所以:
**Looper的阻塞,前提是沒(méi)有輸入事件箕母,此時(shí)MessageQueue是空的储藐,Looper進(jìn)入空閑俱济,線程進(jìn)入阻塞,釋放CPU钙勃,等待輸入事件的喚醒蛛碌。**
## 聊聊ANR
其實(shí)擔(dān)心這個(gè)問(wèn)題的人很多都是被ANR搞怕了,因?yàn)锳NR就是UI線程做耗時(shí)操作了導(dǎo)致卡死狀態(tài)辖源,然后很多人就在想是不是UI線程進(jìn)入Loop死循環(huán)后蔚携,就出現(xiàn)卡死,其實(shí)這兩個(gè)并不是一個(gè)問(wèn)題克饶。
先上結(jié)論和上面的做個(gè)對(duì)比:
**UI耗時(shí)導(dǎo)致卡死酝蜒,前提是要有輸入事件,此時(shí)MessageQueue不是空的矾湃,Looper正常輪詢亡脑,線程并沒(méi)有阻塞,但是該事件執(zhí)行時(shí)間過(guò)長(zhǎng)(一般5秒)邀跃,而且與此期間其他的事件(按鍵按下霉咨,屏幕點(diǎn)擊..)都沒(méi)辦法處理(卡死),然后就ANR異常了拍屑。**
ANR機(jī)制的目的還是為了監(jiān)測(cè)主線程的耗時(shí)操作途戒,譬如密集CPU運(yùn)算、大量IO丽涩、復(fù)雜界面布局等棺滞,因?yàn)檫@些都會(huì)降低應(yīng)用程序的響應(yīng)能力。所以從理念上也能理解矢渊,loop死循環(huán)只是簡(jiǎn)單地處理輕量的消息操作继准,和ANR并沒(méi)有關(guān)系。
- 34矮男、類的成員變量按照什么順序初始化的移必?
成員變量按照其聲明的順序會(huì)被初始化,并且立刻被初始化為二進(jìn)制的0毡鉴,這個(gè)動(dòng)作發(fā)生在所有事件之前崔泵,也就是編譯器會(huì)立刻將分配給對(duì)象的空間初始化。
最后就是調(diào)用類的構(gòu)造方法了猪瞬。
執(zhí)行順序:
執(zhí)行父類靜態(tài)代碼 執(zhí)行子類靜態(tài)代碼
初始化父類成員變量(我們常說(shuō)的賦值語(yǔ)句)
初始化父類構(gòu)造函數(shù)
初始化子類成員變量
初始化子類構(gòu)造函數(shù)
35憎瘸、java中public,private陈瘦,protected和default的區(qū)別
作用域 | 當(dāng)前類 | 同一package | 子孫類 | 其他package |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
注意:java的訪問(wèn)控制是停留在編譯層的幌甘,也就是它不會(huì)在.class文件中留下任何的痕跡,只在編譯的時(shí)候進(jìn)行訪問(wèn)控制的檢查。其實(shí)锅风,通過(guò)反射的手段酥诽,是可以訪問(wèn)任何包下任何類中的成員,例如皱埠,訪問(wèn)類的私有成員也是可能的肮帐。
區(qū)別:
public:可以被所有其他類所訪問(wèn)
private:只能被自己訪問(wèn)和修改
protected:自身、子類及同一個(gè)包中類可以訪問(wèn)
default:同一包中的類可以訪問(wèn)边器,聲明時(shí)沒(méi)有加修飾符训枢,認(rèn)為是friendly
- 36、