Android面試攻略(1)——Android基礎(chǔ)

最近剛從舊公司離職,為面試在做準備,因為平時開發(fā)CV大法用得比較多,很多基礎(chǔ)知識掌握得不是很牢靠以及很多工具框架只會用并不知道內(nèi)部實現(xiàn)。所以正好趁這個機會開個坑,復(fù)習(xí)下Android相關(guān)的基礎(chǔ)和一些面試需要掌握的知識點,內(nèi)容都是基于我在書本及網(wǎng)上查閱的資料整合,筆記性質(zhì)。

系列文章

?Android面試攻略(1)——Android基礎(chǔ)

Android面試攻略(2)——異步消息處理機制

Android面試攻略(3)——View相關(guān)

這篇主要涉及Activity、Fragment、Service、BroadcastReceiver、WebView、Binder。

Activity

Android四大組件中,Activity是我們用的最多也是最基本的組件,因為應(yīng)用的幾乎所有操作都與用戶相關(guān),Activity 提供窗口來和用戶進行交互。


一、Activity的生命周期

Activity生命周期是個老生常談的問題,既然是從基礎(chǔ)開始,那也不能放過。

(1)Activity的四種狀態(tài)

簡單粗暴直接上圖

activity狀態(tài)關(guān)系圖

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

網(wǎng)絡(luò)截圖


二、FragmentStatePagerAdapter與FragmentPagerAdapter的區(qū)別

首先上結(jié)論:FragmentStatePagerAdapter相比于FragmentPagerAdapter,更節(jié)省資源。若與ViewPager配合使用,在頁面比較多的情況下,適合使用FragmentStatePagerAdapter,而如果頁面較少,則適合使用FragmentPagerAdapter。

why?先來看一段源碼

FragmentStatePagerAdapter源碼
FragmentPagerAdapter源碼

第一張圖是FragmentStatePagerAdapter中的destroyItem方法,方法里最后對fragment執(zhí)行了remove操作,也就是將fragment所占用的資源釋放掉了,而第二張FragmentPagerAdapter中的destroyItem方法里,最后只是將fragment與activity進行UI分離,并沒有銷毀回收資源,所以在頁面較少的情況下,使用FragmentPagerAdapter可以保證資源的重復(fù)利用,當(dāng)頁面較多時,使用FragmentStatePagerAdapter保證系統(tǒng)資源的及時回收,以免造成內(nèi)存溢出。


三、Fragment的生命周期

還是先上圖

圖片來自網(wǎng)絡(luò)

再來看看fragment和activity兩者之間的生命周期關(guān)系圖

圖片來自網(wǎng)絡(luò)

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的生命周期

圖片來自網(wǎng)絡(luò)

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是可以進行跨進程傳遞的對象。


參考資料

全面了解Activity

Service全面總結(jié)

BroadcastReceiver使用總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,242評論 25 708
  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-androi...
    eddy_wiki閱讀 3,298評論 0 20
  • 1.什么是Activity?問的不太多,說點有深度的 四大組件之一,一般的,一個用戶交互界面對應(yīng)一個activit...
    JoonyLee閱讀 5,760評論 2 51
  • 曾幾何時,自己也想暢快淋漓地寫一篇令人贊嘆的文章。只可惜,鄙人生性不愛讀書,所以筆墨不夠豐厚,每次抬筆便不知下詞在...
    Puppy831閱讀 376評論 0 0
  • 1995年11月15日聯(lián)合國教科文組織確定每年4月23日為“世界圖書與版權(quán)日”,也被稱為“世界讀書日”,設(shè)定此節(jié)日...
    RhettY閱讀 701評論 1 7