最近剛從舊公司離職,為面試在做準備,因為平時開發(fā)CV大法用得比較多,很多基礎(chǔ)知識掌握得不是很牢靠以及很多工具框架只會用并不知道內(nèi)部實現(xiàn)。所以正好趁這個機會開個坑,復(fù)習(xí)下Android相關(guān)的基礎(chǔ)和一些面試需要掌握的知識點,內(nèi)容都是基于我在書本及網(wǎng)上查閱的資料整合,筆記性質(zhì)。
系列文章
?Android面試攻略(1)——Android基礎(chǔ)
這篇主要涉及Activity、Fragment、Service、BroadcastReceiver、WebView、Binder。
Activity
Android四大組件中,Activity是我們用的最多也是最基本的組件,因為應(yīng)用的幾乎所有操作都與用戶相關(guān),Activity 提供窗口來和用戶進行交互。
一、Activity的生命周期
Activity生命周期是個老生常談的問題,既然是從基礎(chǔ)開始,那也不能放過。
(1)Activity的四種狀態(tài)
簡單粗暴直接上圖
Running(運行):在屏幕前臺(位于當(dāng)前任務(wù)堆棧的頂部)
Paused(暫停):失去焦點但仍然對用戶可見(覆蓋Activity可能是透明或未完全遮擋)
Stopped(停止):完全被另一個Activity覆蓋
Destroyed(銷毀):退出,完全銷毀
(2)Activity生命周期分析
關(guān)于生命周期的文章網(wǎng)上是一搜一大把,這里貼一下《深入理解Activity的生命周期》這篇文章講得很詳細透徹。
場景分析:
Activity啟動 →onCreate()→onStart()→onResume()
點擊Home鍵回到主界面(Activity不可見)→onPause()→onStop()
當(dāng)我們再次回到原Activity時→onRestart()→onStart()→onResume()
退出當(dāng)前Activity時→onPasue()→onStop()→onDestory()
(3)Activity進程優(yōu)先級
1.Foreground processes 前臺進程
? ? a. 進程中包含處于前臺的正與用戶交互的activity;
? ? b. 進程中包含與前臺activity綁定的service;
? ? c. 進程中包含調(diào)用了startForground()方法的service;
? ? d. 進程中包含正在執(zhí)行onCreate(), onStart(), 或onDestroy()方法的service;
? ? e. 進程中包含正在執(zhí)行onReceive()方法的BroadcastReceiver.
2.Visible processes 可視進程
? ? a. 進程中包含未處于前臺但仍然可見的activity(調(diào)用了activity的onPause()方法, 但沒有調(diào)用onStop()方法). 典型的情況是:運行activity時彈出對話框(類似對話框,將activity遮擋), 此時的activity雖然不是前臺activity, 但其仍然可見.
? ? b. 進程中包含與可見activity綁定的service.可視進程不會被系統(tǒng)殺死, 除非為了保證前臺進程的運行而不得已為之.
3.Service processes 服務(wù)進程
正在運行的Service(不在create(),start(),destory()狀態(tài)中)
4.background processes 后臺進程
如:不可見狀態(tài)的activity
5.Empty processes 空進程
不包含任何處于活動狀態(tài)的進程是一個空進程. 系統(tǒng)經(jīng)常殺死空進程, 這不會造成任何影響. 空進程存在的唯一理由是為了緩存一些啟動數(shù)據(jù), 以便下次可以更快的啟動.
補充:Android的進程的銷毀不需要人工干預(yù),由系統(tǒng)控制
二、Activity的任務(wù)棧
Android中的activity全都歸屬于task管理 。task 是多個 activity 的集合,這些 activity 按照啟動順序排隊存入一個棧(即“back stack”)。android默認會為每個App維持一個task來存放該app的所有activity,task的默認name為該app的packagename。
當(dāng)然我們也可以在AndroidMainfest.xml中申明activity的taskAffinity屬性來自定義task,但不建議使用,如果其他app也申明相同的task,它就有可能啟動到你的activity,帶來各種安全問題(比如拿到你的Intent)。
我們知道,當(dāng)我們退出一個應(yīng)用程序的時候,必須把所有Activity清除出棧,這時候你才能安全的退出程序,任務(wù)棧被銷毀了,數(shù)據(jù)才是處于最安全的狀態(tài)。當(dāng)然,如果你不想銷毀任務(wù)棧,那你要合理的去保存這個任務(wù)棧,這時候這個任務(wù)棧會保存有所有Activity的狀態(tài),以及Activity包含的信息 。任務(wù)棧并不是唯一的,一個APP當(dāng)中可能不止一個任務(wù)棧,但是,在某些情況下,一個Activity可以獨享一個任務(wù)棧,這就是我們接下來會說到的啟動模式中的singleInstance。
三、Activity的啟動模式
首先,要知道為什么Android要給我們提供Activity的啟動模式。我們在做app開發(fā)時,不可避免的需要在不同的頁面之間進行跳轉(zhuǎn),也就是Activity之間的跳轉(zhuǎn),所以說我們肯定需要復(fù)用某些Activity。如果我們跳轉(zhuǎn)到某個以前的Activity的時候,我們希望這個Activity是復(fù)用的,而不是被重新創(chuàng)建的,這樣可以避免資源的浪費。所以Android為了應(yīng)對開發(fā)時對Activity跳轉(zhuǎn)的不同需求,提供了4種啟動模式。
standard (默認模式)
當(dāng)通過這種模式來啟動Activity時, Android總會為目標 Activity創(chuàng)建一個新的實例,并將該Activity添加到當(dāng)前Task棧中。這種方式不會啟動新的Task,只是將新的 Activity添加到原有的Task中。
singleTop
該模式和standard模式基本一致,但有一點不同:當(dāng)將要被啟動的Activity已經(jīng)位于Task棧頂時,系統(tǒng)不會重新創(chuàng)建目標Activity實例,而是直接復(fù)用Task棧頂?shù)腁ctivity。
singleTask
Activity在同一個Task內(nèi)只有一個實例。如果將要啟動的Activity不存在,那么系統(tǒng)將會創(chuàng)建該實例,并將其加入Task棧頂;
如果將要啟動的Activity已存在,且存在棧頂,直接復(fù)用Task棧頂?shù)腁ctivity。
如果Activity存在但是沒有位于棧頂,那么此時系統(tǒng)會把位于該Activity上面的所有其他Activity全部移出Task,從而使得該目標Activity位于棧頂,此時會回調(diào)onNewIntent()方法。
singleInstance
無論從哪個Task中啟動目標Activity,只會創(chuàng)建一個目標Activity實例且會用一個全新的Task棧來裝載該Activity實例(全局單例).
如果將要啟動的Activity不存在,那么系統(tǒng)將會先創(chuàng)建一個全新的Task,再創(chuàng)建目標Activity實例并將該Activity實例放入此全新的Task中。
如果將要啟動的Activity已存在,那么無論它位于哪個應(yīng)用程序,哪個Task中;系統(tǒng)都會把該Activity所在的Task轉(zhuǎn)到前臺,從而使該Activity顯示出來。
四、scheme跳轉(zhuǎn)協(xié)議
android中的scheme是一種頁面內(nèi)跳轉(zhuǎn)協(xié)議,是一種非常好的實現(xiàn)機制。通過定義自己的scheme,可以非常方便的跳轉(zhuǎn)app中的各個頁面。通過scheme協(xié)議,服務(wù)器可以定制化告訴app跳轉(zhuǎn)到那個頁面,可以通過通知欄消息定制化跳轉(zhuǎn)頁面,可以通過H5頁面跳轉(zhuǎn)頁面等。
Fragment
Fragment是Activity中用戶界面的一個行為或者是一部分。主要是支持在大屏幕上動態(tài)和更為靈活的去組合或是交換UI組件,通過將activity的布局分割成若干個fragment,可以在運行時編輯activity的呈現(xiàn),并且那些變化會被保存在由activity管理的后臺棧里面。
Fragment必須總是被嵌入到一個activity之中,并且fragment的生命周期直接受其宿主activity的生命周期的影響。你可以認為fragment是activity的一個模塊零件,它有自己的生命周期,接收它自己的輸入事件,并且可以在activity運行時添加或者刪除。
應(yīng)該將每一個fragment設(shè)計為模塊化的和可復(fù)用化的activity組件。也就是說,你可以在多個activity中引用同一個fragment,因為fragment定義了它自己的布局,并且使用它本身生命周期回調(diào)的行為。
一、Fragment加載中到Activity中的兩種方式
(1)靜態(tài)添加:將fragment寫到Activity的布局文件中
(2)動態(tài)添加:動態(tài)在Activity中通過代碼添加fragment
二、FragmentStatePagerAdapter與FragmentPagerAdapter的區(qū)別
首先上結(jié)論:FragmentStatePagerAdapter相比于FragmentPagerAdapter,更節(jié)省資源。若與ViewPager配合使用,在頁面比較多的情況下,適合使用FragmentStatePagerAdapter,而如果頁面較少,則適合使用FragmentPagerAdapter。
why?先來看一段源碼
第一張圖是FragmentStatePagerAdapter中的destroyItem方法,方法里最后對fragment執(zhí)行了remove操作,也就是將fragment所占用的資源釋放掉了,而第二張FragmentPagerAdapter中的destroyItem方法里,最后只是將fragment與activity進行UI分離,并沒有銷毀回收資源,所以在頁面較少的情況下,使用FragmentPagerAdapter可以保證資源的重復(fù)利用,當(dāng)頁面較多時,使用FragmentStatePagerAdapter保證系統(tǒng)資源的及時回收,以免造成內(nèi)存溢出。
三、Fragment的生命周期
還是先上圖
再來看看fragment和activity兩者之間的生命周期關(guān)系圖
fragment所生存的activity生命周期直接影響著fragment的生命周期,由此針對activity的每一個生命周期回調(diào)都會引發(fā)一個fragment類似的回調(diào)。例如,當(dāng)activity接收到onPause()時,這個activity之中的每個fragment都會接收到onPause()。
fragment有一些額外的生命周期回調(diào)方法(創(chuàng)建和銷毀fragment界面).
onAttach()
當(dāng)fragment被綁定到activity時調(diào)用(Activity會被傳入)。
onCreateView()
將本身的布局構(gòu)建到activity中去(fragment作為activity界面的一部分)
onActivityCreated()
當(dāng)activity的onCreate()函數(shù)返回時被調(diào)用。
onDestroyView()
當(dāng)與fragment關(guān)聯(lián)的視圖體系正被移除時被調(diào)用。
onDetach()
當(dāng)fragment正與activity解除關(guān)聯(lián)時被調(diào)用。
當(dāng)activity接收到它的onCreate()回調(diào)時,activity之中的fragment接收到onActivityCreated()回調(diào)。
一旦activity處于resumed狀態(tài),則可以在activity中自由的添加或者移除fragment。因此,只有當(dāng)activity處于resumed狀態(tài)時,fragment的生命周期才可以獨立變化。 fragment會在 activity離開恢復(fù)狀態(tài)時 再一次被activity推入它的生命周期中。
管理fragment生命周期與管理activity生命周期很相像。像activity一樣,fragment也有三種狀態(tài):
Resumed
fragment在運行中的activity可見。
Paused
另一個activity處于前臺且得到焦點,但是這個fragment所在的activity仍然可見(前臺activity部分透明,或者沒有覆蓋全屏)。
Stopped
fragment不可見。要么宿主activity已經(jīng)停止,要么fragment已經(jīng)從activity上移除,但已被添加到后臺棧中。一個停止的fragment仍然活著(所有狀態(tài)和成員信息仍然由系統(tǒng)保留著)。但是,它對用戶來講已經(jīng)不再可見,并且如果activity被殺掉,它也將被殺掉。
如果activity的進程被殺掉了,在activity被重新創(chuàng)建時,你需要恢復(fù)fragment狀態(tài)。可以執(zhí)行fragment的onSaveInstanceState()來保存狀態(tài)(注意在fragment是在onCreate(),onCreateView(),或onActvityCreate()中進行恢復(fù))。
在生命周期方面,activity與fragment之間一個很重要的不同,就是在各自的后臺棧中是如何存儲的。當(dāng)activity停止時,默認情況下activity被安置在由系統(tǒng)管理的activity后臺棧中; fragment僅當(dāng)在一個事務(wù)被移除時,通過顯式調(diào)用addToBackStack()請求保存的實例,該fragment才被置于由宿主activity管理的后臺棧。
四、Fragment通信
1.在Fragment中調(diào)用Activity中的方法:getActivity()。
2.在Activity中調(diào)用Fragment中的方法:接口回調(diào)。
3.在Fragment中調(diào)用另一個Fragment中的方法:findFragmentById();
Service
一、什么是Service
Service是一個應(yīng)用程序組件,它能夠在后臺執(zhí)行一些耗時較長的操作,并且不提供用戶界面。服務(wù)能被其它應(yīng)用程序的組件啟動,即使用戶切換到另外的應(yīng)用時還能保持后臺運行。此外,應(yīng)用程序組件還能與服務(wù)綁定,并與服務(wù)進行交互,甚至能進行進程間通信(IPC)。 比如,服務(wù)可以處理網(wǎng)絡(luò)傳輸、音樂播放、執(zhí)行文件I/O、或者與content provider進行交互,所有這些都是后臺進行的。
二、Service 與 Thread 的區(qū)別
服務(wù)僅僅是一個組件,即使用戶不再與你的應(yīng)用程序發(fā)生交互,它仍然能在后臺運行。因此,應(yīng)該只在需要時才創(chuàng)建一個服務(wù)。
如果你需要在主線程之外執(zhí)行一些工作,但僅當(dāng)用戶與你的應(yīng)用程序交互時才會用到,那你應(yīng)該創(chuàng)建一個新的線程而不是創(chuàng)建服務(wù)。 比如,如果你需要播放一些音樂,但只是當(dāng)你的activity在運行時才需要播放,你可以在onCreate()中創(chuàng)建一個線程,在onStart()中開始運行,然后在onStop()中終止運行。還可以考慮使用AsyncTask或HandlerThread來取代傳統(tǒng)的Thread類。
由于無法在不同的 Activity 中對同一 Thread 進行控制,這個時候就要考慮用服務(wù)實現(xiàn)。如果你使用了服務(wù),它默認就運行于應(yīng)用程序的主線程中。因此,如果服務(wù)執(zhí)行密集計算或者阻塞操作,你仍然應(yīng)該在服務(wù)中創(chuàng)建一個新的線程來完成(避免ANR)。
三、Service的生命周期
onCreate():服務(wù)正在被創(chuàng)建。
onStartCommand():服務(wù)正在啟動,由startService()調(diào)用觸發(fā),該方法必須返回一個整數(shù)。這個整數(shù)是描述系統(tǒng)在殺死服務(wù)之后應(yīng)該如何繼續(xù)運行。onStartCommand()的返回值必須是以下常量之一:
START_NOT_STICKY:如果系統(tǒng)在onStartCommand()返回后殺死了服務(wù),則不會重建服務(wù)了,除非還存在未發(fā)送的intent。 當(dāng)服務(wù)不再是必需的,并且應(yīng)用程序能夠簡單地重啟那些未完成的工作時,這是避免服務(wù)運行的最安全的選項。
START_STICKY 如果系統(tǒng)在onStartCommand()返回后殺死了服務(wù),則將重建服務(wù)并調(diào)用onStartCommand(),但不會再次送入上一個intent, 而是用null intent來調(diào)用onStartCommand() 。除非還有啟動服務(wù)的intent未發(fā)送完,那么這些剩下的intent會繼續(xù)發(fā)送。 這適用于媒體播放器(或類似服務(wù)),它們不執(zhí)行命令,但需要一直運行并隨時待命。
START_REDELIVER_INTENT:如果系統(tǒng)在onStartCommand()返回后殺死了服務(wù),則將重建服務(wù)并用上一個已送過的intent調(diào)用onStartCommand()。任何未發(fā)送完的intent也都會依次送入。這適用于那些需要立即恢復(fù)工作的活躍服務(wù),比如下載文件。
onBind():客戶端用bindService()綁定服務(wù)。
onUnbind():所有的客戶端都用unbindService()解除了綁定
onRebind():某客戶端正用bindService()綁定到服務(wù),而onUnbind()已經(jīng)被調(diào)用過了
onDestroy():服務(wù)被銷毀。
服務(wù)的生命周期與activity的非常類似。不過,更重要的是你需密切關(guān)注服務(wù)的創(chuàng)建和銷毀環(huán)節(jié),因為后臺運行的服務(wù)是不會引起用戶注意的。
服務(wù)的生命周期——從創(chuàng)建到銷毀——可以有兩種路徑:
一個started服務(wù)
這類服務(wù)由其它組件調(diào)用startService()來創(chuàng)建。然后保持運行,且必須通過調(diào)用stopSelf()自行終止。其它組件也可通過調(diào)用stopService() 終止這類服務(wù)。服務(wù)終止后,系統(tǒng)會把它銷毀。
如果一個Service被startService 方法多次啟動,那么onCreate方法只會調(diào)用一次,onStart將會被調(diào)用多次(對應(yīng)調(diào)用startService的次數(shù)),并且系統(tǒng)只會創(chuàng)建Service的一個實例(因此你應(yīng)該知道只需要一次stopService調(diào)用)。該Service將會一直在后臺運行,而不管對應(yīng)程序的Activity是否在運行,直到被調(diào)用stopService,或自身的stopSelf方法。當(dāng)然如果系統(tǒng)資源不足,android系統(tǒng)也可能結(jié)束服務(wù)。
一個bound服務(wù)
服務(wù)由其它組件(客戶端)調(diào)用bindService()來創(chuàng)建。然后客戶端通過一個IBinder接口與服務(wù)進行通信。客戶端可以通過調(diào)用unbindService()來關(guān)閉聯(lián)接。多個客戶端可以綁定到同一個服務(wù)上,當(dāng)所有的客戶端都解除綁定后,系統(tǒng)會銷毀服務(wù)。(服務(wù)不需要自行終止。)
如果一個Service被某個Activity 調(diào)用 Context.bindService 方法綁定啟動,不管調(diào)用 bindService 調(diào)用幾次,onCreate方法都只會調(diào)用一次,同時onStart方法始終不會被調(diào)用。當(dāng)連接建立之后,Service將會一直運行,除非調(diào)用Context.unbindService 斷開連接或者之前調(diào)用bindService 的 Context 不存在了(如Activity被finish的時候),系統(tǒng)將會自動停止Service,對應(yīng)onDestroy將被調(diào)用。
這兩條路徑并不是完全隔離的。也就是說,你可以綁定到一個已經(jīng)用startService()啟動的服務(wù)上。例如,一個后臺音樂服務(wù)可以通過調(diào)用startService()來啟動,傳入一個指明所需播放音樂的 Intent。 之后,用戶也許需要用播放器進行一些控制,或者需要查看當(dāng)前歌曲的信息,這時一個activity可以通過調(diào)用bindService()與此服務(wù)綁定。在類似這種情況下,stopService()或stopSelf()不會真的終止服務(wù),除非所有的客戶端都解除了綁定。
當(dāng)在旋轉(zhuǎn)手機屏幕的時候,當(dāng)手機屏幕在“橫”“豎”變換時,此時如果你的 Activity 如果會自動旋轉(zhuǎn)的話,旋轉(zhuǎn)其實是 Activity 的重新創(chuàng)建,因此旋轉(zhuǎn)之前的使用 bindService 建立的連接便會斷開(Context 不存在了)。
四、使用Service
(1)startService
1.定義一個類繼承Service。
2.在AndroidMainfest.xml文件中聲明該Service。
<mainfest ...>
...
<application...>
<service android:name=".服務(wù)類名">
</application>
</mainfest>
3.使用Context中的startService(Intent)方法啟動該Service。
4.不再使用時,使用stopService(Intent)方法停止該Service。
(2)bindService
1.創(chuàng)建bindService服務(wù)端,繼承自Service并在類中創(chuàng)建一個實現(xiàn)IBinder接口的實例對象并提供公共方法給客戶端調(diào)用。
public class LocalBinder extends Binder ?{
? ? ? ? // 聲明一個方法,getService() 提供給客戶端調(diào)用
? ? ? ? BindService getService() {
? ? ? ? ? ? //返回當(dāng)前對象LocalService,這樣我們就可以在客戶端調(diào)用Service的公共方法了
? ? ? ? ? ? return BindService.this;
? ? ? }
}
2.從onBind()方法返回此IBinder實例。
/**
?* ?把Binder類返回給客戶端
**/
@Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? public IBinder onBind (Intent intent) {
? ? ? ? return new LocalBinder();
}
3.在客戶端中,從onServiceConnected()回調(diào)方法接收Binder,并使用提供的方法調(diào)用綁定服務(wù)。
ServiceConnection conn = new ServiceConnection() {
? ? ? ? // 與服務(wù)端交互的接口方法,綁定服務(wù)的時候被回調(diào),在這個方法獲取綁定Service ? ? ? ? ? // 傳遞過來的IBinder對象,通過這個對象,實現(xiàn)宿主和Service的交互
? ? ? ? @Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? public void onServiceConnected (ComponentName name, IBinder service) {
? ? ? ? ? ? ? ? ?//獲取IBinder ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?BindService.LocalBinder binder = (BindService.LocalBinder) service;
? ? ? ? ? ? ? ? ?mService = binder.getService();
? ? }
? ? ? ? // 當(dāng)取消綁定的時候被回調(diào),但正常情況下是不被調(diào)用的,它的調(diào)用實際是當(dāng) ? ? ? ? ? ? ? ? ? // Service服務(wù)被意外銷毀時,例如內(nèi)存的資源不足時這個方法才被自動調(diào)用。
? ? ? ? @Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? public void onServiceDisconnected(ComponentName name) {
? ? ? ? ? ? ? ? mService = null;
? ? }
}
ServiceConnection代表與服務(wù)的連接,它只有兩個方法:onServiceConnected()和onServiceDisconnected(),前者是在操作者連接一個服務(wù)成功時回調(diào),而后者是在服務(wù)崩潰或被殺死導(dǎo)致服務(wù)連接中斷時回調(diào)。
4.調(diào)用bindService(Intent intent,ServiceConnection conn,int flags)方法綁定啟動服務(wù)。
Intent intent = new Intent(this,BindService.class);
bindService(intent,conn,Service.BIND_AUTO_CREATE);
其中第三個參數(shù)在進行服務(wù)綁定的時,其flags有:
Context.BIND_AUTO_CREATE
表示收到綁定請求的時候,如果服務(wù)尚未創(chuàng)建,則即刻創(chuàng)建,在系統(tǒng)內(nèi)存不足需要先摧毀優(yōu)先級組件來釋放內(nèi)存,且只有駐留該服務(wù)的進程成為被摧毀對象時,服務(wù)才被摧毀
Context.BIND_DEBUG_UNBIND
通常用于調(diào)試場景中判斷綁定的服務(wù)是否正確,但容易引起內(nèi)存泄漏,因此非調(diào)試目的的時候不建議使用
Context.BIND_NOT_FOREGROUND
表示系統(tǒng)將阻止駐留該服務(wù)的進程具有前臺優(yōu)先級,僅在后臺運行。
5.不使用時,解綁服務(wù)。
unbindService(conn);
Broadcast Receiver
廣播是一種廣泛運用的在應(yīng)用程序之間傳輸信息的機制,主要用來監(jiān)聽系統(tǒng)或者應(yīng)用發(fā)出的廣播信息,然后根據(jù)廣播信息作為相應(yīng)的邏輯處理,也可以用來傳輸少量、頻率低的數(shù)據(jù)。Android中我們要發(fā)送的廣播內(nèi)容是一個Intent,這個Intent中可以攜帶我們要傳送的數(shù)據(jù)。
一、廣播的使用場景
1.同一APP具有多個進程的不同組件之間的消息通信。
2.不同APP之間的組件組件之間消息通信。
二、廣播的種類
1.Normal Broadcast(普通廣播):應(yīng)用在需要通知各個廣播接收者的情況下使用,如 開機啟動。使用方式:sendBroadcast()。
2.System Broadcast(有序廣播):應(yīng)用在需要特定攔截場景下使用,如黑名單短信、電話攔截。使用方式:sendOrderedBroadcast();
3.Local Broadcast(本地廣播):只在自身APP內(nèi)傳播。
三、注冊Broadcast Receiver
定義自己的BroadcastReceiver 類
public class MyBroadcastReceiver extends BroadcastReceiver {
? ? // action 名稱
? ? String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;
? ? public void onReceive(Context context, Intent intent) {
? ? ? ? if (intent.getAction().equals( SMS_RECEIVED )) {
? ? ? ? ? ? // 一個receiver可以接收多個action的,即可以有多個intent-filter,需要在 ? ? ? ? ? ? ? ? ? ? ? ? ? //onReceive里面對intent.getAction(action name)進行判斷。
? ? ? ? }
? ? }
}
1.靜態(tài)注冊
靜態(tài)注冊的廣播接收者是一個常駐在系統(tǒng)中的全局監(jiān)聽器,當(dāng)你在應(yīng)用中配置了一個靜態(tài)的BroadcastReceiver,安裝了應(yīng)用后而無論應(yīng)用是否處于運行狀態(tài),廣播接收者都是已經(jīng)常駐在系統(tǒng)中了。同時應(yīng)用里的所有receiver都在清單文件里面,方便查看。要銷毀掉靜態(tài)注冊的廣播接收者,可以通過調(diào)用PackageManager將Receiver禁用。
在AndroidManifest.xml的application里面定義receiver并設(shè)置要接收的action。
< receiver android:name = ".MyBroadcastReceiver" >
< intent-filter android:priority = "777" >
< action android:name = "android.provider.Telephony.SMS_RECEIVED" />
< /intent-filter >
</ receiver>
這里的priority取值是-1000到1000,值越大優(yōu)先級越高,同時注意加上系統(tǒng)接收短信的限權(quán)。
< uses-permission android:name ="android.permission.RECEIVE_SMS" />
2.動態(tài)注冊
在Activity中聲明BroadcastReceiver的擴展對象,在onResume中注冊,onPause中卸載。動態(tài)注冊的廣播跟隨activity的生命周期。
public class MainActivity extends Activity {
MyBroadcastReceiver receiver;
@Override
protected void onResume() {
// 動態(tài)注冊廣播 (代碼執(zhí)行到這才會開始監(jiān)聽廣播消息,并對廣播消息作為相應(yīng)的處理)
receiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED" );
registerReceiver( receiver , intentFilter);
super.onResume();
}
@Override
protected void onPause() {
// 撤銷注冊 (撤銷注冊后廣播接收者將不會再監(jiān)聽系統(tǒng)的廣播消息)
unregisterReceiver(receiver);
super.onPause();
}
}
3.靜態(tài)注冊和動態(tài)注冊的區(qū)別
1、靜態(tài)注冊的廣播接收者一經(jīng)安裝就常駐在系統(tǒng)之中,不需要重新啟動喚醒接收者;動態(tài)注冊的廣播接收者隨著應(yīng)用的生命周期,由registerReceiver開始監(jiān)聽,由unregisterReceiver撤銷監(jiān)聽,如果應(yīng)用退出后,沒有撤銷已經(jīng)注冊的接收者應(yīng)用應(yīng)用將會報錯。
2、當(dāng)廣播接收者通過intent啟動一個activity或者service時,如果intent中無法匹配到相應(yīng)的組件。動態(tài)注冊的廣播接收者將會導(dǎo)致應(yīng)用報錯,而靜態(tài)注冊的廣播接收者將不會有任何報錯,因為自從應(yīng)用安裝完成后,廣播接收者跟應(yīng)用已經(jīng)脫離了關(guān)系。
四、廣播的內(nèi)部實現(xiàn)機制
1.自定義廣播接收者BroadcastReceiver,并復(fù)寫onReceive()方法。
2.通過Binder機制向AMS(Activity Manager Service)進行注冊。
3.廣播發(fā)送者通過Binder機制向AMS發(fā)送廣播。
4.AMS查找符合相應(yīng)條件(IntentFilter/Permission)的BroadcastReceiver,將廣播發(fā)送到BroadcastReceiver(一般情況下是Activity)相應(yīng)的消息循環(huán)隊列中。
5.消息循環(huán)執(zhí)行拿到此廣播,回調(diào)BroadcastReceiver中的onReceive()方法。
五、LocalBroadcastManager
優(yōu)勢
1.使用它發(fā)送的廣播將只在APP內(nèi)中傳播,因此你不必擔(dān)心泄漏隱私數(shù)據(jù)。
2.其他APP無法對你的APP發(fā)送此廣播,因為你的APP根本就不可能接收到非自身應(yīng)用發(fā)送的該廣播,因此你不必擔(dān)心有安全漏洞可以利用。
3.比系統(tǒng)的全局廣播更加高效。
原理
1.LocalBroadcastManager高效的原因主要是它內(nèi)部是通過Handler實現(xiàn)的,它的sendBroadcast()方法含義并非和我們平時用的一樣,因為它的sendBroadcast()其實是通過handler發(fā)送一個Massage實現(xiàn)的。
2.既然他的內(nèi)部是通過Handler來實現(xiàn)廣播的發(fā)送的,那么相比于系統(tǒng)廣播通過Binder實現(xiàn)肯定是更高效了,同時使用Handler實現(xiàn),別的應(yīng)用無法向我們的應(yīng)用發(fā)送該廣播,而我們應(yīng)用內(nèi)發(fā)送的廣播也不會離開我們的應(yīng)用。
WebView
一、WebView開發(fā)中常見的一些坑
1. webview 在Androidapi16以及之前版本的安全漏洞,該漏洞是因為程序沒有正確的限制webview.addjavascriptinterface方法,讓遠程攻擊者可以使用Java的反射機制利用該漏洞執(zhí)行任意的java對象方法。
2. webview動態(tài)添加到其他布局的時候,在activity銷毀的生命周期時,需要主動調(diào)用webview.removeallviews和webview的ondestory方法釋放內(nèi)存,否則會導(dǎo)致內(nèi)存泄漏。
3. jsbridge ,這應(yīng)該不算是坑,只是一個概念吧。js橋可以允許遠程網(wǎng)頁端與android的native端進行通信,通俗的說就是使用js橋可以在android代碼中調(diào)用網(wǎng)頁的js方法,也可以讓js調(diào)用原生的代碼
4. webview.onpagefinished方法,該方法并不靠譜,按照api上面的說法,在web頁面完全加載完成的時候會回調(diào)該方法,但在實際應(yīng)用過程中,該方法在跳轉(zhuǎn)url的時候會被多次調(diào)用,更加靠譜的方法是使用onprogresschange方法代替該方法的功能,當(dāng)newProgress為100的時候,即是頁面加載完成。
5. 后臺耗電問題,webview加載網(wǎng)頁的時候,會自動創(chuàng)建線程,如果如果使用不當(dāng),這些線程會永遠在后臺運行,導(dǎo)致你的應(yīng)用耗電量居高不下,這個問題的解決方式是在activity的ondetory方法中銷毀webview或者簡單粗暴的調(diào)用System.exit()直接關(guān)閉虛擬機。
6. webview硬件加速導(dǎo)致渲染問題,比如加載的時候會有閃屏現(xiàn)象,解決方式就是暫時關(guān)閉硬件加速。
二、關(guān)于WebView的內(nèi)存泄漏的問題
為什么WebView會造成內(nèi)存泄漏呢?根本原因是由于我們的WebView需要關(guān)聯(lián)一個Activity,而WebView內(nèi)部執(zhí)行的操作是在一個新的線程中的,它的執(zhí)行時間我們Activity并不能確定,所以會導(dǎo)致WebView可能會一直持有該Activity的引用,導(dǎo)致無法回收。其原理類似于匿名內(nèi)部類持有外部類的引用導(dǎo)致外部類無法釋放的問題。所以,為了避免內(nèi)存泄漏問題,我們一般都兩個做法:
1.獨立進程。開啟單獨的一個進程給WebView使用,在Activity銷毀的同時將進程也銷毀。不過可能涉及到進程間通信。
2.動態(tài)添加WebView,對傳入的WebView中的Context使用弱引用。動態(tài)添加WebView的意思是在布局創(chuàng)建個ViewGroup用來放置WebView,Activity創(chuàng)建的時候add進來,在Activity停止的時候remove掉。
Binder
這部分涉及的知識有點多且雜,但卻不是所有都是需要我們掌握的,所以我強烈推薦包建強老師的《寫給Android App開發(fā)人員看的Android底層知識》系列文章。這個系列很全面的總結(jié)了與binder相關(guān)的各種知識,原理,通俗易懂,沒有大篇幅的代碼和對我們暫時無意義的深度知識,值得一看。作為應(yīng)付面試,我這只做簡單總結(jié)。
什么是Binder
1.通常意義下,Binder指的是一種跨進程的通信機制。
2.對于Server進程來說,Binder指的是Binder本地對象,而對于Client進程來說,Binder指的是Binder的代理對象。
3.對于傳輸過程而言,Binder是可以進行跨進程傳遞的對象。