1.Activity
說下Activity的生命周期?
答:
onStart()和onResume()/onPause()和onStop()的區(qū)別?并且這幾個方法在那種場景下用到,具體有什么作用?
答:onStart和onStop是從Activity是否可見這個角度來回調(diào)的,而onResume和onPause是從Activity是否位于前臺這個角度來回調(diào)的。
onstart表示Activity可見,但是還不能與用戶進(jìn)行交互,可以理解為Activity已經(jīng)顯示出來了,但是我們還看不見。
onStop表示Activity即將停止,此時可以做一些稍微重量級的回收工作,但不能太耗時,此時Activity已經(jīng)變得不可見。
onResume表示此時Activity從后臺切換到前臺,可以與用戶進(jìn)行交互,于onstart相比,onStart和onResume都表示Activity可見,但onstart的時候Activity還在后臺,而onResume時Activity從后臺切換到前臺。onPause表示Activity正在停止,此時Activity切換到后臺,不能與用戶進(jìn)行交互。不能再onPause中做重量級的操作。
從Activity的整個生命周期來看,onCreate和ondestory是配對的,分別標(biāo)識著Activity的創(chuàng)建與銷毀,并且只能有一次被調(diào)用。
從Activity的可見來說,onstart和onStop是配對的,隨著用戶的操作或者設(shè)備屏幕的點(diǎn)亮與熄滅,這兩個方法可能被多次調(diào)用。
從Activit是否在前臺來說,onResume和onPause是配對的,隨著用戶的操作或者設(shè)備屏幕的點(diǎn)亮與熄滅,這兩個方法可能多次被調(diào)用。
有兩個Activity,分別為Activity A和Activity B,用A啟動B ,B 的onResume和A的onPause哪個先執(zhí)行?
答:追蹤android的源碼可知,在新的activity啟動之前,棧頂?shù)腁ctivity需要先onPause后,新的Activity才能執(zhí)行。因此在啟動ActivityB之前先執(zhí)行Activity A的onPause,然后啟動Activity B 執(zhí)行Activity B的onResume。
Activity異常生命周期,以及簡單說一下這些方法是做什么用的?
答:情況1:資源相關(guān)的系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被殺死并重新創(chuàng)建
在默認(rèn)情況下,如果我們的Activity不做特殊處理,那么當(dāng)系統(tǒng)配置發(fā)生改變后,Activity就會被銷毀并重新創(chuàng)建,其生命周期如下圖:
當(dāng)系統(tǒng)配置發(fā)生改變后,Activity會被銷毀,其onPause,onStop,onDestroy均會被調(diào)用,由于Activity是在異常情況下終止的,系統(tǒng)會調(diào)用onSaveInstanceState來保存當(dāng)前Activity的狀態(tài)。
如圖:當(dāng)豎屏切換到橫屏?xí)r,測試log如下:
當(dāng)由橫屏切換到豎屏的時候,測試log如下:
由此我們可以看出,當(dāng)系統(tǒng)配置發(fā)生改變后,Activity會被銷毀,其中onPause,onStop,onDestory均會被調(diào)用,同時由于Activity是在異常情況下終止的,系統(tǒng)會調(diào)用onSaveInstanceState來保存當(dāng)前Activity的狀態(tài)。由上圖我們可以看出,onSaveInstanceState調(diào)用時機(jī)是在onStop之前,需要說明的是這個方法只會出現(xiàn)在Activity被異常終止的情況下,正常情況下系統(tǒng)不會回調(diào)這個方法。當(dāng)系統(tǒng)重建的時候會調(diào)用onRestoreInstanceState這個方法,并且把Activity銷毀時onSaveInstanceState方法所保存的Bundle對象作為參數(shù)同時傳遞給onRestoreInstanceState和onCreate方法,因為我們可以通過onCreate和onRestoreInstanceState方法來判斷Activity是否被重建了,如果被重建了,那么我們就可以取出之前保存的數(shù)據(jù)并恢復(fù),從上圖我們可以看出,onRestoreInstanceState的調(diào)用時機(jī)是在onStart之后。
同時,我們知道onSaveInstanceState和onRestoreInstanceState方法當(dāng)中,系統(tǒng)為我們做了一定得恢復(fù)工作。當(dāng)Activity在異常情況下需要重新創(chuàng)建時,系統(tǒng)會默認(rèn)為我們保存當(dāng)前的Activity的視圖結(jié)構(gòu),并且在Activity重啟后為我們恢復(fù)這些數(shù)據(jù)。比如文本框中用戶輸入的數(shù)據(jù),ListVIew滾動的位置等。這些View相關(guān)的狀態(tài)系統(tǒng)都能夠默認(rèn)為我們恢復(fù)。
關(guān)于保存和恢復(fù)View層次結(jié)構(gòu),系統(tǒng)的工作流程是這樣的:首先Activity被意外終止時,Activity會調(diào)用onSaveInstanceState去保存數(shù)據(jù),然后Activity會委托Window去保存數(shù)據(jù),接著Window再委托它上面的頂級容器去保存數(shù)據(jù)。頂層容器是一個ViewGroup,一般來說它可能是DecorView。最后頂層容器再去意義通知它的子元素來保存數(shù)據(jù),這樣整個數(shù)據(jù)保存過程就完成了。可以發(fā)現(xiàn),這就是一種典型的委托思想,上層委托下次,父容器委托子元素去處理一件事情。
針對onSaveInstanceState方法還需要有一點(diǎn)說明,那就是系統(tǒng)只會在Activity即將被銷毀并且有機(jī)會重新顯示的情況下才會調(diào)用它。當(dāng)Activity正常銷毀的時候,系統(tǒng)不會調(diào)用onSaveInstanceState,因為被銷毀的Activity不可能再次被顯示。比如我們上文提到的旋轉(zhuǎn)屏幕所造成的Activity異常銷毀,這個過程和正常停止Activity是不一樣的,因為旋轉(zhuǎn)屏幕后,Activity被銷毀的同時會立刻創(chuàng)建Activity實例,這個時候Activity有機(jī)會再次立刻展示,所以系統(tǒng)要進(jìn)行數(shù)據(jù)存儲。這里可以簡單地這么理解,系統(tǒng)只在Activity異常終止的時候才會調(diào)用onSaveInstanceState和onRestoreInstanceState來存儲和恢復(fù)數(shù)據(jù),其他情況不會觸發(fā)這個過程。
情況2:資源內(nèi)存不足導(dǎo)致低優(yōu)先級的Activity被殺死
這種情況,不是很好模擬,但是其數(shù)據(jù)存儲和恢復(fù)過程和情況1完全一致,這里我們描述一下Activity的優(yōu)先級情況,Activity按照優(yōu)先級從高到低,可以分為如下三種:
1)前臺Activity——正在和用戶交互的Activity,優(yōu)先級最高
2)可見但是非前臺Activity——比如Activity中彈出了一個對話框,導(dǎo)致Activity可見但是位于后臺無法和用戶直接交互
3)后臺Activity——已經(jīng)被暫停的Activity,比如執(zhí)行了onStop,優(yōu)先級最低
當(dāng)系統(tǒng)內(nèi)存不足時,系統(tǒng)就會按照上述優(yōu)先級去殺死目標(biāo)Activity所在的進(jìn)程,并且后續(xù)通過onSaveInstanceState和onRestoreInstanceState來存儲和恢復(fù)數(shù)據(jù),如果一個進(jìn)程中沒有四大組件在執(zhí)行,那么這個進(jìn)程將很快被系統(tǒng)殺死,比較好的方法是將后臺工作放入Service中從而保證進(jìn)程有一定的優(yōu)先級,這樣就不會輕易地被系統(tǒng)殺死。
onSaveInstanceState()和onRestoreInstanceState()何時會調(diào)用?
答:1.onSaveInstanceState(Bundle outState)在什么時機(jī)會被調(diào)用呢?
答案是當(dāng)activity有可能被系統(tǒng)回收的情況下,而且是在onStop()之前。注意是有可能,如果是已經(jīng)確定會被銷毀,比如用戶按下了返回鍵,或者調(diào)用了finish()方法銷毀activity,則onSaveInstanceState不會被調(diào)用。
或者也可以說,此方法只有在activity被異常終止的情況下會被調(diào)用。
總結(jié)下,onSaveInstanceState(Bundle outState)會在以下情況被調(diào)用:
1、當(dāng)用戶按下HOME鍵時。
2、從最近應(yīng)用中選擇運(yùn)行其他的程序時。
3、按下電源按鍵(關(guān)閉屏幕顯示)時。
4、從當(dāng)前activity啟動一個新的activity時。
5、屏幕方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會調(diào)用)。
在前4種情況下,當(dāng)前activity的生命周期為:
onPause -> onSaveInstanceState -> onStop。
2.onRestoreInstanceState什么時機(jī)被調(diào)用?
onRestoreInstanceState(Bundle savedInstanceState)只有在activity確實是被系統(tǒng)回收,重新創(chuàng)建activity的情況下才會被調(diào)用。
比如第5種情況屏幕方向切換時,activity生命周期如下:
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
在這里onRestoreInstanceState被調(diào)用,是因為屏幕切換時原來的activity確實被系統(tǒng)回收了,又重新創(chuàng)建了一個新的activity。
而按HOME鍵返回桌面,又馬上點(diǎn)擊應(yīng)用圖標(biāo)回到原來頁面時,activity生命周期如下:
onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume
因為activity沒有被系統(tǒng)回收,因此onRestoreInstanceState沒有被調(diào)用。
如果onRestoreInstanceState被調(diào)用了,則頁面必然被回收過,則onSaveInstanceState必然被調(diào)用過。
onSaveInstanceState()與onPause()的區(qū)別?如何避免配置改變時Activity重建?
答:前者適用于對臨時性狀態(tài)的保存,而后者適用于對數(shù)據(jù)的持久化保存。
如果不手動配置Activity configChanges屬性,系統(tǒng)默認(rèn)對Activity進(jìn)行銷毀重建操作。
API13以下,Activity configChanges屬性中含有orientation即可避免Activity銷毀重建。
API13及以上,Activity configChanges屬性中同時含有orientation和screenSize即可避免Activity銷毀重建。
Activity啟動模式有哪些?具體說一下使用場景,并繪制進(jìn)出棧的圖形?
答:1.standard
在Activity的棧中無論該活動有沒有加入棧,活動就會被創(chuàng)建。
standard加載模式的棧如下所示,無論棧中有沒有該對象的實例,都會被創(chuàng)建。
2.singleTop模式
只要被創(chuàng)建的活動不位于棧的頂部,該活動就會被創(chuàng)建入棧。如果將要被創(chuàng)建的活動位于棧的頂部,該活動的實例就不會被創(chuàng)建。測試方法,把上面的模式直接改成singleTop模式,MainActivty往自己身上跳轉(zhuǎn)就不會從新創(chuàng)建一個新的實例,會重用之前在棧頂中的實例。如果是MainActivty跳轉(zhuǎn)到SecondActivty, 然后SecondActivity再次跳轉(zhuǎn)到MainActivty, 那么此時的MainActivity將會被創(chuàng)建,因為棧頂是SecondActivity。如下所示:
應(yīng)用場景一:可以解決重復(fù)打開activity的問題,例如?點(diǎn)擊注冊按鈕(500毫秒你點(diǎn)擊了兩次) , 如果你用系統(tǒng)默認(rèn)的啟動模式, 就會打開2個注冊頁面,singTop啟動模式的意思是開啟actiivty的時候系統(tǒng)會先判斷此activity是否存在棧頂, 如果存在就激活此實例?因此就可以解決上述的問題。
應(yīng)用場景二:在瀏覽器的書簽?特點(diǎn):檢查棧頂是否存在這個實例?如果存在則不重新創(chuàng)建
單任務(wù)模式,這個也不難理解,如果從MainActivty跳轉(zhuǎn)到SecondActivity, 如果再從SecondActivty跳轉(zhuǎn)到MainActivity, 在單任務(wù)模式下MainActivity已經(jīng)在棧中,就會把它之前的Activity出棧,使其處于在棧頂活躍的位置。原理如下圖所示:
應(yīng)用場景:瀏覽器???特點(diǎn):該實例在任務(wù)棧只能存在一個,如果再啟動,則把上面的Activity實例全部清除。A --> B --> C -->D在D點(diǎn)擊返回鍵,就返回到A。
可以看成單例模式,這個比較特殊,被設(shè)置成singleInstance的Activity將會放入另一個棧中,因為這樣為了便于共用。上面3中模式位于同一個棧中。下方ThirdActivity跳轉(zhuǎn)到一個加載模式為singleInstance的Activity中。
應(yīng)用場景:來電顯示界面?特點(diǎn):該實例Activity會創(chuàng)建一個單獨(dú)的任務(wù)棧,且與用戶正在交互的界面的任務(wù)棧在????前端,直到全部Activity退出.
從非activity中開啟activity為什么要添加flag,不添加會出現(xiàn)什么問題?
答:如果不是在Activity中啟動的,那就可以看做不是用戶主動的行為,也就說這個界面可能出現(xiàn)在任何APP之上,如果不用Intent.FLAG_ACTIVITY_NEW_TASK將其限制在自己的Task中,那用戶可能會認(rèn)為該Activity是當(dāng)前可見APP的頁面,這是不合理的。舉個例子:我們在聽音樂,這個時候如果郵件Service突然要打開一個Activity,如果不用Intent.FLAG_ACTIVITY_NEW_TASK做限制,那用戶可能認(rèn)為這個Activity是屬于音樂APP的,因為用戶點(diǎn)擊返回的時候,可能會回到音樂,而不是郵件(如果郵件之前就有界面)。
onNewIntent()調(diào)用時機(jī)?onNewIntent()具體有什么作用?
答:singleInstance:
第一次進(jìn)入:onCreate onStart
在棧頂再次進(jìn)入: onNewIntent
不在棧頂再次進(jìn)入:onNewIntent onRestart onStart
按home鍵再次進(jìn)入:onRestart onStart
按返回鍵:onRestart onStart
standard:
第一次進(jìn)入:onCreate onStart
在棧頂再次進(jìn)入: onCreate onStart
不在棧頂再次進(jìn)入:onCreate onStart
按home鍵再次進(jìn)入:onRestart onStart
按返回鍵:onRestart onStart
singleTop:
第一次進(jìn)入:onCreate onStart
在棧頂再次進(jìn)入:onNewIntent
不在棧頂再次進(jìn)入:onCreate onStart
按home鍵再次進(jìn)入:onRestart onStart
按返回鍵:onRestart onStart
singleTask:
第一次進(jìn)入:onCreate onStart
在棧頂再次進(jìn)入:onNewIntent
不在棧頂再次進(jìn)入:onNewIntent onRestart onStart
按home鍵再次進(jìn)入:onRestart onStart
按返回鍵:onRestart onStart
具體作用:
1. standard ?
默認(rèn)啟動模式,每次激活A(yù)ctivity時都會創(chuàng)建Activity,并放入任務(wù)棧中,永遠(yuǎn)不會調(diào)用onNewIntent()。 ?
2. singleTop ?
如果在任務(wù)的棧頂正好存在該Activity的實例, 就重用該實例,并調(diào)用其onNewIntent(),否者就會創(chuàng)建新的實例并放入棧頂(即使棧中已經(jīng)存在該Activity實例,只要不在棧頂,都會創(chuàng)建實例,而不會調(diào)用onNewIntent(),此時就跟standard模式一樣)。 ?
3. singleTask ?
如果在棧中已經(jīng)有該Activity的實例,就重用該實例(會調(diào)用實例的onNewIntent())。重用時,會讓該實例回到棧頂,因此在它上面的實例將會被移除棧。如果棧中不存在該實例,將會創(chuàng)建新的實例放入棧中(此時不會調(diào)用onNewIntent())。 ??
4. singleInstance ?
在一個新棧中創(chuàng)建該Activity實例,并讓多個應(yīng)用共享該棧中的該Activity實例。一旦改模式的Activity的實例存在于某個棧中,任何應(yīng)用再激活改Activity時都會重用該棧中的實例,其效果相當(dāng)于多個應(yīng)用程序共享一個應(yīng)用,不管誰激活該Activity都會進(jìn)入同一個應(yīng)用中。 ?
當(dāng)調(diào)用到onNewIntent(intent)的時候,需要在onNewIntent() 中使用setIntent(intent)賦值給Activity的Intent.否則,后續(xù)的getIntent()都是得到老的Intent。
2.Service
Service的生命周期
答:
onCreate():
首次創(chuàng)建服務(wù)時,系統(tǒng)將調(diào)用此方法。如果服務(wù)已在運(yùn)行,則不會調(diào)用此方法,該方法只調(diào)用一次。
onStartCommand():
當(dāng)另一個組件通過調(diào)用startService()請求啟動服務(wù)時,系統(tǒng)將調(diào)用此方法。
onDestroy():
當(dāng)服務(wù)不再使用且將被銷毀時,系統(tǒng)將調(diào)用此方法。
onBind():
當(dāng)另一個組件通過調(diào)用bindService()與服務(wù)綁定時,系統(tǒng)將調(diào)用此方法。
onUnbind():
當(dāng)另一個組件通過調(diào)用unbindService()與服務(wù)解綁時,系統(tǒng)將調(diào)用此方法。
onRebind():
當(dāng)舊的組件與服務(wù)解綁后,另一個新的組件與服務(wù)綁定,onUnbind()返回true時,系統(tǒng)將調(diào)用此方法。
1)啟動Service服務(wù)
單次:startService() —> onCreate() —> onStartCommand()
多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()
2)停止Service服務(wù)
stopService() —> onDestroy()
3)綁定Service服務(wù)
bindService() —> onCreate() —> onBind()
4)解綁Service服務(wù)
unbindService() —> onUnbind() —> onDestroy()
5)啟動綁定Service服務(wù)
startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()
6)解綁停止Service服務(wù)
unbindService() —> onUnbind() —> stopService() —> onDestroy()
7)解綁綁定Service服務(wù)
unbindService() —> onUnbind(ture) —> bindService() —> onRebind()
Activity如與Service通信?
Activity與Service進(jìn)行通信的三種方式
第一種 簡單通信
直接通過Intent進(jìn)行傳值,我們在啟動一個Service的時候通過Intent的對象向Service進(jìn)行傳值,這種方式傳遞值比較不方便,性能不是很高。
(1)在MainActivity中通過啟動服務(wù)和終止服務(wù)的按鈕分別調(diào)用startService(intent)和stopService(intent)來啟動和停止服務(wù)
(2)在Myservice中,我們通過onStartCommand(finalIntent intent,?int?flags,?intstartId)這個函數(shù)來接收從Activity傳過來的值
第二種 Binder
我們在綁定服務(wù)的時候,首先要做的就是讓我們的MainActivity實現(xiàn)ServiceConnection類,實現(xiàn)這個類之后,我們還需要重寫ServiceConnection類中的兩個方法onServiceConnected和onServiceDisconnected,這兩個方法分別是在綁定成功和服務(wù)所在進(jìn)程崩潰的時候被調(diào)用,如果綁定成功了,那么onServiceConnected(ComponentName componentName, IBinder iBinder) 就會被執(zhí)行,然后第二個參數(shù)IBinder正是MyService中onBind()方法的返回值,因此我們可以通過這個返回值來想MyService傳遞數(shù)據(jù)。
(1)MyService 中我們創(chuàng)建一個Binder類,讓其實現(xiàn)android.os.Binder類,并且定義一個方法setData,然后我們通過onBind()方法將其對象返回MainActivity。
(2)在MainActivity中,首先添加一個Binder對象,然后在ServiceConnection中獲取MyService中返回的Binder對象,接著我們通過Binder對象調(diào)用它的方法setData向其傳遞數(shù)據(jù)
第三種,監(jiān)聽服務(wù)中的進(jìn)程的變化
在MyService中
(1)添加一個公開的接口Callback
(2)在MyService內(nèi)部添加一個變量
(3)向外界派發(fā)信息
(4)在Binder中返回一個當(dāng)前的MyService對象,然外部可以添加事件的綁定
在MainActivity中我們可以通過onServiceConnected方法中的iBinder對象來實現(xiàn)MyService中Callback的接口,由于 onDataChange() 是執(zhí)行在子線程中的,因此我們需要再定義一個Hander對象,將任務(wù)由子線程切換到主線程中,讓主線程來進(jìn)行 UI 操作
startService,bindService交叉使用時的生命周期
答:(1)針對startService和bindService:
a.先startService,則調(diào)用onCreate,onStartCommand;當(dāng)bindService時,再調(diào)用onBind(不會再調(diào)用onCreate)
b.先bindServcie,則調(diào)用onCreate,onBind;當(dāng)startService時,再調(diào)用onStartCommand
(2)針對stopService和unbindService:
a.先stopService,則不會調(diào)用任何方法,然后當(dāng)unbindService時,依次調(diào)用onUnbind和onDestroy方法
b.先unbindService,則會調(diào)用onUnbind,然后當(dāng)stopService時,會調(diào)用onDestroy
詳細(xì)的log記錄如下,其中,MyService中定義MyServiceBinder類(繼承自IBinder),該類中定義了start方法(當(dāng)Activity與service建立連接時,onServiceConnected中調(diào)用),end方法(當(dāng)連接異常斷開時,onServiceDisconnected中調(diào)用):
1.StartService
(1)start
12-02?07:39:50.986?24755-24755/com.example.wtx.service?D/MyService:?onCreate
12-02?07:39:50.986?24755-24755/com.example.wtx.service?D/MyService:?onStartCommand
(2)stop
12-02?07:40:31.731?24755-24755/com.example.wtx.service?D/MyService:?onDestroy
2.BindService
(1)bind
12-02?07:42:12.212?24755-24755/com.example.wtx.service?D/MyService:?onCreate
12-02?07:42:12.212?24755-24755/com.example.wtx.service?D/MyService:?onBind
12-02?07:42:12.226?24755-24755/com.example.wtx.service?D/MyService:?MyServiceBinder.start
(2)unbind
12-02?07:42:25.559?24755-24755/com.example.wtx.service?D/MyService:?onUnbind
12-02?07:42:25.559?24755-24755/com.example.wtx.service?D/MyService:?onDestroy
3.先Start再bind
(1)start
12-02?07:44:34.809?24755-24755/com.example.wtx.service?D/MyService:?onCreate
12-02?07:44:34.809?24755-24755/com.example.wtx.service?D/MyService:?onStartCommand
(2)bind
12-02?07:44:47.059?24755-24755/com.example.wtx.service?D/MyService:?onBind
12-02?07:44:47.075?24755-24755/com.example.wtx.service?D/MyService:?MyServiceBinder.start
(3)stop
無新log打印
(4)unbind
12-02?07:45:19.796?24755-24755/com.example.wtx.service?D/MyService:?onUnbind
12-02?07:45:19.796?24755-24755/com.example.wtx.service?D/MyService:?onDestroy
4.先Start再bind
(1)start
12-02?07:46:05.332?24755-24755/com.example.wtx.service?D/MyService:?onCreate
12-02?07:46:05.332?24755-24755/com.example.wtx.service?D/MyService:?onStartCommand
(2)bind
12-02?07:46:06.395?24755-24755/com.example.wtx.service?D/MyService:?onBind
12-02?07:46:06.409?24755-24755/com.example.wtx.service?D/MyService:?MyServiceBinder.start
(3)unbind
12-02?07:46:18.284?24755-24755/com.example.wtx.service?D/MyService:?onUnbind
(4)stop
12-02?07:46:56.315?24755-24755/com.example.wtx.service?D/MyService:?onDestroy
5.先Bind再start
(1)bind
12-02?07:48:04.821?24755-24755/com.example.wtx.service?D/MyService:?onCreate
12-02?07:48:04.821?24755-24755/com.example.wtx.service?D/MyService:?onBind
12-02?07:48:04.832?24755-24755/com.example.wtx.service?D/MyService:?MyServiceBinder.start
(2)start
12-02?07:48:18.636?24755-24755/com.example.wtx.service?D/MyService:?onStartCommand
(3)unbind
12-02?07:48:34.132?24755-24755/com.example.wtx.service?D/MyService:?onUnbind
(4)stop
12-02?07:48:45.197?24755-24755/com.example.wtx.service?D/MyService:?onDestroy
6.先Bind再start
(1)bind
12-02?07:51:01.932?11090-11090/com.example.wtx.service?D/MyService:?onCreate
12-02?07:51:01.932?11090-11090/com.example.wtx.service?D/MyService:?onBind
12-02?07:51:01.944?11090-11090/com.example.wtx.service?D/MyService:?MyServiceBinder.start
(2)start
12-02?07:51:09.613?11090-11090/com.example.wtx.service?D/MyService:?onStartCommand
(3)stop
無新log打印
(4)unbind
12-02?07:51:23.592?11090-11090/com.example.wtx.service?D/MyService:?onUnbind
12-02?07:51:23.592?11090-11090/com.example.wtx.service?D/MyService:?onDestroy
前臺服務(wù)是什么?和普通服務(wù)的不同?如何去開啟一個前臺服務(wù)?如何去做保活?
前臺服務(wù)是那些被認(rèn)為用戶知道(用戶所認(rèn)可的)且在系統(tǒng)內(nèi)存不足的時候不允許系統(tǒng)殺死的服務(wù)。前臺服務(wù)必須給狀態(tài)欄提供一個通知,它被放到正在運(yùn)行(Ongoing)標(biāo)題之下——這就意味著通知只有在這個服務(wù)被終止或從前臺主動移除通知后才能被解除。
前臺服務(wù)與普通服務(wù)的區(qū)別:
前臺Service的系統(tǒng)優(yōu)先級更高、不易被回收;
前臺Service會一直有一個正在運(yùn)行的圖標(biāo)在系統(tǒng)的狀態(tài)欄顯示,下拉狀態(tài)欄后可以看到更加詳細(xì)的信息,非常類似于通知的效果。
實現(xiàn)前臺服務(wù)的步驟:
其實很簡單就是將服務(wù)創(chuàng)建后創(chuàng)建一個Notification就好,利用提供好的方法進(jìn)行顯示和移除就好
1.創(chuàng)建一個Service,在這個Service的onCreate方法中創(chuàng)建一個Notification
要請求讓服務(wù)運(yùn)行于前臺,請調(diào)用 startForeground()。此方法采用兩個參數(shù):唯一標(biāo)識通知的整型數(shù)和狀態(tài)欄的 Notification。
注意:提供給 startForeground() 的整型 ID 不得為 0。
如何保活后臺服務(wù):
1. 提高Service的優(yōu)先級:
android:priority="1000"
2.把service寫成系統(tǒng)服務(wù),將不會被回收:
在Manifest.xml文件中設(shè)置persistent屬性為true,則可使該服務(wù)免受out-of-memory?killer的影響。但是這種做法一定要謹(jǐn)慎,系統(tǒng)服務(wù)太多將嚴(yán)重影響系統(tǒng)的整體運(yùn)行效率。?
3.將服務(wù)改成前臺服務(wù)foreground?service:
重寫onStartCommand方法,使用StartForeground(int,Notification)方法來啟動service。??
注:一般前臺服務(wù)會在狀態(tài)欄顯示一個通知,最典型的應(yīng)用就是音樂播放器,只要在播放狀態(tài)下,就算休眠也不會被殺,如果不想顯示通知,只要把參數(shù)里的int設(shè)為0即可。?
同時,對于通過startForeground啟動的service,onDestory方法中需要通過stopForeground(true)來取消前臺運(yùn)行狀態(tài)。?
4.利用Android的系統(tǒng)廣播
利用ANDROID的系統(tǒng)廣播檢查Service的運(yùn)行狀態(tài),如果被殺掉,就再起來,系統(tǒng)廣播是Intent.ACTION_TIME_TICK,這個廣播每分鐘發(fā)送一次,我們可以每分鐘檢查一次Service的運(yùn)行狀態(tài),如果已經(jīng)被結(jié)束了,就重新啟動Service。?
3.Broadcast Receiver
廣播有哪些注冊方式?有什么區(qū)別?
答:1、在應(yīng)用程序的代碼中注冊(動態(tài)注冊)
注冊BroadcastReceiver:
registerReceiver(receiver,filter);
取消注冊BroadcastReceiver:
unregisterReceiver(receiver);
當(dāng)BroadcastReceiver更新UI,通常會使用這樣的方法注冊。啟動Activity時候注冊BroadcastReceiver,Activity不可見時候,取消注冊。
2、在androidmanifest.xml當(dāng)中注冊(靜態(tài)注冊)
<receiver>
????<intent-filter>
?????<action android:name = "android.intent.action.PICK"/>
????</intent-filter>
</receiver>
?1)第一種不是常駐型廣播,也就是說廣播跟隨程序的生命周期。
2)第二種是常駐型,也就是說當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來,程序也會被系統(tǒng)調(diào)用自動運(yùn)行。
使用這樣的方法注冊弊端:它會始終處于活動狀態(tài),畢竟是手機(jī)開發(fā),cpu和電源資源比較少,一直處于活動耗費(fèi)大,不利。
BroadcastReceiver與LocalBroadcastReceiver有什么區(qū)別?
答:BroadcastReceiver是針對應(yīng)用間、應(yīng)用與系統(tǒng)間、應(yīng)用內(nèi)部進(jìn)行通信的一種方式
LocalBroadcastReceiver僅在自己的應(yīng)用內(nèi)發(fā)送接收廣播,也就是只有自己的應(yīng)用能收到,數(shù)據(jù)更加安全廣播只在這個程序里,而且效率更高。
BroadcastReceiver 使用
1.制作intent(可以攜帶參數(shù))
2.使用sendBroadcast()傳入intent;
3.制作廣播接收器類繼承BroadcastReceiver重寫onReceive方法(或者可以匿名內(nèi)部類啥的)
4.在java中(動態(tài)注冊)或者直接在Manifest中注冊廣播接收器(靜態(tài)注冊)使用registerReceiver()傳入接收器和intentFilter
5.取消注冊可以在OnDestroy()函數(shù)中,unregisterReceiver()傳入接收器
LocalBroadcastReceiver 使用
LocalBroadcastReceiver不能靜態(tài)注冊,只能采用動態(tài)注冊的方式。
在發(fā)送和注冊的時候采用,LocalBroadcastManager的sendBroadcast方法和registerReceiver方法
4.ContentProvider
ContentProvider、ContentResolver與ContentObserver之間的關(guān)系是什么?
答:ContentProvider:Android應(yīng)用程序運(yùn)行在不同的進(jìn)程空間中,因此不同應(yīng)用程序的數(shù)據(jù)是不能夠直接訪問的。為了增強(qiáng)程序之間的數(shù)據(jù)共享能力,Android系統(tǒng)提供了像SharedPreferences這類簡單的跨越程序邊界的訪問方法,但這些方法都存在一定的局限性,提供數(shù)據(jù)的能力有限,安卓系統(tǒng)提供了另一種跨進(jìn)程提供數(shù)據(jù)的方式也就ContentProvider翻譯過來叫做:數(shù)據(jù)提供者,是應(yīng)用程序之間共享數(shù)據(jù)的一種接口機(jī)制,其他應(yīng)用程序則可以在不知道數(shù)據(jù)來源的情況下,對共享數(shù)據(jù)進(jìn)行增刪改查等操作。在Android系統(tǒng)中,許多系統(tǒng)內(nèi)置的數(shù)據(jù)也是通過ContentProvider提供給用戶使用,例如通訊錄、音視頻圖像文件等。
調(diào)用者不能直接調(diào)用ContentProvider的接口函數(shù),需要通過ContentResolver對象,通過URI間接調(diào)用ContentProvider,Android系統(tǒng)根據(jù)URI確定處理這個查詢的ContentProvider。
ContentObserver:內(nèi)容觀察者,觀察)特定Uri引起的數(shù)據(jù)庫的變化,繼而做一些相應(yīng)的處理,當(dāng)ContentObserver所觀察的Uri發(fā)生變化時,便會觸發(fā)它回調(diào)onChange方法。ContentObserver的編寫:創(chuàng)建一個類繼承自ContentObserver,重寫onChange,監(jiān)聽的的url數(shù)據(jù)發(fā)生變化時就會回調(diào)此方法。
簡單說一下ContentProvider的權(quán)限管理
答:在AndroidManifest.xml中provider標(biāo)簽中有三個額外的參數(shù)permission、readPermission、writePermission;
先看下面這段代碼:
<provider
? ? android:name=".PeopleContentProvider"
? ? android:authorities="com.harvic.provider.PeopleContentProvider"
? ? android:exported="true"
? ? android:permission="com.harvic.contentProviderBlog"
? ? android:readPermission="com.harvic.contentProviderBlog.read"
? ? android:writePermission="com.harvic.cotentProviderBlog.write"/>
在這段代碼中有幾個參數(shù)要特別注意一下:
exported:這個屬性用于指示該服務(wù)是否能被其他程序應(yīng)用組件調(diào)用或跟他交互; 取值為(true | false),如果設(shè)置成true,則能夠被調(diào)用或交互,否則不能;設(shè)置為false時,只有同一個應(yīng)用程序的組件或帶有相同用戶ID的應(yīng)用程序才能啟動或綁定該服務(wù)。具體參見:《Permission Denial: opening provider 隱藏的android:exported屬性的含義
readPermission:使用Content Provider的查詢功能所必需的權(quán)限,即使用ContentProvider里的query()函數(shù)的權(quán)限;
writePermission:使用ContentProvider的修改功能所必須的權(quán)限,即使用ContentProvider的insert()、update()、delete()函數(shù)的權(quán)限;
permission:客戶端讀、寫 Content Provider 中的數(shù)據(jù)所必需的權(quán)限名稱。 本屬性為一次性設(shè)置讀和寫權(quán)限提供了快捷途徑。 不過,readPermission和writePermission屬性優(yōu)先于本設(shè)置。 如果同時設(shè)置了readPermission屬性,則其將控制對 Content Provider 的讀取。 如果設(shè)置了writePermission屬性,則其也將控制對 Content Provider 數(shù)據(jù)的修改。也就是說如果只設(shè)置permission權(quán)限,那么擁有這個權(quán)限的應(yīng)用就可以實現(xiàn)對這里的ContentProvider進(jìn)行讀寫;如果同時設(shè)置了permission和readPermission那么具有readPermission權(quán)限的應(yīng)用才可以讀,擁有permission權(quán)限的才能寫!也就是說只擁有permission權(quán)限是不能讀的,因為readPermission的優(yōu)先級要高于permission;如果同時設(shè)置了readPermission、writePermission、permission那么permission就無效了。