Android系統(tǒng)架構
Android 系統(tǒng)架構由5部分組成僵朗,分別是:Linux Kernel县踢、Android Runtime、Libraries修然、Application Framework、Applications。
Activity 生命周期
在不同生命周期調用finish()
1愕宋、在Activity的onCreate()中調用finish()方法玻靡,則執(zhí)行的生命周期方法順序為:
onCreate() -> onDestroy()
2、在Activity的onStart()中調用finish()方法中贝,則執(zhí)行的生命周期方法順序為:
onCreate() -> onStart() -> onStop() -> onDestroy()
3囤捻、在Activity的onResume()或onPostResume()中調用finish()方法,則執(zhí)行的生命周期方法順序為:
onCreate() -> onStart() -> onResume() -> onPostResume() -> onPause() -> onStop() -> onDestroy()
4.頁面回收雄妥,重建
?onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
activity三種狀態(tài)
active:當Activity運行在屏幕前臺(處于當前任務活動棧的最上面)最蕾,此時它獲取了焦點能響應用戶的操作依溯,屬于活動狀態(tài)老厌,同一個時刻只會有一個Activity處于活動(Active)。
paused:當Activity失去焦點但仍對用戶可見(如在它之上有另一個透明的Activity或Toast黎炉、AlertDialog等彈出窗口時)它處于暫停狀態(tài)枝秤。暫停的Activity仍然是存活狀態(tài)(它保留著所有的狀態(tài)和成員信息并保持和窗口管理器的連接),但是當系統(tǒng)內存極小時可以被系統(tǒng)殺掉慷嗜。
stoped:完全被另一個Activity遮擋時處于停止狀態(tài)淀弹,它仍然在內存中保留著所有的狀態(tài)和成員信息。只是對用戶不可見庆械,當其他地方需要內存時它往往被系統(tǒng)殺掉薇溃。
第一個Activity的啟動順序:onCreate()——>onStart()——>onResume()
當另一個Activity啟動時:第一個Activity onPause()——>第二個Activity onCreate()——>onStart()——>onResume()——>第一個Activity onStop()
當返回到第一個Activity時:第二個Activity onPause()——>?第一個Activity onRestart()——>onStart()——>onResume()——>第二個Activity onStop()——>onDestroy()
從第二點可以看出,view是在onResume之后才繪制的缭乘,因此導致第一個view完全被遮擋而響應第一個activity的onStop
App進程的創(chuàng)建流程
App的進程是由zygote fork出來的沐序,運行在Android Runtime,進程創(chuàng)建圖如下:
當點擊桌面應用圖標時堕绩,會觸發(fā)startActivity策幼,startActivity發(fā)起進程便是Launcher所在進程。發(fā)起進程通過binder發(fā)送消息到system_server進程奴紧。
system_server進程檢測到目標應用所在進程沒有啟動的話特姐,就會調用Process.start來啟動目標進程,該函數(shù)通過socket向zygote進程發(fā)送創(chuàng)建新進程的請求黍氮。
Zygote進程啟動的時候運行了ZygoteInit.main函數(shù)唐含,并進入runSelectLoop循環(huán),該循環(huán)用來檢測外部的socket請求沫浆,當運行Process.start的時候捷枯,該循環(huán)檢測到請求,就會調用ZygoteConnection.runOnce函數(shù)件缸,最后創(chuàng)建出新的目標進程铜靶。
View 事件一般要經(jīng)過三個流程:分發(fā)(dispatchTouchEvent)、攔截(onInterceptTouchEvent)、消費(onTouchEvent)
dispatchTouchEvent:View 會先執(zhí)行 OnTouchListener 中 onTouch 方法争剿,如果這個監(jiān)聽器的方法返回 true 則不會將事件交由自身的 onTouchEvent 進行消費已艰。
而 ViewGroup 跟 View 不同的是,它是直接將事件交由觸摸位置上的子 View 的 ?dispatchTouchEvent 方法處理蚕苇。
onInterceptTouchEvent:這個是 ViewGroup 獨有的方法哩掺,在 dispatchTouchEvent 方法中調用,一般情況下返回 false(也就是不攔截)涩笤,ViewGroup 如果想攔截觸摸事件可以重寫此方法返回 true嚼吞,將事件交由自身的 onTouchEvent 方法處理。
onTouchEvent:一次觸摸事件會產(chǎn)生 down(按下)蹬碧、move(移動)舱禽、up(抬起)三個事件,這個方法將決定事件產(chǎn)生的效果恩沽,比如狀態(tài)選擇器的 Drawable 變換就是在這個方法中觸發(fā)的誊稚,還有最常用的點擊事件 onClick 也是在這里回調的。
總結:三個事件方法各有各的作用罗心,分發(fā)是想把事件傳遞下去里伯,攔截是不希望事件傳遞下去鹊汛,而消費是對事件的一系列處理丰辣。通過這三個方法跨算,我們可以控制觸摸事件交由誰去處理狠持,整個事件機制采用了責任鏈設計模式走触,事件會一層一層往下傳遞借帘。
1. dispatchTouchEvent
return true; 表示該view內部消化掉了所有事件
return false: 表示事件在本層不再繼續(xù)分發(fā)贿衍,并交由上層控件的onTouchEvent方法進行消費
return super.dispatchTouchEvent(ev) 默認事件將分發(fā)給本層事件攔截onInterceptTouchEvent
2. onInterceptTouchEvent
return true: 表示將事件進行攔截艇炎,并將攔截到的事件交由本層的 onTouchEvent進行處理
return false: 表示不進行事件攔截處理补憾,事件得以分發(fā)到子View
return super.onInterceptTouchEvent(ev) 默認表示不攔截該事件漫萄,并將事件傳遞給下一層的view的dispatchTouchEvent;
3. onTouchEvent
return true: 表示onTouchEvent處理完事件后消費了此事件
return false : 表示不響應事件,那么該事件將不斷的向上層view的onTouchEvent方法傳遞盈匾;
return super.onTouchEvent(ev) 表示不響應事件腾务,與return false一樣;
IPC
IPC方式有多少種:
傳統(tǒng)的IPC方式有Socket削饵、共享內存岩瘦、管道、信號量等
安卓特有的是Binder窿撬,其他系統(tǒng)也有自己定義的比如window的wfc(Windows Communication Foundation)
Bundle:Intent中傳遞Bundle數(shù)據(jù)启昧,Bundle實現(xiàn)了Parcelable接口,可以在不同進程間傳輸劈伴。
文件共享:進程通過讀寫同一個文件交換數(shù)據(jù)
AIDL
ContentProvider
Messenger:Messenger是一種輕量級的IPC方案密末,他的底層實現(xiàn)是AIDL
binder是一種進程間通訊(IPC,Interprocess Communication)的機制
binder的定義
每個進程擁有自己的獨立虛擬機,系統(tǒng)為他們分配的地址空間都是互相隔離的。
如兩個進程需要進行通訊严里,則需要使用到內核空間做載體新啼,內核空間是所有進程共享的一塊內存區(qū)域。 而用戶空間切到內核空間需要使用到系統(tǒng)api ioctl進行通訊刹碾。內核獲取用戶的數(shù)據(jù)需要使用copy_from_user燥撞,內核將數(shù)據(jù)發(fā)送給其他進程需要使用copy_to_user,這兩個方法是有性能開銷的迷帜,對于socket就是使用的這種模式
為了減少這部分的開銷物舒,內核提供了binder,binder只需要一次拷貝就可以實現(xiàn)進程通訊戏锹。
binder的是實現(xiàn)原理:
在內核空間和用戶空間都開辟一塊虛擬內存區(qū)域同時指向一塊物理地址冠胯,這樣內核需要傳遞數(shù)據(jù)給用戶空間時,只需要將數(shù)據(jù)拷貝到對應的虛擬內存地址中景用,用戶可以通過虛擬內存映射關系涵叮,獲取到內核中的數(shù)據(jù),實現(xiàn)了一次拷貝通訊伞插。
傳統(tǒng)的數(shù)據(jù)拷貝方式如socket:
用戶空間---->內核空間:`copy_from_user `
內核空間---->用戶空間:`copy_to_user`
第一遍是進程A復制到Socket通道,第二遍是Socket通道復制到進程B盾碗,兩次拷貝媚污。
而binder使用mmap機制
在內核空間和用戶空間中間使用物理地址開辟了一個映射關系
內核空間調用copy_from_user會直接將數(shù)據(jù)拷貝到內核空間并反饋到映射后的物理地址上,
由于用戶空間和物理地址也有個映射關系廷雅,用戶空間可以直接通過映射的虛擬地址指針訪問到寫入物理地址的數(shù)據(jù)耗美。
這就是binder一次拷貝的原理
有四個角色,其實很好理解航缀,兩個進程商架、一個Binder內核空間、一個ServiceManager服務芥玉。兩個進程時采用的C/S結構一個客戶端一個服務端蛇摸。
Client、Server和Service Manager實現(xiàn)在用戶空間中灿巧,Binder驅動程序實現(xiàn)在內核空間中
1赶袄、首先,Server進程要向SM注冊抠藕;告訴自己是誰饿肺,自己有什么能力;在這個場景就是Server告訴SM盾似,它叫A敬辣,它有一個object對象,可以執(zhí)行add 操作;于是SM建立了一張表:A這個名字對應進程Server
2溉跃、然后Client向SM查詢:我需要聯(lián)系一個名字叫做A的進程里面的object對象汰聋;這時候關鍵來了:進程之間通信的數(shù)據(jù)都會經(jīng)過運行在內核空間里面的驅動,驅動在數(shù)據(jù)流過的時候做了一點手腳喊积,它并不會給Client進程返回一個真正的object對象烹困,而是返回一個看起來跟object一模一樣的代理對象objectProxy,這個objectProxy也有一個add方法乾吻,但是這個add方法沒有Server進程里面object對象的add方法那個能力髓梅;objectProxy的add只是一個傀儡,它唯一做的事情就是把參數(shù)包裝然后交給驅動绎签。
3枯饿、驅動收到消息,發(fā)現(xiàn)是這個objectProxy诡必;一查表就明白了:我之前用objectProxy替換了object發(fā)送給Client了奢方,它真正應該要訪問的是object對象的add方法;于是Binder驅動通知Server進程爸舒,調用你的object對象的add方法蟋字,然后把結果發(fā)給我,Sever進程收到這個消息扭勉,照做之后將結果返回驅動鹊奖,驅動然后把結果返回給Client進程;于是整個過程就完成了涂炎。
4忠聚、由于驅動返回的objectProxy與Server進程里面原始的object是如此相似,給人感覺好像是直接把Server進程里面的對象object傳遞到了Client進程唱捣;因此两蟀,我們可以說Binder對象是可以進行跨進程傳遞的對象
5、這里隱藏了SM這一部分驅動進行的操作震缭;實際上赂毯,由于SM與Server通常不在一個進程,Server進程向SM注冊的過程也是跨進程通信蛀序,驅動也會對這個過程進行暗箱操作:SM中存在的Server端的對象實際上也是代理對象欢瞪,后面Client向SM查詢的時候,驅動會給Client返回另外一個代理對象徐裸。Sever進程的本地對象僅有一個遣鼓,其他進程所擁有的全部都是它的代理。
binder架構上面使用的是C/S架構:
三要素:
客戶端重贺,服務端和ServiceManager
整體過程:
1.注冊服務 2.獲取服務 3.使用服務
binder的大小限制
對于內核可以傳輸?shù)氖?M骑祟,但是應用層限制在1M-8K范圍內回懦,這就是在進程間傳輸過大的數(shù)據(jù)會導致崩潰的原因
為什么zygote進程跟其他進程通訊使用socket而不是binder
Binder雖然在內核,但是提供服務的是ServiceManager次企,這個時候如果要給AMS提供Binder IPC就需要等ServiceManager先初始化好怯晕,這個是沒辦法保證的,如果要保證這個先后順序又要搞多一套進程通訊就更麻煩了缸棵。
另外舟茶,由于Zygote進程只與少數(shù)幾個進程進行通訊,使用Socket通訊的開銷相對較小堵第,因此選擇Socket通訊更加合適吧凉。而且這里面是優(yōu)化過的LocalSocket效率會更高。
SystemServer->run()->createSystemContext():創(chuàng)建了系統(tǒng)的ActivityThread對象踏志,運行環(huán)境mSystemContext阀捅、systemUiContext。
SystemServer->run()->startBootstrapServices()->ActivityManagerService.Lifecycle.startService():AMS在引導服務啟動方法中针余,通過構造函數(shù)new ActivityManagerService()進行了一些對象創(chuàng)建和初始化(除activity外3大組件的管理和調度對象創(chuàng)建饲鄙;內存、電池圆雁、權限忍级、性能、cpu等的監(jiān)控等相關對象創(chuàng)建)摸柄,start()啟動服務(移除進程組颤练、啟動cpu線程、注冊權限驱负、電池等服務)。
SystemServer->run()->startBootstrapServices()->setSystemServiceManager()患雇、setInstaller()跃脊、initPowerManagement()、setSystemProcess():AMS創(chuàng)建后進行了一系列相關的初始化和設置苛吱。
setSystemProcess():將framework-res.apk的信息加入到SystemServer進程的LoadedApk中酪术,并創(chuàng)建了SystemServer進程的ProcessRecord,加入到mPidsSelfLocked翠储,由AMS統(tǒng)一管理绘雁。
SystemServer->run()->startOtherServices():AMS啟動后的后續(xù)工作,主要調用systemReady()和運行調用時傳入的goingCallback援所。
systemReady()/goingCallback:各種服務或進程等AMS啟動完成后需進一步完成的工作及系統(tǒng)相關初始化庐舟。 桌面應用在systemReady()方法中啟動,systemui在goingCallback中完成住拭。當桌面應用啟動完成后挪略,發(fā)送開機廣播ACTION_BOOT_COMPLETED历帚,到此為止。
使用步驟
服務端
1 創(chuàng)建一個服務端 也就是說創(chuàng)建一個Service
2 繼承Service,重寫onBind函數(shù)
3 創(chuàng)建內部類繼承Aidl接口重寫Aidl接口方法
客戶端
1. 把服務端的aidl 接口復制過來.他的包名必須跟服務端的包名一致,
2 通過bindService綁定服務端的服務
BroadcastReceiver廣播接收器生命周期
生命周期只有十秒左右杠娱,如果在onReceive()內做超過十秒內的事情挽牢,就會報ANR(Application No Response)程序無響應的錯誤信息。
它的生命周期為從回調onReceive()方法開始到該方法返回結果后結束摊求。
動態(tài)注冊過的BroadcastReceiver需要解注冊禽拔,否則可能造成內存泄漏。因為動態(tài)注冊的BroadcastReceiver持有注冊他的Activity的引用室叉。
動態(tài)注冊廣播睹栖。
context.registerReceiver(new BroadcastReceiver() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onReceive(Context context, Intent intent) {
? ? ? ? ? ? }
? ? ? ? },new IntentFilter("Action.xxxxx"));
發(fā)送廣播:
? ? ? ? context.sendBroadcast(intent);
靜態(tài)注冊廣播:
在AndroidManifest里面注冊廣播肴敛。
本地廣播和全局廣播的區(qū)別
本地廣播通過LocalBroadcastManager來實現(xiàn)廣播的注冊和發(fā)送恼五,只在本應用范圍內傳播,不必擔心隱私數(shù)據(jù)的泄漏望拖。
? ? ? ? // 注冊 接收
? ? ? ? LocalBroadcastManager.getInstance(context).registerReceiver(new BroadcastReceiver() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onReceive(Context context, Intent intent) {
? ? ? ? ? ? }
? ? ? ? }, new IntentFilter("Action.xxx"));
? ? ? ? // 發(fā)送
? ? ? ? LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
全局廣播會在整個系統(tǒng)內發(fā)布凿渊。
Service服務生命周期
Service完整的生命周期從調用onCreate()開始直到調用onDestroy()結束梁只。
Service有兩種使用方法:
(1)以調用Context.startService()啟動,而以調用Context.stopService()結束埃脏。
(2)以調用Context.bindService()方法建立搪锣,以調用Context.unbindService()關閉
startService和bindService的區(qū)別
生命周期的區(qū)別:
生命周期:
onCreate → startCommand → onDestroy
onCreate → onBind→onUnBind→ onDestroy
靜態(tài)綁定對應著startService;動態(tài)綁定對應著bindService彩掐,靜態(tài)有自己獨立的生命周期构舟,動態(tài)會依附activity等組件的生命周期。
1.如果先startService,再bindService:
在bind的Activity退出的時候,Service會執(zhí)行unBind方法而不執(zhí)行其onDestory方法,因為有startService方法調用過,
所以Activity與Service解除綁定后會有一個與調用者沒有關連的Service存在堵幽。
2.如果先bindService,再startService,再調用Context.stopService
Service的onDestory方法不會立刻執(zhí)行,因為有一個與Service綁定的Activity,但是在Activity退出的時候,會執(zhí)行其(Service的)onDestory方法,
如果要立刻執(zhí)行stopService,就得先解除綁定狗超。
當一個服務沒被onDestory()銷毀之前,只有第一個啟動它的客戶端能調用它的onBind()和onUnbind()朴下。
結論:無論是先start還是先bind努咐,再結束時執(zhí)行onUnbind和stopService;只要還有一種啟動方法存在殴胧,Service就會繼續(xù)存活渗稍。
Activity的四種啟動模式: ?
standard ?
? ? ? ? 模式啟動模式,每次激活Activity時都會創(chuàng)建Activity团滥,并放入任務棧中竿屹。 ?
singleTop ?
? ? ? ? 如果在任務的棧頂正好存在該Activity的實例, 就重用該實例灸姊,否者就會創(chuàng)建新的實例并放入棧頂(即使棧中已經(jīng)存在該Activity實例拱燃,只要不在棧頂,都會創(chuàng)建實例)厨钻。 ?
singleTask ?
? ? ? ? 如果在棧中已經(jīng)有該Activity的實例扼雏,就重用該實例(會調用實例的onNewIntent())坚嗜。重用時,會讓該實例回到棧頂诗充,因此在它上面的實例將會被移除棧苍蔬。如果棧中不存在該實例,將會創(chuàng)建新的實例放入棧中蝴蜓。 ??
singleInstance ?
? ? ? ?在一個新棧中創(chuàng)建該Activity實例碟绑,并讓多個應用共享改棧中的該Activity實例。一旦改模式的Activity的實例存在于某個棧中茎匠,任何應用再激活該Activity時都會重用該棧中的實例格仲,其效果相當于多個應用程序共享一個應用,不管誰激活該Activity都會進入同一個應用中诵冒。 ?
OkHttp最核心的工作是在 getResponseWithInterceptorChain() 中進行凯肋,在進入這個方法分析之前,我們先來了 解什么是責任鏈模式汽馋,因為此方法就是利用的責任鏈模式完成一步步的請求侮东。責任鏈顧名思義就是由一系列的負責者構成的一個鏈條,類似于工廠流水線豹芯。
責任鏈設計模式
它為請求創(chuàng)建了一個接收者對象的鏈悄雅。為了避免請求發(fā)送者與多個請求處理者耦合在一起,于是將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈铁蹈,當有請求發(fā)生時宽闲,可將請求沿著這條鏈傳遞,直到有對象處理它為止握牧。(責任鏈模式也叫職責鏈模式)
在責任鏈模式中容诬,每一個對象對其下家的引用而接起來形成一條鏈。請求在這個鏈上傳遞沿腰,直到鏈上的某一個對象 決定處理此請求放案。客戶并不知道鏈上的哪一個對象最終處理這個請求矫俺,系統(tǒng)可以在不影響客戶端的 情況下動態(tài)的重 新組織鏈和分配責任。處理者有兩個選擇:承擔責任或者把責任推給下家掸冤。一個請求可以最終不被任何接收端對象 所接受厘托。
默認的5大攔截器有哪些?
RetryAndFollowUpInterceptor(重試和重定向攔截器)
第一個接觸到請求稿湿,最后接觸到響應;負責判斷是否需要重新發(fā)起整個請求铅匹。主要就是完成兩件事情:重試與重定向。
請求階段發(fā)生了 RouteException 或者 IOException會根據(jù)進行recover 判斷是否重新發(fā)起請求饺藤。
BridgeInterceptor(橋接攔截器)
補全請求包斑,并對響應進行額外處理流礁。比如設置請求內容長度,編碼罗丰,gzip壓縮神帅,cookie等補全請求頭,獲取響應后保存Cookie等操作
CacheInterceptor(緩存攔截器)
請求前查詢緩存萌抵,獲得響應并判斷是否需要緩存找御。
在發(fā)出請求前,判斷是否命中緩存绍填。如果命中則可以不請求霎桅,直接使用緩存的響應。 (只會存 在Get請求的緩存)
ConnectInterceptor(鏈接攔截器)
與服務器完成TCP連接(Socket)
這個攔截器中的所有實現(xiàn)都是為了獲得一份與目標服務器的連接讨永,在這個連接上進行HTTP數(shù)據(jù)的收發(fā)滔驶。
CallServerInterceptor(請求服務攔截器)
與服務器通信;封裝請求數(shù)據(jù)與解析響應數(shù)據(jù)(如:HTTP報文)
這個請求頭代表了在發(fā)送請求體之前需要和服務器確定是 否愿意接受客戶端發(fā)送的請求體。
OkHttp和HttpClient
OkHttp和HttpClient都是Java中常用的HTTP客戶端庫卿闹,它們都有各自的優(yōu)缺點揭糕。
?OkHttp的優(yōu)點:
?1. 性能高:OkHttp使用了連接池技術,可以復用TCP連接比原,減少了連接建立的時間插佛,提高了性能。
?2. 簡單易用:OkHttp的API設計簡單易用量窘,支持同步和異步請求雇寇,支持鏈式調用,使用起來非常方便蚌铜。?
?3. 支持SPDY和HTTP/2:OkHttp支持SPDY和HTTP/2協(xié)議锨侯,可以提高網(wǎng)絡傳輸效率。
?4. 支持攔截器:OkHttp支持攔截器冬殃,可以在請求和響應的過程中進行一些自定義的操作囚痴,比如添加請求頭、記錄日志等审葬。
?5. 支持緩存:OkHttp支持緩存深滚,可以減少網(wǎng)絡請求的次數(shù),提高性能涣觉。
?HttpClient的優(yōu)點:
1. 穩(wěn)定可靠:HttpClient是Apache開源組織的產(chǎn)品痴荐,經(jīng)過了長時間的使用和測試,穩(wěn)定性和可靠性都比較高官册。?
2. 功能豐富:HttpClient提供了很多高級功能生兆,比如連接池、Cookie管理膝宁、代理設置鸦难、SSL/TLS支持等根吁。?
3. 支持HTTP/1.1:HttpClient支持HTTP/1.1協(xié)議,可以在網(wǎng)絡傳輸效率和兼容性之間做出權衡合蔽。
4. 支持多種認證方式:HttpClient支持多種認證方式击敌,比如基本認證、摘要認證辈末、NTLM認證等愚争。
5. 可擴展性強:HttpClient提供了很多擴展點,可以方便地進行二次開發(fā)和定制挤聘。 總的來說轰枝,OkHttp更適合移動端開發(fā),因為它輕量級组去、性能高鞍陨、簡單易用;HttpClient更適合服務器端開發(fā)从隆,因為它功能豐富诚撵、穩(wěn)定可靠、可擴展性強键闺。
當前主要使用的是 retrofit的框架 + okhttp?
retrofit的好處就是降低的網(wǎng)絡接口請求的代碼
Retrofit 使用起來非常方便寿烟,共包含以上四個步驟
書寫網(wǎng)絡請求的接口類
構建 Retrofit 對象
使用retrofit 對象 獲得實現(xiàn)接口的實體類對象
調用接口方法,實現(xiàn)網(wǎng)絡請求
android存儲的5種方式
SharedPreferences存儲數(shù)據(jù)(如app的配置信息)
?SharedPreferences是一個輕量級的存儲類辛燥,基于XML文件存儲的key-value鍵值對數(shù)據(jù)筛武,通常用來存儲一些簡單的配置信息。
文件存儲數(shù)據(jù)(I/O流? 如保存網(wǎng)絡圖片)
ContentProvider
ContentProvider不僅是Android四大組件之一挎塌,還是Android五大存儲方式之一徘六,當應用繼承ContentProvider類,并重寫該類用于提供數(shù)據(jù)和存儲數(shù)據(jù)的方法榴都,就可以向其他應用共享其數(shù)據(jù)待锈。雖然使用其他方法也可以對外共享數(shù)據(jù),但數(shù)據(jù)訪問方式會因數(shù)據(jù)存儲的方式而不同嘴高。
使用ContentProvider共享數(shù)據(jù)的好處是統(tǒng)一了數(shù)據(jù)訪問方式竿音。 Android內置的許多數(shù)據(jù)都是使用ContentProvider形式,供開發(fā)者調用的(如視頻拴驮,音頻谍失,圖片,通訊錄等)莹汤。
網(wǎng)絡存儲數(shù)據(jù)
網(wǎng)絡存儲方式,需要與Android 網(wǎng)絡數(shù)據(jù)包打交道
SQLite數(shù)據(jù)庫存儲數(shù)據(jù)(如保存網(wǎng)絡數(shù)據(jù))
如果事務操作成功颠印,則該事務進行的所有數(shù)據(jù)均會提交纲岭,更改成為數(shù)據(jù)庫中的永久組成部分抹竹;如果事務操作失敗,則所有操作均取消止潮,所有數(shù)據(jù)的更改均清除
特性:
原子性:事務是數(shù)據(jù)庫的邏輯工作單元
一致性:事務執(zhí)行結果必須是使數(shù)據(jù)庫從一個一致性狀態(tài)變到另一個一致性狀態(tài)
隔離性:一個事務的執(zhí)行不能被其他事務干擾
持續(xù)性:一個事務一但提交窃判,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就應該是永久性的
事務的語句
開始事物:BEGIN TRANSACTION
提交事物:COMMIT TRANSACTION
回滾事務:ROLLBACK TRANSACTION
隔離級別
四個級別(低到高;性能由高到低):
Read uncommitted:未提交讀。事務A讀取數(shù)據(jù)時喇闸,其他事務可讀取該數(shù)據(jù)袄琳,導致事務A提交數(shù)據(jù)后,其他事務讀取的數(shù)據(jù)失效
Read committed:提交讀燃乍。事務A提交數(shù)據(jù)時唆樊,其他事務在其之前也提交數(shù)據(jù)如蚜。導致事務A提交數(shù)據(jù)時序六,讀取到的數(shù)據(jù)失效
Repeatable read:重復讀。事務A讀取數(shù)據(jù)后藕各,其他事務不能讀取該數(shù)據(jù)
Serializable:序列化
臟讀:
指的是讀到了其他事務未提交的數(shù)據(jù)舆瘪,未提交意味著這些數(shù)據(jù)可能會回滾片效,也就是可能最終不會存到數(shù)據(jù)庫中,也就是不存在的數(shù)據(jù)英古。讀到了并一定最終存在的數(shù)據(jù)淀衣,這就是臟讀。
不可重復讀:
指的是在一個事務內召调,最開始讀到的數(shù)據(jù)和事務結束前的任意時刻讀到的同一批數(shù)據(jù)出現(xiàn)不一致的情況膨桥。
幻讀:
并不是說兩次讀取獲取的結果集不同,幻讀側重的方面是某一次的 select 操作得到的結果所表征的數(shù)據(jù)狀態(tài)無法支撐后續(xù)的業(yè)務操作某残。
更為具體一些:select 某記錄是否存在国撵,不存在,準備插入此記錄玻墅,但執(zhí)行 insert 時發(fā)現(xiàn)此記錄已存在介牙,無法插入,此時就發(fā)生了幻讀澳厢。
性能分析
抓trace的方法
在需要測試的函數(shù)前添加
Debug.startMethodTracing("/sdcard/xxx.trace");
然后在測試結束后添加
Debug.stopMethodTracing();
找到sd卡中的xxx.trace文件环础,導入AS,分析子函數(shù)耗時剩拢。
性能分析的指標:
CPU使用率:測量應用程序占用CPU的百分比线得。
GPU使用率:測量應用程序占用GPU的百分比,GPU主要決定應用的圖形繪制。
內存使用率:測量應用程序占用內存的百分比徐伐。
硬盤讀寫速度:測量應用程序從存儲器讀取和寫入數(shù)據(jù)的速度贯钩。
UI響應時間:測量應用程序響應用戶交互的時間。
APP響應時間:作為應用成功的核心特征之一,移動應用響應時間至關重要角雷。用戶對較長的加載時間和緩慢的處理零容忍祸穷。
流暢度:FPS,可通過工具或者安卓開發(fā)者模式中的設置查看到繪制耗時的柱狀圖
工具:
Android Studio自帶的Profiler:可用于跟蹤應用程序的CPU和內存使用率勺三,并分析應用程序的性能瓶頸雷滚。
Monkey:一種隨機事件測試工具,可模擬用戶的隨機操作吗坚,如點擊祈远、滑動和縮放,以測試應用程序的穩(wěn)定性和響應性能商源。
Selendroid:一種自動化測試工具车份,可自動執(zhí)行UI測試、功能測試和性能測試炊汹。
JMeter:一種功能強大的性能測試工具躬充,可用于測試Android應用程序的性能,包括CPU和內存使用率讨便、網(wǎng)絡延遲等充甚。
Experitest:一種專門針對移動應用程序的測試工具,可用于測試Android應用程序的性能霸褒、穩(wěn)定性伴找、安全性等。
ANR
什么是ANR废菱?
ANR技矮,全稱為Application Not Responding。在Android 中殊轴,如果你的應用程序有一段時間沒有響應衰倦,系統(tǒng)會向用戶顯示一個對話框,這個對話框稱作應用程序無響應對話框旁理。
造成ANR的原因有哪些
首先明確一點:只有當應用程序的UI線程響應超時才會引起ANR
當前的事件沒有機會得到處理
當前的事件正在處理樊零,但是由于耗時太長沒能及時完成
分類
KeyDispatchTimeout:
原因是View的按鍵事件或觸摸事件在5秒內無法得到響應。一般就是主線程卡住了孽文,導致無法響應事件驻襟。
Input事件處理慢觸發(fā)的ANR
BroadcastTimout
原因是廣播接收者(BrocastReceiver)的onReceive()函數(shù)在特定時間內(10秒)無法完成處理。
ServiceTimeout
原因是服務(Service)的各個聲明周期函數(shù)在特定時間(20秒)內無法完成處理芋哭。
ContentProvider Timeout:
provider在publish后需要在10秒內完成沉衣。
典型場景:
應用程序UI線程存在耗時操作,例如在UI線程中進行網(wǎng)絡請求减牺,數(shù)據(jù)庫操作或者文件操作等豌习,可能會導致UI線程無法及時處理用戶輸入等存谎。
應用程序UI線程等待子線程釋放某個鎖,從而無法處理用戶的請求的輸入斑鸦。
耗時操作的動畫需要大量的計算工作愕贡,可能導致CPU負載過重。
分析方法:
anr的trace data/anr 路徑下
看時間節(jié)點的業(yè)務日志巷屿,logcat
如何分析ANR
系統(tǒng)針對ANR,會生成一個trace.txt文件
log文件找到tid=1的線程墩虹,該線程為主線程嘱巾。
可以看到是什么原因導致的ANR,查看主線程的狀態(tài)诫钓,是主線程等待了旬昭、死鎖了還是其他什么原因。
然后查看main log菌湃,搜索ANR關鍵字问拘,查看當前的CPU狀態(tài)、IO wait惧所、message的延時時間骤坐、block、memory leak等下愈。
如果是死鎖了纽绍,可以通過trace文件看到對象被哪個線程占用著,如果內存泄漏了势似,需要dump內存文件進行分析拌夏。
我們也可以借助于第三方工具BlockCanary在測試的時候進行慢操作分析,該組件利用了Looper類的loop函數(shù)的特性履因,如下所示障簿,loop函數(shù)在調用dispatchMessage函數(shù)(會調用handleMessage)的時候會可以打印handleMessage的執(zhí)行日志,通過自定義Printer類栅迄,將printer類設置到當前l(fā)ooper對象站故,這樣在就可以打印每個message的執(zhí)行時間,進而找到慢操作霞篡。
如何避免ANR
數(shù)據(jù)庫操作或者文件讀寫等IO耗時操作使用異步世蔗;
UI線程只做UI的事;
廣播如果需要耗時操作可以在onReceiver啟動一個IntentService朗兵;
如果要使用Service污淋,盡量使用IntentService;
線程間交互使用Handler來做;
后臺線程使用Background優(yōu)先級余掖;
圖片質量勁量降低寸爆,如使用565圖片礁鲁;
界面層次布局降低,減少繪制時間赁豆;
數(shù)據(jù)庫打開并發(fā)讀寫模式仅醇;release環(huán)境關閉log。
IntentService
IntentService是一個基于Service的一個類魔种,用來處理異步的請求析二。
可以通過startService(Intent)來提交請求,該Service會在需要的時候創(chuàng)建节预,當完成所有的任務以后自己關閉叶摄,且請求是在工作線程處理的。
IntentService負責執(zhí)行后臺的耗時的任務安拟,也因為IntentService是服務的原因蛤吓,這導致它的優(yōu)先級比單純的線程要高。
優(yōu)點:
一方面不需要自己去new Thread了糠赦;另一方面不需要考慮在什么時候關閉該Service会傲。
IntentService會自己創(chuàng)建一個Handler線程,用于處理拙泽。
IntentService與Service的區(qū)別
IntentService可以在onHandlerIntent方法中執(zhí)行耗時操作
Service不可以執(zhí)行耗時操作?
Android內存泄漏
通俗的來說就是堆中的一些對象已經(jīng)不會再被使用了淌山,但垃圾收集器(GC)卻無法將它們從內存中清除。
阻塞內存資源并隨著時間的推移降低系統(tǒng)性能奔滑,最終的結果將會使應用程序耗盡內存資源艾岂,無法正常服務,導致程序崩潰朋其,拋出java.lang.OutOfMemoryError異常王浴。
資源性對象未關閉
資源性對象,比如Cursor梅猿,F(xiàn)ile等Closeable對象氓辣,這些對象需要使用緩存區(qū)。如果只是將對象賦值為null袱蚓,不去調用close钞啸,會導致緩存未關閉,而得不到釋放喇潘,造成泄漏体斩。使用try-with-resource。
注冊對象未注銷
如果注冊對象不再使用時颖低,未及時注銷絮吵,會導致訂閱者列表中維持這對象的引用,阻止垃圾回收忱屑,導致內存泄露蹬敲。常見場景:動態(tài)注冊BroadcastReceiver暇昂,注冊PhoneStateListener,注冊EventBus等等一般會在onDestory里面解注冊伴嗡。
?非靜態(tài)內部類的靜態(tài)實例
非靜態(tài)內部類持有外部類實例的引用急波,若非靜態(tài)內部類的實例是靜態(tài)的,便擁有app存活期整個生命周期瘪校,長期持有外部類的引用澄暮,阻止外部類實例被回收。
單例模式引起的內存泄露
由于單例模式的靜態(tài)特性阱扬,使得它的生命周期和我們的應用一樣長赏寇,如果讓單例無限制的持有Activity的強引用就會導致內存泄漏
解決方案:使用Activity的弱引用,或者沒特殊需求時使用Application Contex
Handler臨時性內存泄露
非靜態(tài)Handler持有Activity或Service的引用价认,Message中的target指向Handler實例,所以當Message在MessageQueue中排隊自娩,長時間未得到處理時用踩,Activity就不會被回收,導致臨時性內存泄露忙迁。
解決方案:
(1)使用靜態(tài)Handler內部類脐彩,然后對Handler持有的對象(Activity或Service)使用弱引用
(2)在onDestroy()中移除消息隊列中的消息 mHandler.removeCallbacksAndMessages(null)
類似的:AsyncTask內部也是Handler機制日矫,也存在同樣的臨時性內存泄露風險
容器中對象未及時清理導致內存泄露
容器類一般擁有較長的生命周期沪铭,若內部不再使用的對象不及時清理篓跛,內部對象邊一直被容器類引用隙姿。上述2中的訂閱者列表也屬于容器類這中情況奶赠。另外常見的容器類還有線程池捎迫、對象池武鲁、圖片緩存池等灭翔。線程池中的線程若存在ThreadLocal對象嵌言,因為線程對象一直被循環(huán)使用嗅回,ThreadLocal對象便會一直被引用,要注意對value對象的置空釋放摧茴。
靜態(tài)View導致內存泄露
有時绵载,當一個Activity經(jīng)常啟動,但是對應的View讀取非常耗時苛白,我們可以通過靜態(tài)View變量來保持對該Activity的rootView引用娃豹。這樣就可以不用每次啟動Activity都去讀取并渲染View了。這確實是一個提高Activity啟動速度的好方法购裙!但是要注意懂版,一旦View attach到我們的Window上,就會持有一個Context(即Activity)的引用缓窜。而我們的View有事一個靜態(tài)變量定续,所以導致Activity不被回收谍咆。 解決辦法:在使用靜態(tài)View時,需要確保在資源回收時私股,將靜態(tài)View detach掉摹察。
屬性動畫未及時關閉導致內存泄露
在使用ValueAnimator或者ObjectAnimator時,如果沒有及時做cancel取消動畫倡鲸,就可能造成內存泄露供嚎。 因為在cancel方法里,最后調用了endAnimation(); 峭状,在endAnimation里克滴,有個AnimationHandler的單例,會持有屬性動畫對象的引用
解決辦法:在在onDestory時优床,調用動畫的cancel方法
WebView內存泄露
目前Android中WebView的實現(xiàn)存在很大的兼容性問題劝赔,Google支持各個ROM廠商自行定制自己的WebView實現(xiàn),各個ROM間差異較大胆敞,且大多都存在內存泄露問題着帽。除了調用其內部的clearCache()、clearHistory()移层、removeAllViews()仍翰、freeMemory()、destroy()和置null以外观话,一般比較粗暴有效的解決方法是:將包含WebView的Activity放在一個單獨的進程中予借,不需要時將進程銷毀,從而釋放所有所占內存频蛔。
其他的系統(tǒng)控件以及自定義View
在 Android Lollipop 之前使用 AlertDialog 可能會導致內存泄漏
view中有線程或者動畫 要及時停止灵迫。這是為了防止內存泄漏,可以在onDetachedFromWindow方法中結束帽驯,這個方法回調的時機是 當View的Activity退出或者當前View被移除的時候 會調用 這時候是結束動畫或者線程的好時機 另外還有一個對應的方法 onAttachedToWindow 這個方法調用的時機是在包含View的Activity啟動時 回調 回調在onDraw方法之前
其他常見的引起內存泄漏原因
(1)構造Adapter時龟再,沒有使用緩存的 contentView
(2)Bitmap在不使用的時候沒有使用recycle()釋放內存
(3)警惕線程未終止造成的內存泄露;譬如在Activity中關聯(lián)了一個生命周期超過Activity的Thread尼变,在退出Activity時切記結束線程利凑。一個典型的例子就是HandlerThread的run方法是一個死循環(huán),它不會自己結束嫌术,線程的生命周期超過了Activity生命周期哀澈,我們必須手動在Activity的銷毀方法中調用thread.getLooper().quit();才不會泄露
(4)避免代碼設計模式的錯誤造成內存泄露;譬如循環(huán)引用度气,A持有B割按,B持有C,C持有A磷籍,這樣的設計誰都得不到釋放
1适荣、什么是 Activity现柠?從視圖角度分析
Activity 并不負責視圖控制,它只是控制生命周期和處理事件弛矛。真正控制視圖的是Window够吩。一個Activity 包含了一個Window,Window才是真正代表一個窗口丈氓。
Activity就像一個控制器周循,統(tǒng)籌視圖的添加與顯示,以及通過其他回調方法万俗,來與Window湾笛、以及View 進行交互。
2闰歪、什么是Window嚎研?
Window 是一個抽象類,實際在Activity中持有的是其子類PhoneWindow库倘。PhoneWindow中有個內部類DecorView嘉赎,通過創(chuàng)建DecorView 來加載Activity中設置的布局R.layout.activity_main
Window 是視圖的承載器,內部持有一個DecorView于樟,而這個DecorView才是view 的根布局。
Window 通過WindowManager 將DecorView 加載其中拇囊,并將DecorView 交給ViewRootimpl迂曲,進行視圖繪制以及其他交互
3、什么是DecorView?
DecorView 是 FrameLayout 的子類寥袭,它可以被認為是 Android 視圖樹的根節(jié)點視圖路捧。
DecorView 作為頂級 View,一般情況下它內部包含一個豎直方向的 LinearLayout传黄,在這個 LinearLayout 里面有上下三個部分杰扫,上面是個 ViewStub,延遲加載的視圖(應該是設置ActionBar膘掰,根據(jù) Theme 設置)章姓,中間的是標題欄(根據(jù)Theme設置,有的布局沒有)识埋,下面的是內容欄凡伊。具體情況和Android版本及主體有關。
在 Activity 中通過 setContentView 所設置的布局文件其實就是被加到內容欄之中的窒舟,成為其唯一子 View系忙,就是上面的 id 為 content 的 FrameLayout 中,在代碼中可以通過 content 來得到對應加載的布局惠豺。
4银还、什么是 ViewRootImpl
從結構上來看风宁,ViewRootImpl 和 ViewGroup 其實是一種東西。
相同點
它們都繼承了ViewParent蛹疯。
ViewParent 是一個接口戒财,定義了一些父View 的基本行為,比如requestlayout苍苞,getparent 等固翰。
不同點
ViewRootImpl 并不會像ViewGroup 一樣被真正繪制在屏幕上
在Activity 中,它是專門用來繪制DecorView 的羹呵,核心方法是setView
5骂际、Activity,window冈欢,View 三者之間的關系是什么歉铝?
window 是 activity 的一個成員變量,window 和 View 是“顯示器”和“顯示內容”的關系
Window 抽象類凑耻,PhoneWindow 唯一實現(xiàn)類太示,用于加載Activity 的頂級View –DecorView
一個 Activity 對應一個 Window 也就是 PhoneWindow,一個 PhoneWindow 持有一個 DecorView 的實例香浩,DecorView 本身是一個 FrameLayout
?View 繪制之前类缤,系統(tǒng)會先對 View 進行測量,以確定 View 的大小和位置邻吭。
簡單的可以說餐弱,如 measure,layout囱晴,draw 分別對應測量膏蚓,布局,繪制三個過程畸写。
在整個 Activity 的生命周期中驮瞧,setContentView 是在 onCreate 中調用的,它實現(xiàn)了對資源文件的解析枯芬,完成了 xml 文件到 View 的轉化论笔。
而真正將view繪制出來是在onResume之后進行的。
在onResume之后千所,從?Activity 中的 Window 實例中獲取Decorview
調用activity 中windowmanager 的addView 方法翅楼,將decorView 傳入到ViewRootImpl 的setView 方法中
通過ViewRootImpl.setView() 來完成View 的繪制。
setView
首先會檢查當前線程是否是view的創(chuàng)建線程真慢,【view是否一定要在主線程創(chuàng)建和更新毅臊?】
通過內存屏幕保證view的任務是最優(yōu)先的
調用?performTraversals 完成measure,layout,draw的繪制
view是否一定要在主線程創(chuàng)建和更新
并不一定的管嬉,由于view的更新都會調用到ViewRootImpl的setview皂林,然后會調用android.view.ViewRootImpl.checkThread,當我們不在主線程更新view的時候會拋出異常蚯撩,異常的堆棧指向的也是android.view.ViewRootImpl.checkThread拋出CalledFromWrongThreadException础倍,這個方法檢查的就是當前線程是否是ViewRootImpl的mThread,而mThread是在ViewRootImpl初始化時賦值的胎挎。
而ViewRootImpl的初始化是在Activity創(chuàng)建時賦值的沟启。
而當我們強制在子線程給WindowManagerImpl中addview,那么添加的view的ViewRootImpl就會在當前線程被創(chuàng)建犹菇。但是其他線程和主線程都沒法更新這次添加的button德迹。
new Thread(() -> {
? ? Looper.prepare();
? ? WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
? ? layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
? ? layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
? ? getWindowManager().addView(button,layoutParams);
? ? Looper.loop();
}).start();
總結:view的更新可在子線程進行,但是需要保證和view被添加時的線程一致揭芍。
onMeasure
測量了View的大小胳搞,系統(tǒng)會先根據(jù)xml布局文件和代碼中對控件屬性的設置,來獲取或者計算出每個View和ViewGrop的尺寸称杨,并將這些尺寸保存下來肌毅。
在測量大小時,MeasureSpec概括了從父布局傳遞給子view布局要求姑原。每一個MeasureSpec代表了寬度或者高度要求悬而,它由size(尺寸)和mode(模式)組成。
可能有三種模式锭汛,
UNSPECIFIED:未定義模式摊滔,父布局不對子布局強加限制,一般用于系統(tǒng)中店乐,很少用到。 0 << 30
EXACTLY:精準模式呻袭,給定確定值眨八,如layout_width 知道多少dp硕旗,或者 match parent彭沼。match parent也是給定了確定值,跟隨父view的大小啤握。 1 << 30
AT_MOST:限制最大值模式篓足,子view可以到的最大寬度段誊,如wrap_content就是這種。2 << 30
onLayout
布局栈拖。根據(jù)測量出的結果以及對應的參數(shù)连舍,來確定每一個控件應該顯示的位置。
onDraw
繪制涩哟。確定好位置后索赏,就將這些控件繪制到屏幕上盼玄。
自定義視圖
需要在進行了可能會改變外觀的操作之后調用invalidate()函數(shù)和requestLayout()函數(shù),來使得系統(tǒng)重新繪制view潜腻,確定大小和形狀埃儿。這也確保了視圖行為的可靠性和可用性。
重載onDraw()方法
自定義視圖最重要的部分便是它的外觀融涣。我們通過重載onDraw()方法來實現(xiàn)自定義的外觀童番。傳遞給onDraw()方法的參數(shù)是Canvas對象,Canvas類定義了繪制文字威鹿、線剃斧、位圖以及其他基本圖形的方法。
在調用所有的繪制方法之前专普,我們需要創(chuàng)建一個Paint對象悯衬。
Canvas好比是一個畫布(實際是一個bitmap, 我們自定義的視圖都是在這個Bitmap上進行繪制的)
而Paint是一個畫筆(確定了顏色和樣式等信息)。Canvas提供了繪制一條線的方法檀夹,而Piant提供了定義這條線顏色的方法筋粗。Canvas提供了繪制一個矩形的方法,而Paint定義了這個矩形是否需要填充炸渡∧纫冢總結起來,Canvas定義了繪制在屏幕上的形狀蚌堵,而Paint定義了顏色买决、字體、樣式等吼畏。
自定義View的種類各不相同督赤,須要根據(jù)實際須要選擇一種簡單低成本的方式來實現(xiàn),盡可能的減少UI的層級泻蚊,view的種類如下躲舌,開發(fā)中需要盡可能的選擇適合自己的。
繼承View重寫onDraw方法
主要用于實現(xiàn)不規(guī)則的效果性雄,也就是說這種效果不適宜采用布局的組合方式來實現(xiàn)没卸。也就是需要使用canvas,Paint秒旋,運用算法去“繪制”了约计。采用這種方式須要本身支持wrap_content,padding也須要本身處理canvas迁筛。
繼承ViewGroup派生特殊的Layout
主要用于實現(xiàn)自定義的布局煤蚌,看起來很像幾種View組合在一塊兒的時候,可使用這種方式。這種方式須要合適地處理ViewGroup的測量和布局铺然,尤其在測量的時候需要注意padding 和margin俗孝,比如:自定義一個自動換行的LinerLayout等。
繼承特定的View魄健,好比TextView
這種方法主要是用于擴展某種已有的View赋铝,增長一些特定的功能。這種方法比較簡單沽瘦,也不須要本身支持wrap_content和padding革骨。這種效果就相當于我們使用imageView來實現(xiàn)一個圓形的imageView。
繼承特定的ViewGroup析恋,好比LinearLayout
這種方式也比較常見良哲,也就是基于原來已經(jīng)存在的layout,在其上去添加新的功能助隧。和上面的第2種方法比較相似筑凫,第2種方法更佳接近View的底層。
View需要支持padding
直接繼承View的控件需要在onDraw方法中處理padding并村,否則用戶設置padding屬性就不會起作用巍实。
直接繼承ViewGroup的控件需要在onMeasure和onLayout中考慮padding和子元素的margin對其造成的影響,不然將導致padding和子元素的margin失效哩牍。
invaliate 和 requestlayout 方法的區(qū)別
ViewRootImpl 作為頂級 View 負責 View 的繪制棚潦。
所以簡單來說,requestlayout 和 invaliate 最終都會向上回溯調用到 ViewRootImpl 的 postTranversals 方法來繪制 View膝昆。
不同的是 requestlayout 會繪制 View 的 measure丸边,layout 和 draw 過程。
invaliate 因為只添加了繪制 draw 的標志位荚孵,只會繪制 draw 過程
Android 動畫詳解:屬性動畫妹窖、View 動畫和幀動畫
基本的動畫共有三種類型:
View 動畫:
也叫視圖動畫或者補間動畫,主要是指android.view.animation包下面的一些類收叶,只能被用來設置給 View骄呼,缺點是比如當控件移動之后,接收點擊的控件的位置不會跟隨移動滔驾,并且能夠實現(xiàn)的效果只有移動、縮放俄讹、旋轉和淡入淡出操作四種及其組合哆致。
Drawable 動畫:
也叫 Frame 動畫或者幀動畫,其實可以劃分到視圖動畫的類別患膛,實現(xiàn)方式是將一系列的 Drawable 像幻燈片一樣一個一個地顯示摊阀。
Property 動畫:
屬性動畫主要是指 android.animation包下面的一些類,只對 API 11 以上版本的Android 系統(tǒng)才有效,但我們可以通過兼容庫做低版本兼容胞此。這種動畫可以設置給任何 Object臣咖,包括那些還沒有渲染到屏幕上的對象。這種動畫是可擴展的漱牵,可以讓你自定義任何類型和屬性的動畫夺蛇。
Handler
3首先答案是可以,但是不能直接創(chuàng)建酣胀。handler的創(chuàng)建依賴looper刁赦,否則會拋出異常Can't create handler inside thread that has not called Looper.prepare()。
因此可以主動調用Looper.prepare(); 再new Handler闻镶。
new Thread(new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? Looper.prepare();
? ? ? ? ? ? ? ? new Handler() {
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? ? ? ? ? ? ? super.handleMessage(msg);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? Looper.loop();
? ? ? ? ? ? }
? ? ? ? })
或者甚脉,我們獲取主線程的looper。Handler handler = new Handler(Looper.getMainLooper()){...}這個Handler雖然是在子線程的代碼中創(chuàng)建的铆农,但是可以用來更新UI牺氨,因為looper使用的是主線的,事件會被放到主線程中去處理墩剖。
在主線程中我們沒有主動調用Looper.prepare()
這是因為系統(tǒng)替我們做過了猴凹,在ActivityThread中,主動調用了Looper.prepareMainLooper()涛碑。
即使是在同一個線程中創(chuàng)建的不同handler精堕,他們之間也不會互相通知消息。
handler:A在主線程創(chuàng)建蒲障,handler:B在子線程創(chuàng)建的主線程Handler歹篓。A發(fā)送的msg并不會派發(fā)給B。android.os.Message#target
準備:
Looper.prepare()是給這個Thread創(chuàng)建Looper對象揉阎,一個Thead只有一個Looper對象庄撮。
Looper對象需要一個MessageQueue對象一個Looper實例也只有一個MessageQueue。
調用Looper.loop();??不斷遍歷MessageQueue中是否有消息毙籽。
發(fā)送:
Handler 作用是發(fā)消息到MessageQueue洞斯,從而回調Handler 的HandleMessage的回掉方法。
MessageQueue插入新的Message并喚醒阻塞坑赡。由于MessageQueue沒有Message時烙如,會進入阻塞狀態(tài)因此當新消息來時,會激活毅否。
處理:
重新檢查MessageQueue并獲取新插入的消息Message
Looper獲取Message后亚铁,通過Message的target即Handler調用dispatchMessage(Message msg)方法分發(fā)提取到的Message,然后回收Message并循環(huán)獲取下一個Message
Handler使用handlerMessage(Message msg)方法處理Messag
等待
MessageQueue沒有Message時螟加,重新進入阻塞狀態(tài)
主線程的死循環(huán)是否一致耗費CPU資源徘溢?
Handler底層采用Linux的pipe/epoll機制吞琐,MessageQueue沒有消息的時候,便阻塞在Looper.mQueue.next方法中然爆,此時主線程會釋放CPU資源進入休眠站粟,直到下個事件到達,當有新消息的時候曾雕,通過往pipe管道寫數(shù)據(jù)來喚醒主線程工作奴烙。所以主線程大多數(shù)時候處于休眠狀態(tài),不會阻塞翻默。
冷啟動缸沃、熱啟動、溫啟動
冷啟動:系統(tǒng)不存在App進程(如APP首次啟動或APP被完全殺死)時啟動App稱為冷啟動修械。
熱啟動:按了Home鍵或其它情況app被切換到后臺趾牧,再次啟動App的過程。
溫啟動:溫啟動包含了冷啟動的一些操作肯污,不過App進程依然存在翘单,這代表著它比熱啟動有更多的開銷。
MVC,MVP,MVVM
模塊和模塊之間的數(shù)據(jù)通信方式構成不同的架構蹦渣。在這3種架構中哄芜,都是把系統(tǒng)整體劃分成了3個模塊:視圖層,數(shù)據(jù)層柬唯,業(yè)務層认臊。?他們之間的區(qū)別在于,模塊之間的通信方式(數(shù)據(jù)流動方向)不一致锄奢。
插件classLoader
1失晴、DexClassLoader可以加載jar/apk/dex,可以從SD卡中加載未安裝的apk
2拘央、PathClassLoader只能加載系統(tǒng)中已經(jīng)安裝過的apk
插件化:需要在宿主manifest里面占位涂屁,然后分發(fā)。通過這種方式可以跳轉到對應的插件的activity
classloader類型
Bootstrap ClassLoader(啟動類加載器):該類加載器由C++實現(xiàn)的灰伟。負責加載Java基礎類拆又,對應加載的文件是%JRE_HOME/lib/ 目錄下的rt.jar、resources.jar栏账、charsets.jar和class等帖族。
Extension ClassLoader(標準擴展類加載器):繼承URLClassLoader。對應加載的文件是%JRE_HOME/lib/ext 目錄下的jar和class等挡爵。
App ClassLoader(系統(tǒng)類加載器):繼承URLClassLoader竖般。對應加載的應用程序classpath目錄下的所有jar和class等。
還有一種就是我們自定義的 ClassLoader了讨,由Java實現(xiàn)捻激。我們可以自定義類加載器,并可以指定這個類加載器 要 加載哪個路徑下的class文件
PathCLassLoader?
BaseDexClassLoader的子類前计,是整個程序中的類加載器胞谭,相當于 java 中的 AppClassLoader ,
也就是說我們整個項目的除了系統(tǒng)的類是用BootClassLoader加載的 其他都是用 PathCLassLoader加載的 男杈,也就是說丈屹,根據(jù) 雙親委派 。 PathCLassLoader去加載類的時候 先判斷 BootClassLoader 是否加載過伶棒,沒有那就是 自己去加載了 也就是 PathCLassLoader自己去加載旺垒。
DexClassLoader?BaseDexClassLoader的子類, 相當于 java 的CustomClassLoader。
Java相關
Synchronized
Java用Synchronized來實現(xiàn)線程同步,該關鍵字可以加在方法上仓蛆,也可以加在對象上狠轻,
如果他的作用的對象是非靜態(tài)的,則它取得的鎖是對象侮繁;
如果作用的對象是靜態(tài)方法或者類,則它取得的鎖是類對象(Class對象)。
每個對象有一把鎖业岁,誰取得這個鎖,誰就可以訪問對象的方法寇蚊。
可重入性:
假設類A有兩個方法a,b都用Synchronized修飾笔时,當調用a()時需要請求類A的鎖,調用B的時候仗岸,
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? final Test test = new Test();
? ? ? ? Thread thread1 = new Thread(() -> {
? ? ? ? ? ? test.a("thread-1");
? ? ? ? ? ? test.b("thread-1");
? ? ? ? });
? ? ? ? Thread thread2 = new Thread(() -> {
//? ? ? ? ? ? synchronized (test.count) {
? ? ? ? ? ? ? ? test.b("thread-2");
? ? ? ? ? ? ? ? test.a("thread-2");
//? ? ? ? ? ? }
? ? ? ? });
? ? ? ? thread1.start();
? ? ? ? thread2.start();
? ? ? ? thread1.join(10000L);
? ? }
? ? public synchronized void a(String name) {
? ? ? ? System.out.println(name + ":a");
? ? ? ? try {
? ? ? ? ? ? Thread.sleep(1000L);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
? ? public synchronized void b(String name) {
? ? ? ? synchronized (count) {
? ? ? ? ? ? System.out.println(name + ":b");
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? Thread.sleep(1000L);
? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
結果:
thread-1:a
thread-1:b
thread-2:b
thread-2:a
在線程1執(zhí)行時允耿,線程1持有類鎖,線程2無法進入方法爹梁,等待右犹;當a執(zhí)行完,執(zhí)行b方法姚垃,鎖還依然持有念链,線程2繼續(xù)等待。
不可繼承性:
子類聲明集成自父親的同步方法可以不必聲明成同步的积糯。
對象鎖和類鎖:
Java的所有對象都含有一個鎖掂墓,這個鎖由JVM自動獲取和釋放,線程進入Synchronized代碼塊會去獲取該對象的鎖看成,如果已經(jīng)有線程獲取了對象鎖君编,那個當前線程就會等待。
對象鎖是控制實例方法的鎖
類鎖是用來控制靜態(tài)代碼的同步的川慌,相當于Class對象的鎖吃嘿。
類鎖和對象鎖不同祠乃,一個是類的Class對象的鎖,一個是類的實例對象的鎖兑燥,也就是說一個線程訪問靜態(tài)的Synchronized時亮瓷,另外的線程可以訪問對象實例的Synchronized方法。
Java引用類型
強引用----------- 垃圾收集器不會收集它降瞳,寧可拋出OOM
軟引用(Soft Reference)-----------當垃圾收集器工作時嘱支,如果內存足夠,就不回收只被軟引用關聯(lián)的對象挣饥,如果內存不夠除师,則會進行回收。一旦被回收扔枫,這個軟引用就會被加入到關聯(lián)的ReferenceQueue中汛聚。
弱引用(Weak Reference) -------------- 對象只能生存到下一次GC,當垃圾收集器工作時無論內存是否足夠都會收集只被弱引用關聯(lián)的對象短荐。一旦一個弱引用被回收贞岭,便會加入一個注冊引用隊列ReferenceQueue中。
虛引用(Phantom Reference) ------------ 最弱的引用類型搓侄,get方法返回null瞄桨。主要用來跟蹤對象被垃圾回收的狀態(tài)。虛引用被回收時會放入ReferenceQueue讶踪,從而達到跟蹤對象垃圾回收的作用芯侥。
ReferenceQueue-----------是這樣的一個對象,當一個obj被gc掉之后乳讥,其相應的包裝類柱查,即ref對象會被放入queue中。我們可以從queue中獲取到相應的對象信息云石,同時進行額外的處理唉工。比如反向操作,數(shù)據(jù)清理等汹忠。
WeakReference weakReference=new WeakReference(obj,ReferenceQueue)將引用隊列傳入即可進行觀察淋硝。
結論上來說,一般一個對象 equals相等宽菜,則 hashcode 一定也是相同的谣膳。但是,兩個對象有相同的 hashcode 值铅乡,它們也不一定是相等的 继谚。
但是hashCode是對象的散列值,默認的hashcode方法是對堆上的對象產(chǎn)生獨特值阵幸。如果沒有重寫 hashcode()花履,則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數(shù)據(jù))芽世。
相同hashcode但是對象不一定相等,是因為hashcode是通過hash方法對對象進行散列诡壁,縮短了查找的成本捂襟。一個實際例子,如果對象不相等欢峰,hashcode就不同,那么在hashmap在涨共,每個空間都只會存在一個值纽帖,這顯然不實際。
equals方法的定義在Object.java中是直接通過判斷兩個對象的地址是否相等(判斷是否為同一個對象)來區(qū)分的举反,等價于【==】的判斷懊直。
基礎類型都復寫了equals方法,判斷對象的內容是否相等火鼻。
查閱大量的資料顯示室囊,理論上來說,java只有值傳遞魁索。
但是從功能定義上來說融撞,我認為:
Java 中的基本類型,屬于值傳遞粗蔚。
Java 中的引用類型尝偎,屬于引用傳遞。
什么是值傳遞
指在調用函數(shù)時將實際參數(shù)復制一份傳遞到函數(shù)中鹏控,這樣在函數(shù)中如果對參數(shù)進行修改致扯,將不會影響到實際參數(shù)。
什么是引用傳遞
是指在調用函數(shù)時將實際參數(shù)的地址直接傳遞到函數(shù)中(的形參)当辐,那么在函數(shù)中對參數(shù)所進行的修改抖僵,將會影響到實際參數(shù)。
重載發(fā)生在一個類中缘揪,同名的方法如果有不同的參數(shù)列表(類型不同耍群、個數(shù) 不同、順序不同)則視為重載找筝。
重寫發(fā)生在子類與父類之間世吨,重寫要求子類重寫之后的方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問呻征,不能比父類被重寫方法聲明更多的異常耘婚。
ArrayList和LinkedList
ArrayList:
基于動態(tài)數(shù)組,連續(xù)內存存儲陆赋,適合下標訪問(隨機訪問)
擴容機制:因為數(shù)組長度固定沐祷,超出長度存數(shù)據(jù)時需要新建數(shù)組嚷闭,然后將老數(shù)組的數(shù)據(jù)拷貝到新數(shù)組,如果不是尾部插入數(shù)據(jù)還會涉及到元素的移動(往后復制一份赖临,插入新元素)胞锰,使用尾插法并指定初始容量可以極大提升性能、甚至超過linkedList(需要創(chuàng)建大量的node對象)
LinkedList:
基于鏈表兢榨,可以存儲在分散的內存中嗅榕,適合做數(shù)據(jù)插入及刪除操作,不適合查詢:需要逐一遍歷遍歷LinkedList必須使用iterator不能使用for循環(huán)吵聪,因為每次for循環(huán)體內通過get(i)取得某一元素時都需要對list重新進行遍歷凌那,性能消耗極大。另外不要試圖使用indexOf等返回元素索引吟逝,并利用其進行遍歷帽蝶,使用indexlOf對list進行了遍歷,當結果為空時會遍歷整個列表块攒。
for励稳、forEach,Iterator在ArrayList囱井、LinkedList上的性能比較
································here for loop······················ArrayList····················
execution time:? 0.6844 ms.
································here forEach loop··················ArrayList····················
execution time:? 1.9834 ms.
································here iterator loop·················ArrayList····················
execution time:? 0.8786 ms.
································································································
·····After many tests, in most cases, the for the most efficiency, followed by the iterator.····
································································································
································here for loop·····················LinkedList····················
execution time:3035.3940 ms.
································here forEach loop·················LinkedList····················
execution time:? 1.4215 ms.
································here iterator loop················LinkedList····················
execution time:? 0.8353 ms.
for對于ArrayList的遍歷效率略勝一籌驹尼,與iterator差不多;
對于LinkedList庞呕,for效率最差扶欣,iterator最高。
String 和 StringBuffer千扶、StringBuilder 的區(qū)別是什么料祠?
?可變性線程安全性能
Stringfinal修飾,不可變線程安全澎羞,常量對象不可變賦值生成一個新的對象髓绽,指針指向新對象
StringBuffer繼承AbstractStringBuilder類,值保存在char[]value中妆绞,可變線程安全顺呕,對方法添加了同步鎖相同情況下使用 StringBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險
StringBuilder線程不安全
?抽象類與接口
抽象是對類的抽象括饶,是一種模板設計株茶,而接口是對行為的抽象, 是一種行為的規(guī)范图焰。
實現(xiàn):抽象類的子類使用 extends 來繼承启盛;接口必須使用 implements 來實現(xiàn)接口。
構造函數(shù):抽象類可以有構造函數(shù);接口不能有僵闯。
實現(xiàn)數(shù)量:類可以實現(xiàn)很多個接口卧抗;但只能繼承一個抽象類【java只支持單繼承】。
訪問修飾符:抽象類中的抽象方法可以使用Public 和 Protected修飾鳖粟,如果抽象方法修飾符為Private社裆,則報錯:The abstract method 方法名 in type Test can only set a visibility modifier, one of public or protected。接口中的方法默認使用 public 修飾向图; 接口中除了static泳秀、final變量,不能有其他變量榄攀,而抽象類中則不一定 嗜傅。
有抽象方法的一定是抽象類,抽象類不一定有抽象方法航攒。編碼報錯:Abstract method in non-abstract class
定義:
程序:程序是指按照一定的規(guī)則和順序的任務執(zhí)行過程趴梢,就是指令和數(shù)據(jù)的集合漠畜。指令是一系列的命令或則代碼,數(shù)據(jù)是指一堆二進制的代碼坞靶。它一種二進制文件憔狞,存儲在磁盤中,靜態(tài)的不占用系統(tǒng)資源(cpu內存)彰阴;
進程:進程是指一個具有獨立功能的程序在某個數(shù)據(jù)集合上的一次動態(tài)執(zhí)行過程它是操作系統(tǒng)進行資源分配的調度的基本單元瘾敢。一個任務運行可以激活多個進程,這些進程相互合作一起完成該任務尿这。
進程具有并發(fā)性簇抵、動態(tài)性、交互性和獨立性等主要特征射众。
線程:被稱為輕量級進程碟摆。線程可以對進程的內存空間和資源進行訪問,并與同一進程中其他線程共享叨橱,是任務調度和執(zhí)行的最小單位典蜕。一個進程有多個線程,其中每個線程共享該進程所擁有的資源罗洗。
進程是資源調度的基本單位
線程是處理器調度和分派的基本單位愉舔,是程序執(zhí)行的最小單位
一個進程由多個線程執(zhí)行,線程是一個進程中代碼的不同執(zhí)行路線
進程間相互獨立伙菜,同一進程下的線程共享進程的內存空間轩缤,且對其他進程不可見;
進程狀態(tài):
就緒狀態(tài) --> 運行狀態(tài):當處于就緒狀態(tài)的進程被調度后,獲得處理機資源典奉,此時進程就由就緒狀態(tài)轉換成運行狀態(tài)躺翻。
運行狀態(tài)-->阻塞狀態(tài):處于運行狀態(tài)的進程在時間片用完之后,不得不讓出處理機卫玖,此時進程就有運行狀態(tài)轉成阻塞狀態(tài)公你。
阻塞狀態(tài)-->就緒狀態(tài):當進程等待的事件到來時,中斷處理程序必須把相應進程的狀態(tài)由阻塞狀態(tài)轉換成就緒狀態(tài)。
線程中狀態(tài)轉換:
Java線程中狀態(tài)轉換:
RUNNABLE與BLOCKED的狀態(tài)轉換:
只有線程在等待synchronized的隱式鎖時灾茁,synchronized修飾的方法杈曲、代碼塊同一時刻只能允許一個線程執(zhí)行,其他線程只能等待剪芥,這種情況下,等待的線程就會從RUNNABLE轉換到BLOCKED狀態(tài)琴许。而當?shù)却@得到synchronized隱式鎖時税肪,就又會從BLOCKED轉換到RUNNABLE狀態(tài)。
并且java層面上不關心操作系統(tǒng)進程的調度狀態(tài)榜田,因為在JVM看來益兄,等待CPU使用權和等待IO沒有區(qū)別,都是在等待某個資源箭券,因此都被歸為RUNNABLE狀態(tài)净捅。
RUNNABLE與WAITING的狀態(tài)轉換
獲取到synchronized的隱式鎖的進程,調用無參數(shù)的Object.wait()方法
調用無參數(shù)的Thread.join()方法
調用LOCKSupport.park()方法辩块。LockSupport.unpark(Thread thread)課喚醒目標線程蛔六,目標線程的狀態(tài)又會從WAITNG狀態(tài)轉換成RUNNABLE狀態(tài)
RUNNABLE與TIMED_WEAITING的狀態(tài)轉換
調用帶超時參數(shù)的Thread.sleep(long millis)方法
獲得synchronized隱式鎖的線程,調用帶超時參數(shù)的Object.wait(long timeout)方法
調用帶超時參數(shù)的LockSupport.parkNanos(Object blocker,long dealing)方法
調用帶超時參數(shù)鎖的Thread.join(long millis)方法
調用帶超時參數(shù)的LockSupport.parkUnitl(long deadline)方法
從NEW到RUNNABLE狀態(tài)
線程在創(chuàng)建成功后废亭,調用其對應的start方法就會從RUNNABLE狀態(tài)轉換成
從RUNNABLE到TERMINATED狀態(tài)
通過調用Thread.interrupt()方法国章,也可以調用stop()方法,但當前方法被廢止了豆村。
創(chuàng)建線程有哪幾種方式
創(chuàng)建線程有三種方式:
1)繼承 Thread 重寫 run 方法捉腥;
2)實現(xiàn) Runnable 接口;
3)實現(xiàn) Callable 接口你画。
?runnable 和 callable有什么區(qū)別抵碟?
runnable 沒有返回值,callable 可以拿到有返回值坏匪,callable 可看作是 runnable 的補充拟逮。
sleep() 和wait() 有什么區(qū)別
類的不同:sleep() 來自Thread,wait() 來自Object适滓。
釋放鎖:sleep() 不釋放鎖敦迄;wait()釋放鎖。
用 法 不 同 : sleep() 時 間 到 會 自 動 恢 復 ; wait() 可 以 使 用 notify()/notifyAll()直接喚醒罚屋。
notify()和 notifyAll()有什么區(qū)別苦囱?
notify()和notifyAll()都可以用于線程的喚醒
喚醒數(shù)量不同
notify()方法只會隨機喚醒等待隊列中的一個線程,而notifyAll()方法則會喚醒等待隊列中的所有線程脾猛。
?調用方式不同
notify()和notifyAll()方法都必須在同步代碼塊中調用撕彤,并且必須包含在synchronized塊中
且必須是該對象的監(jiān)視器對象才能夠調用。
而且只有在獲取了鎖之后才能調用猛拴,否則會拋出IllegalMonitorStateException異常羹铅。
競爭情況不同
notify()方法只會喚醒等待隊列中的一個線程,并使其與其他線程競爭獲取鎖愉昆,這可能會導致某些線程無法被喚醒或者一直處于等待狀態(tài)职员。
notifyAll()方法則會喚醒等待隊列中的所有線程,并使它們競爭獲取鎖跛溉,這樣可以使所有線程都有機會獲取鎖并進入運行狀態(tài)焊切,從而避免了一些線程一直處于等待狀態(tài)。
run() 和 start() 有什么區(qū)別芳室?
start() 方法用于啟動線程专肪,start() 只能調用一次;執(zhí)行多次會報錯IllegalThreadStateException
run() 方法用于執(zhí)行線程的運行時代碼渤愁。run() 可以重復調用牵祟。只是在當前線程下執(zhí)行這段代碼塊深夯,并不會啟動一個新得線程抖格。
線程池創(chuàng)建時的參數(shù)
public ThreadPoolExecutor(int corePoolSize,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int maximumPoolSize,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? long keepAliveTime,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit unit,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BlockingQueue<Runnable> workQueue,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ThreadFactory threadFactory,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RejectedExecutionHandler handler)
corePoolSize:線程池核心線程大小
線程池中會維護一個最小的線程數(shù)量,即使這些線程處理空閑狀態(tài)咕晋,他們也不會被銷毀雹拄,線程池剛創(chuàng)建時,里面沒有一個線程掌呜,當調用 execute() 方法添加一個任務時滓玖,如果正在運行的線程數(shù)量小于corePoolSize,則馬上創(chuàng)建新線程并運行這個任務质蕉。
maximumPoolSize:最大線程數(shù)
理想的線程數(shù)势篡,使用 2倍cpu核心數(shù)
當前線程數(shù)達到corePoolSize后,如果繼續(xù)有任務被提交到線程池模暗,會將任務緩存到工作隊列中禁悠。如果隊列也已滿,則會去創(chuàng)建一個新線程來出來這個處理兑宇。線程池不會無限制的去創(chuàng)建新線程碍侦,它會有一個最大線程數(shù)量的限制。
keepAliveTime:空閑線程存活時間
一個線程如果處于空閑狀態(tài),并且當前的線程數(shù)量大于corePoolSize瓷产,那么在指定時間后站玄,這個空閑線程會被銷毀,這里的指定時間由keepAliveTime來設定濒旦。
unit:空閑線程存活時間單位
workQueue:工作隊列
存放待執(zhí)行任務的隊列(五種):當提交的任務數(shù)超過核心線程數(shù)大小后株旷,再提交的任務就存放在工作隊列,任務調度時再從隊列中取出任務疤估。
threadFactory:線程工廠
創(chuàng)建線程的工廠灾常,可以設定線程名、線程編號等铃拇。
handler:拒絕策略
當線程池線程數(shù)已滿钞瀑,并且工作隊列達到限制,新提交的任務使用拒絕策略處理慷荔。
處理策略:
比如5個核心線程雕什,10個最大線程,工作隊列20显晶。
情況一:
5個核心線程在工作贷岸,還有19個任務等待執(zhí)行,那么19個排進隊列
情況二:
5個核心線程在工作磷雇,還有23個任務等待執(zhí)行偿警,其中20個排進隊列,剩下3個開啟新線程執(zhí)行
情況三:
5個核心線程在工作唯笙,還有26個任務等待執(zhí)行螟蒸,其中20個排進隊列,剩下5個開啟新線程執(zhí)行崩掘,多出的一個就執(zhí)行設置的拒絕策略
submit() 和 execute() 方法區(qū)別
execute():只能執(zhí)行 Runnable 類型的任務七嫌。
submit():可以執(zhí)行Runnable 和 Callable 類型的任務。
Callable 類型的任務可以獲取執(zhí)行的返回值苞慢,而Runnable執(zhí)行無返回值诵原。
保證多線程的運行安全
使用安全類,比如 Java. util. concurrent 包下的類挽放。
使用自動鎖 synchronized绍赛。
使用手動鎖 Lock
session 和 cookie 有什么區(qū)別?
存儲位置不同:session 存儲在服務器端辑畦;cookie 存儲在瀏覽器端吗蚌。
安全性不同:cookie 安全性一般,在瀏覽器存儲航闺,可以被偽造和修改褪测。
容量和個數(shù)限制:cookie 有容量限制猴誊,每個站點下的 cookie 也有個數(shù)限制。
存儲的多樣性:session 可以存儲在 Redis 中侮措、數(shù)據(jù)庫中懈叹、應用程序中;而 cookie 只能存儲在瀏覽器中
throw 和 throws 的區(qū)別分扎?
throw:是真實拋出一個異常澄成。
throws:是聲明可能會拋出一個異常。
final畏吓、finally墨状、finalize 有什么區(qū)別?
final:是修飾符菲饼,如果修飾類肾砂,此類不能被繼承;如果修飾方法和變量宏悦,則 表示此方法和此變量不能在被改變镐确,只能使用。
finally:是 try{} catch{} finally{} 最后一部分饼煞,表示不論 發(fā)生任何情況 都會執(zhí)行源葫,finally 部分可以省略,但如果 finally 部分存在砖瞧,則一定會執(zhí)行 finally 里面的代碼息堂。
finalize: 是 Object 類的一個方法,在垃圾收集器執(zhí)行的時候會調用被回收對象的此方法块促。
finally語句什么時候不會執(zhí)行
try語句沒有被執(zhí)行到荣堰,如在try語句之前就返回了,這樣finally語句就不會執(zhí)行褂乍,這也說明了finally語句被執(zhí)行的必要而非充分條件是:相應的try語句一定被執(zhí)行到持隧。
在try塊中有System.exit(0);這樣的語句即硼,System.exit(0);是終止Java虛擬機JVM的逃片,連JVM都停止了,所有都結束了只酥,當然finally語句也不會被執(zhí)行到褥实。
Java中violate關鍵字
Java中的內存模型
Java內存模型規(guī)定了所有的變量都存儲在主內存中。每條線程中還有自己的工作內存裂允,線程的工作內存中保存了被該線程所使用到的變量(這些變量是從主內存中拷貝而來)损离。線程對變量的所有操作(讀取,賦值)都必須在工作內存中進行绝编。不同線程之間也無法直接訪問對方工作內存中的變量僻澎,線程間變量值的傳遞均需要通過主內存來完成貌踏。
基于此種內存模型,便產(chǎn)生了多線程編程中的數(shù)據(jù)“臟讀”等問題窟勃。
結果:
thread2:2
thread2:3
thread1:2
thread2:4
thread1:5
可以看到祖乳,出現(xiàn)了兩個2的打印,一個線程修改i秉氧,另外一個線程也讀取到了這個修改后的i眷昆,而不是剛才此線程+1后的1;而且在線程1讀取到2時汁咏,線程2的值已經(jīng)修改為了3亚斋。并且多次運行這段代碼可能帶來不同的運行結果
如果給操作加上鎖,則可以保證運行與預期一致攘滩。
并發(fā)編程的三大概念:原子性帅刊,有序性,可見性
原子性
即一個操作或者多個操作 要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷漂问,要么就都不執(zhí)行
x = 10; //語句1
y = x; //語句2
x++; //語句3
x = x + 1; //語句4
其實只有語句1是原子性操作厚掷,其他三個語句都不是原子性操作。
語句1是直接將數(shù)值10賦值給x级解,也就是說線程執(zhí)行這個語句的會直接將數(shù)值10寫入到工作內存中冒黑。
語句2實際上包含2個操作,它先要去讀取x的值勤哗,再將x的值寫入工作內存抡爹,雖然讀取x的值以及將x的值寫入工作內存這2個操作都是原子性操作,但是合起來就不是原子性操作了芒划。
同樣的冬竟,x++和 x = x+1包括3個操作:讀取x的值,進行加1操作民逼,寫入新的值泵殴。
所以上面4個語句只有語句1的操作具備原子性。
Java內存模型只保證了基本讀取和賦值是原子性操作拼苍,如果要實現(xiàn)更大范圍操作的原子性笑诅,可以通過synchronized和Lock來實現(xiàn)。由于synchronized和Lock能夠保證任一時刻只有一個線程執(zhí)行該代碼塊疮鲫,那么自然就不存在原子性問題了吆你,從而保證了原子性。
可見性
可見性是指當多個線程訪問同一個變量時俊犯,一個線程修改了這個變量的值妇多,其他線程能夠立即看得到修改的值。
上面的例子也闡述了燕侠,多線程對于同一個變量的可見性問題者祖。
violate保證了可見性:
當一個共享變量被volatile修飾時立莉,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時七问,它會去內存中讀取新值桃序。
而普通的共享變量不能保證可見性,因為普通共享變量被修改之后烂瘫,什么時候被寫入主存是不確定的媒熊,當其他線程去讀取時,此時內存中可能還是原來的舊值坟比,因此無法保證可見性芦鳍。
另外,通過synchronized和Lock也能夠保證可見性葛账,synchronized和Lock能保證同一時刻只有一個線程獲取鎖然后執(zhí)行同步代碼柠衅,并且在釋放鎖之前會將對變量的修改刷新到主存當中。因此可以保證可見性。
有序性
即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。編譯器在編譯代碼時會進行指令重排序(Instruction Reorder)前方。
指令重排序,一般來說喝峦,處理器為了提高程序運行效率,可能會對輸入代碼進行優(yōu)化呜达,它不保證程序中各個語句的執(zhí)行先后順序同代碼中的順序一致谣蠢,但是它會保證程序最終執(zhí)行結果和代碼順序執(zhí)行的結果是一致的。
Java內存模型
在Java虛擬機規(guī)范中試圖定義一種Java內存模型(Java Memory Model查近,JMM)來屏蔽各個硬件平臺和操作系統(tǒng)的內存訪問差異眉踱,以實現(xiàn)讓Java程序在各種平臺下都能達到一致的內存訪問效果。那么Java內存模型規(guī)定了哪些東西呢霜威,它定義了程序中變量的訪問規(guī)則谈喳,往大一點說是定義了程序執(zhí)行的次序。注意戈泼,為了獲得較好的執(zhí)行性能婿禽,Java內存模型并沒有限制執(zhí)行引擎使用處理器的寄存器或者高速緩存來提升指令執(zhí)行速度,也沒有限制編譯器對指令進行重排序矮冬。也就是說谈宛,在java內存模型中次哈,也會存在緩存一致性問題和指令重排序的問題胎署。
舉個簡單的例子:在java中,執(zhí)行下面這個語句:
i? = 10;
執(zhí)行線程必須先在自己的工作線程中對變量i所在的緩存行進行賦值操作窑滞,然后再寫入主存當中琼牧。而不是直接將數(shù)值10寫入主存當中恢筝。
violate
一旦一個共享變量(類的成員變量、類的靜態(tài)成員變量)被volatile修飾之后巨坊,那么就具備了兩層語義:
1)保證了不同線程對這個變量進行操作時的可見性撬槽,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的趾撵。
2)禁止進行指令重排序侄柔。
//線程1
boolean stop = false;
while(!stop){
doSomething();
}
//線程2
stop = true;
這段代碼是很典型的一段代碼,很多人在中斷線程時可能都會采用這種標記辦法占调。但是事實上暂题,這段代碼會完全運行正確么?即一定會將線程中斷么究珊?不一定薪者,也許在大多數(shù)時候,這個代碼能夠把線程中斷剿涮,但是也有可能會導致無法中斷線程(雖然這個可能性很小言津,但是只要一旦發(fā)生這種情況就會造成死循環(huán)了)。在前面已經(jīng)解釋過取试,每個線程在運行過程中都有自己的工作內存悬槽,那么線程1在運行的時候,會將stop變量的值拷貝一份放在自己的工作內存當中瞬浓。
那么當線程2更改了stop變量的值之后陷谱,但是還沒來得及寫入主存當中,線程2轉去做其他事情了瑟蜈,那么線程1由于不知道線程2對stop變量的更改烟逊,因此還會一直循環(huán)下去。
但是用volatile修飾之后就變得不一樣了:
第一:使用volatile關鍵字會強制將修改的值立即寫入主存铺根;
第二:使用volatile關鍵字的話宪躯,當線程2進行修改時,會導致線程1的工作內存中緩存變量stop的緩存無效(反映到硬件層的話位迂,就是CPU的L1或者L2緩存中對應的緩存行無效)访雪;
第三:由于線程1的工作內存中緩存變量stop的緩存行無效,所以線程1再次讀取變量stop的值時會去主存讀取掂林。
springBoot相關
Spring原始注解
@Component:使用在類上用于實例化Bean
@Component不帶參數(shù)臣缀,默認值為類名首字母小寫。如:在類User上使用@Component泻帮, ? ? ? ? ? ? 相當于@Component("user")精置。
代參數(shù)@Component("xxx"),可以自定義锣杂,xxx在spring容器 必須中唯一脂倦。
@Repository:使用在dao層類上用于實例化bean
與@Component用法一致番宁。
@Controller:使用在web層類上用于實例化bean
與@Component用法一致。
@Service:使用在service層類上用于實例化bean
與@Component用法一致赖阻。
@Autowired? :使用在字段上用于根據(jù)類型依賴注入
@Autowired的注入方式為ByType(根據(jù)類型進行匹配注入)蝶押。簡單來說:優(yōu)先根據(jù)接口類型進行進行匹配(接口只有一個實現(xiàn)類)。
@Qualifier :結合@Autowired一起使用根據(jù)名稱進行依賴注入
@Qualifier要結合@Autowired用火欧,不可以單獨使用棋电。
當一個接口有多個實現(xiàn)類的時候,就要用到@Qualifier去指明需要注入的名稱苇侵。
[這里的名稱就是 @Service("xxx") 中的 xxx]? 這時就會根據(jù)名稱ByName進行匹配并注入离陶。
@Resource:相當于@Autowired和@Qualifier,按照名稱進行注入
@Resource的默認注入方式為ByName(根據(jù)名稱進行匹配)衅檀,若名稱匹配不了招刨,則會ByType(根據(jù)類型進行匹配注入)。@Resourece相當于@Autowired + @Qualifier哀军。
注* 1沉眶、2、3杉适、4都是使用在類上而不是接口上谎倔; 5、6猿推、7一般使用在字段上
?Dart 沒有 「public」「private」等關鍵字片习,默認就是公開的,私有變量使用 下劃線 _開頭蹬叭。
Dart 是單線程模型藕咏,如何運行的看這張圖:
Future
Future,字面意思「未來」秽五,是用來處理異步的工具孽查。
剛才也說過:
Dart 在單線程中是以消息循環(huán)機制來運行的,其中包含兩個任務隊列坦喘,一個是“微任務隊列” microtask queue盲再,另一個叫做“事件隊列” event queue。
Future 默認情況下其實就是往「事件隊列」里插入一個事件瓣铣,當有空余時間的時候就去執(zhí)行答朋,當執(zhí)行完畢后會回調 Future.then(v) 方法。
而我們也可以通過使用 Future.microtask 方法來向 「微任務隊列」中插入一個任務棠笑,這樣就會提高他執(zhí)行的效率梦碗。
因為在 Dart 每一個 isolate 當中,執(zhí)行優(yōu)先級為 : Main > MicroTask > EventQueue
Stream
Stream 和 Feature 一樣,都是用來處理異步的工具叉弦。
但是 Stream 和 Feature 不同的地方是 Stream 可以接收多個異步結果丐一,而Feature 只有一個藻糖。
Stream 的創(chuàng)建可以使用 Stream.fromFuture淹冰,也可以使用 StreamController 來創(chuàng)建和控制。
還有一個注意點是:普通的 Stream 只可以有一個訂閱者巨柒,如果想要多訂閱的話樱拴,要使用 asBroadcastStream()。
main()和runApp()函數(shù)在flutter的作用分別是什么洋满?有什么關系嗎晶乔?
main函數(shù)是類似于java語言的程序運行入口函數(shù)
runApp函數(shù)是渲染根widget樹的函數(shù)
一般情況下runApp函數(shù)會在main函數(shù)里執(zhí)行
什么是widget? 在flutter里有幾種類型的widget?分別有什么區(qū)別牺勾?能分別說一下生命周期嗎正罢?
widget在flutter里基本是一些UI組件有兩種類型的widget,
分別是statefulWidget 和statelessWidget兩種驻民,statelessWidget不會自己重新構建自己翻具,但是statefulWidget會