2.1 Activity
2.1.1 Activity的生命周期全面分析
典型情況下的生命周期:在用戶(hù)參與的情況下膝晾,Activity所經(jīng)過(guò)的生命周期改變区宇。
異常情況下的生命周期:Activity被系統(tǒng)回收或者設(shè)備的Configuration發(fā)生改變從而導(dǎo)致Activity被銷(xiāo)毀重建。
1. 典型情況下的生命周期分析
- ==onPause必須執(zhí)行完四啰,新的Activity的onResume才會(huì)執(zhí)行==吆鹤。比如A打開(kāi)B,生命周期順序是 A onPause->B onCreate ->B onStart ->B onResume ->A onStop队塘。因此onPause不能做耗時(shí)操作,才能使新的Activity盡快顯示出來(lái)宜鸯。
- onStart表示Activity已經(jīng)可見(jiàn)但還在后臺(tái)我們看不見(jiàn)憔古,沒(méi)有出現(xiàn)在前臺(tái)無(wú)法與用戶(hù)進(jìn)行交互。
- onResume表示Activity已經(jīng)出現(xiàn)在前臺(tái)且可以與用戶(hù)進(jìn)行交互淋袖。
- Activity切換到后臺(tái)( 用戶(hù)打開(kāi)新的Activity或者切換到桌面) 鸿市,onPause->onStop(如果新Activity采用了透明主題,則當(dāng)前Activity不會(huì)回調(diào)onStop)。
2. 異常情況下的生命周期分析
(1) 情況1:系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被殺死并重新創(chuàng)建
比如橫豎屏切換焰情,默認(rèn)情況下陌凳,Activity就會(huì)銷(xiāo)毀重建。生命周期如下:
當(dāng)系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被銷(xiāo)毀内舟,onPause->onStop->onDestroy合敦,還會(huì)==調(diào)用onSaveIntanceState保存當(dāng)前Activity的狀態(tài),時(shí)機(jī)是在onStop之前==(僅僅出現(xiàn)在Activity異常終止的情況)验游。當(dāng)Activity被重建時(shí)充岛,==會(huì)調(diào)用onRestoreInstanceState,時(shí)機(jī)是在onStart之后==耕蝉,并把onSaveIntanceState中保存的Bundle對(duì)象同時(shí)傳遞給onRestoreInstanceState和onCreate崔梗。注意,onRestoreInstanceState被調(diào)用垒在,其入?yún)undle一定有值蒜魄,但是onCreate的Bundle入?yún)⒖赡軙?huì)null,建議采用onRestoreInstanceState去恢復(fù)數(shù)據(jù)场躯。
==每個(gè)View都有onSaveIntanceState和onRestoreInstanceState方法谈为,系統(tǒng)會(huì)自動(dòng)幫我們做一些恢復(fù)工作==,具體恢復(fù)哪些數(shù)據(jù)踢关,要看每個(gè)View的實(shí)現(xiàn)峦阁。大致的工作流程如下:Activity意外終止,調(diào)用onSaveIntanceState去保存數(shù)據(jù)耘成,然后Activity委托Window去保存數(shù)據(jù),Window再委托上面的頂層容器(ViewGroup驹闰,一般為DecorView)去保存數(shù)據(jù)瘪菌,頂層容器再去一一通知它的子元素來(lái)保存數(shù)據(jù)。(這是一種典型的委托思想嘹朗,事件分發(fā)和View的繪制過(guò)程也是采用類(lèi)似的思想)
系統(tǒng)只有在Activity異常終止的情況下师妙,才會(huì)調(diào)用onSaveIntanceState和onRestoreInstanceState方法,其他情況不會(huì)調(diào)用屹培。
(2) 情況2:內(nèi)存資源不足默穴,導(dǎo)致低優(yōu)先級(jí)Activity被殺死
Activity按照優(yōu)先級(jí)分類(lèi):
1)前臺(tái)Activity
2)可見(jiàn)非前臺(tái)Activity
3)后臺(tái)Activity
系統(tǒng)內(nèi)存不足時(shí),低優(yōu)先級(jí)的Activity所在進(jìn)程會(huì)被殺死褪秀,并通過(guò)onSaveIntanceState和onRestoreInstanceState來(lái)存儲(chǔ)和恢復(fù)數(shù)據(jù)蓄诽。
3. 系統(tǒng)配置發(fā)生改變,Activity如何不重新創(chuàng)建媒吗?
在Activity的配置中仑氛,配置屬性android:configChanges="orientation|screenSize"
,可以在屏幕旋轉(zhuǎn)的時(shí)候,不重新創(chuàng)建Activity锯岖,取而代之的是==調(diào)用Activity的onConfigurationChanged
方法==介袜。
其他可以配置項(xiàng)目如下表所示
項(xiàng)目 | 含義 |
---|---|
locale | 一般指切換了系統(tǒng)語(yǔ)言 |
keboardHidden | 鍵盤(pán)的可訪(fǎng)問(wèn)性發(fā)生了變化 |
orientation | 屏幕方向發(fā)生了變化 |
screenSize | 屏幕尺寸信息發(fā)生了變化,旋轉(zhuǎn)屏幕尺寸信息就會(huì)發(fā)生變化出吹。若編譯版本小于13遇伞,不會(huì)導(dǎo)致Activity的重啟;若大于13捶牢,則會(huì)導(dǎo)致Activity的重啟 |
2.1.2 Activity的啟動(dòng)模式
1. Activity的LaunchMode
四種啟動(dòng)模式:standard鸠珠、singleTop、singleTask和singleInstance叫确。
(1) standard
默認(rèn)模式跳芳,誰(shuí)啟動(dòng)了Activity,那么這個(gè)Activity就運(yùn)行在啟動(dòng)它的那個(gè)Activity所在的棧中竹勉。非Activity類(lèi)型的Context(如:ApplicationContext)并沒(méi)有所謂的任務(wù)棧飞盆,所以就會(huì)有問(wèn)題。
(2) singleTop
棧頂復(fù)用模式次乓。新的Activity位于任務(wù)棧的頂部吓歇,那么此Activity不會(huì)重新創(chuàng)建(onCreate、onStart不會(huì)調(diào)用)票腰,此時(shí)==onNewIntent(Intent intent)會(huì)被調(diào)用==城看,入?yún)⒕褪俏覀冋?qǐng)求的信息。
(3) singleTask
棧內(nèi)復(fù)用模式杏慰。比如啟動(dòng)ActivityA测柠,系統(tǒng)會(huì)先找A想要的任務(wù)棧,若不存在缘滥,則創(chuàng)建任務(wù)棧轰胁、A實(shí)例、入棧朝扼;若存在赃阀,棧中已經(jīng)存在A實(shí)例,移除A之上的元素擎颖,使A在棧頂榛斯,并調(diào)用onNewIntent
,否則創(chuàng)建A實(shí)例搂捧,入棧驮俗。
(4) singleInstance
加強(qiáng)的singTask模式,加強(qiáng)一點(diǎn):此類(lèi)Activity只能單獨(dú)的位于一個(gè)任務(wù)棧中允跑,==后續(xù)的請(qǐng)求均不會(huì)創(chuàng)建新的Activity意述,除非這個(gè)任務(wù)棧被銷(xiāo)毀。==
(5) Activity所需的任務(wù)棧
==TaskAffinity==:任務(wù)相關(guān)性,標(biāo)識(shí)了一個(gè)Activity所需要的任務(wù)棧的名字荤崇,默認(rèn)情況下是包名拌屏。==必須和singleTask啟動(dòng)模式或者allowTaskReparenting屬性配對(duì)使用==,否則沒(méi)有意義术荤。
任務(wù)棧分為前臺(tái)任務(wù)棧和后臺(tái)任務(wù)棧倚喂,后臺(tái)任務(wù)棧中的Activity處于暫停狀態(tài),用戶(hù)可以通過(guò)切換將后臺(tái)任務(wù)棧調(diào)到前臺(tái)瓣戚。
TaskAffinity和allowTaskReparenting結(jié)合使用:現(xiàn)在有2個(gè)應(yīng)用A和B端圈,A啟動(dòng)了B的Activity C,然后按Home鍵回到桌面子库,然后單擊桌面圖標(biāo)啟動(dòng)B舱权,此時(shí)并不是啟動(dòng)B的主Activity,而是重新顯示了Activity C或者說(shuō)仑嗅,==C從A的任務(wù)棧轉(zhuǎn)移到了B的任務(wù)棧==宴倍。可以這么理解仓技,由于A啟動(dòng)了C鸵贬,這個(gè)C只能運(yùn)行在A所在的任務(wù)棧中,但是C是屬于B應(yīng)用的脖捻,正常情況下阔逼,它的TaskAffinity應(yīng)該是B的包名。所以地沮,B被啟動(dòng)之后嗜浮,B會(huì)創(chuàng)建自己的任務(wù)棧,此時(shí)系統(tǒng)發(fā)現(xiàn)C原本想要的任務(wù)棧已經(jīng)有了,就會(huì)把C從A的任務(wù)棧中轉(zhuǎn)移過(guò)。
(6) 指定啟動(dòng)模式的兩種方式
第一種:通過(guò)AndroidMenifest指定android:launchMode="singTask"
第二種:通過(guò)Intent的addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
2. Activity的Flags
Flags有很多,有些可以設(shè)定Activity的啟動(dòng)模式,有些可以影響Activity的運(yùn)行狀態(tài)教藻。
FLAG_ACTIVITY_NEW_TASK
為Activity指定“singleTask”啟動(dòng)模式
FLAG_ACTIVITY_SINGLE_TOP
為Activity指定“singleTop”啟動(dòng)模式
FLAG_ACTIVITY_CLEAR_TOP
具有此標(biāo)記的Activity在啟動(dòng)時(shí),同一個(gè)任務(wù)棧中位于它上面的都要出棧伪煤。與singleTask一起使用朵诫,若實(shí)例已經(jīng)存在,會(huì)調(diào)用newIntent方法速侈;若被啟動(dòng)的Activity是standard率寡,則它自己也會(huì)出棧,然后重新創(chuàng)建一個(gè)新的Activity實(shí)例入棧倚搬。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
等價(jià)于android:excludeFromRecents="true"
冶共,表明==此Activity不會(huì)出現(xiàn)在歷史Activity列表中==。
三、IntentFilter的匹配規(guī)則
Activity的啟動(dòng)分為顯式調(diào)用和隱式調(diào)用捅僵。隱式調(diào)用需要Intent能夠匹配目標(biāo)組件的IntentFilter中設(shè)置的過(guò)濾信息家卖,不匹配將無(wú)法啟動(dòng)目標(biāo)Activity。IntentFilter的過(guò)濾信息有action庙楚、category上荡、data。
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<category android:name="com.hilton.controlpanel.category.SELF_MAINTAIN" />
<data android:scheme="file" />
</intent-filter>
1. action的匹配規(guī)則
是一個(gè)區(qū)分大小寫(xiě)的字符串馒闷,一個(gè)過(guò)濾規(guī)則中可以有多個(gè)action酪捡。
匹配要求:Intent中的action存在,且能夠和過(guò)濾規(guī)則中的==任何一個(gè)action相同==即可匹配成功纳账。
2. category的匹配規(guī)則
是一個(gè)字符串逛薇,一個(gè)過(guò)濾規(guī)則中可以有多個(gè)category。
匹配要求:Intent可以沒(méi)有category疏虫,但是如果有永罚,==每一個(gè)都要和過(guò)濾規(guī)則中任何一個(gè)的匹配==。在startActivity或者startActivityForResult的時(shí)候议薪,會(huì)默認(rèn)加上“android.inent.category,DEFULT”尤蛮,所以必須在intent-filter中指定這個(gè)category。
3. data的匹配規(guī)則
由兩部分組成:mimeType和URI斯议。一個(gè)過(guò)濾規(guī)則中可以有多個(gè)产捞。
匹配要求:Intent中必須有,且==和過(guò)濾規(guī)則中的一個(gè)相匹配==哼御。
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
(1) mimeType
指媒體類(lèi)型坯临,如 image/jpeg、vide/*等恋昼,可以表示圖片看靠、文本、視頻等不同的媒體格式液肌、
<data android:mimeType="image/*"/>
如上匹配規(guī)則==指定了媒體類(lèi)型為所有類(lèi)型的圖片挟炬,雖然沒(méi)有指定URI,但是卻默認(rèn)為content和file==嗦哆。Intent中的URI部分的scheme必須為content或者file才能匹配谤祖。如下:
intent.setDataAndType(Uri.parse("file://abc"),"image/png")
setDataAndType(Uri data, String type)
指定Data和Type屬性,因?yàn)?code>setAction(String action)和addCategory(String category)
2個(gè)方法會(huì)相互覆蓋老速,所以當(dāng)要指定Data和Type時(shí)粥喜,使用這個(gè)方法
(2) URI
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
scheme:URI的模式,如http橘券、file额湘、content卿吐。沒(méi)有指定,則URI無(wú)效锋华。
host:URI的主機(jī)名嗡官。沒(méi)有指定,則URI無(wú)效供置。
port:URI中的端口號(hào)谨湘,可以沒(méi)有。
path:表示完整的路徑信息芥丧。
pathPattern:表示完整的路徑信息紧阔,建議包含通配符“*”(表示0個(gè)或多個(gè)任意字符)。
pathPrefix:表示路徑的前綴信息续担。
<data android:scheme="file" android:host="www.baidu.com"/>
等價(jià)于
<data android:scheme="file"/>
<data android:host="www.baidu.com"/>
2.2 Service
2.2.1 基礎(chǔ)知識(shí)
1. 定義
服務(wù)擅耽,屬于Android中的計(jì)算型組件
2. 作用
提供需要在后臺(tái)長(zhǎng)期運(yùn)行的服務(wù)(如復(fù)雜計(jì)算、下載等等)
3. 特點(diǎn)
- 長(zhǎng)生命周期的物遇、沒(méi)有用戶(hù)界面乖仇、在后臺(tái)運(yùn)行、用戶(hù)不手動(dòng)關(guān)閉不會(huì)停止
- 從Context派生出來(lái)的询兴,也有g(shù)etResources()乃沙,getContentResolver()等方法
2.2.2 相關(guān)方法
1. Context相關(guān)方法
-
startService(Intent intent) ComponentName
啟動(dòng)一個(gè)Service,訪(fǎng)問(wèn)者和Service之間沒(méi)有關(guān)聯(lián)诗舰,==即使訪(fǎng)問(wèn)者退出了警儒,Service仍然運(yùn)行== -
stopService (Intent intent) boolean
之后會(huì)自動(dòng)調(diào)用內(nèi)部方法:onDestory() -
bindService(Intent service, ServiceConnection conn, int flags) boolean
訪(fǎng)問(wèn)者與Service綁在了一起,==訪(fǎng)問(wèn)者一旦全部退出眶根,Service也將終止==蜀铲。conn:該參數(shù)是一個(gè)ServiceConnection(I)對(duì)象,用于==監(jiān)聽(tīng)訪(fǎng)問(wèn)者和Service之間的連接情況==属百,若連接成功记劝,將回調(diào)ServiceConnection#onServiceConnected(ComponentName name, IBinder service)
方法,當(dāng)異常終止連接時(shí)族扰,回調(diào)ServiceConnection#onServiceDisconnected(ComponentName name)
方法(若訪(fǎng)問(wèn)者主動(dòng)調(diào)用unbindService(ServiceConnection conn)
斷開(kāi)連接厌丑,不會(huì)回調(diào)此方法),flags:指定綁定時(shí)若Service未創(chuàng)建是否自動(dòng)創(chuàng)建渔呵,值:0或BIND_AUTO_CREATE
unbindService(ServiceConnection conn)
2. 生命周期方法
-
onBind(Intent intent) IBinder
應(yīng)用程序可以通過(guò)IBinder與Service組件進(jìn)行通信怒竿,綁定該Service時(shí)回調(diào)該方法。
在綁定本地Service的情況下厘肮,onBind(Intent service)方法返回的IBinder對(duì)象會(huì)傳給ServiceConnection#onServiceConnected(ComponentName name, IBinder service)的service參數(shù)愧口,IBinder相當(dāng)于一個(gè)代理人的角色睦番,實(shí)現(xiàn)互相通信类茂,所以onBind方法應(yīng)該返回一個(gè)繼承Binder類(lèi)的對(duì)象耍属,可以操作Service中的內(nèi)容,在onServiceConnected方法就可以使用該代理人巩检。
-
onUnbind(Intent intent) boolean
當(dāng)綁定在該Service上的所有客戶(hù)端都斷開(kāi)連接時(shí)回調(diào)該方法 -
onStartCommand(Intent intent, int flags, int startId) int
調(diào)用startService(Intent)方法啟動(dòng)Service時(shí)回調(diào)該方法厚骗,==會(huì)被調(diào)用多次== -
onCreate()
第一次被創(chuàng)建時(shí)回調(diào)該方法,僅被調(diào)用一次 onDestroy()
stopSelf()
3. onStartCommand(Intent intent, int flags, int startId)
(1) 返回值
有三種可選值兢哭。
-
START_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后领舰,一段時(shí)間后內(nèi)存再次空閑時(shí),系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service迟螺,一旦創(chuàng)建成功后將回調(diào)onStartCommand方法冲秽,但其中的Intent將是null,除非有掛起的Intent矩父,如pendingintent锉桑。這個(gè)狀態(tài)下比較適用于不執(zhí)行命令、但無(wú)限期運(yùn)行并等待作業(yè)的媒體播放器或類(lèi)似服務(wù)窍株。 -
START_NOT_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后民轴,即使系統(tǒng)內(nèi)存再次空閑時(shí),系統(tǒng)也不會(huì)嘗試重新創(chuàng)建此Service球订。除非程序中再次調(diào)用startService啟動(dòng)此Service后裸,這是最安全的選項(xiàng),可以避免在不必要時(shí)以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)冒滩。 -
START_REDELIVER_INTENT
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后微驶,一段時(shí)間后內(nèi)存再次空閑時(shí),系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service旦部,并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用 onStartCommand()祈搜,任何掛起 Intent均依次傳遞。與START_STICKY不同的是士八,其中的傳遞的Intent將是非空容燕,是最后一次調(diào)用startService中的intent。這個(gè)值適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)婚度。
(2) 方法入?yún)?/h5>
- intent :?jiǎn)?dòng)Service組件傳遞過(guò)來(lái)的Intent蘸秘。
- flags:表示啟動(dòng)請(qǐng)求時(shí)是否有額外數(shù)據(jù)』茸拢可選值有0醋虏,START_FLAG_REDELIVERY,START_FLAG_RETRY哮翘。0代表沒(méi)有颈嚼,它們具體含義如下:
a)START_FLAG_REDELIVERY
這個(gè)值代表返回值為START_REDELIVER_INTENT,而且在上一次服務(wù)被殺死前會(huì)去調(diào)用stopSelf()
方法停止服務(wù)饭寺。
b)START_FLAG_RETRY
該flag代表當(dāng)onStartCommand調(diào)用后一直沒(méi)有返回值時(shí)阻课,會(huì)嘗試重新去調(diào)用onStartCommand()叫挟。
- startId : 指明當(dāng)前服務(wù)的唯一ID,與
stopSelfResult(int startId)
配合使用限煞,stopSelfResult 可以更安全地根據(jù)ID停止服務(wù)抹恳。
4. Service生命周期
(1) 單獨(dú)調(diào)用
- startService()->onCreate()->onStartCommand()->onStop()->onDestory()
- bindService()->onCreate()->onBind()->onUnbind()->onDestory()
(2) 混合調(diào)用
- onCreate()->onStartCommand()->onBind()->onUnbind()->onRebind()
說(shuō)明:混合調(diào)用時(shí),要兩兩對(duì)應(yīng)署驻,不要相互嵌套(類(lèi)似于html標(biāo)簽)
服務(wù)只能解綁一次奋献,多次會(huì)報(bào)錯(cuò)
建議在Activity的onDestroy方法中解綁掉服務(wù)
startService用于保證服務(wù)的后臺(tái)運(yùn)行,bindService用于調(diào)用服務(wù)的方法
2.2.3 Service分類(lèi)
1. 本地Service
a)START_FLAG_REDELIVERY
這個(gè)值代表返回值為START_REDELIVER_INTENT,而且在上一次服務(wù)被殺死前會(huì)去調(diào)用
stopSelf()
方法停止服務(wù)饭寺。b)START_FLAG_RETRY
該flag代表當(dāng)onStartCommand調(diào)用后一直沒(méi)有返回值時(shí)阻课,會(huì)嘗試重新去調(diào)用onStartCommand()叫挟。
stopSelfResult(int startId)
配合使用限煞,stopSelfResult 可以更安全地根據(jù)ID停止服務(wù)抹恳。說(shuō)明:混合調(diào)用時(shí),要兩兩對(duì)應(yīng)署驻,不要相互嵌套(類(lèi)似于html標(biāo)簽)
服務(wù)只能解綁一次奋献,多次會(huì)報(bào)錯(cuò)
建議在Activity的onDestroy方法中解綁掉服務(wù)
startService用于保證服務(wù)的后臺(tái)運(yùn)行,bindService用于調(diào)用服務(wù)的方法
這是最普通旺上、最常用的后臺(tái)服務(wù)Service瓶蚂。
(1) 使用步驟
步驟1:新建子類(lèi)繼承Service類(lèi)
需重寫(xiě)父類(lèi)的onCreate()、onStartCommand()宣吱、onDestroy()和onBind()方法
步驟2:構(gòu)建用于啟動(dòng)Service的Intent對(duì)象
步驟3:調(diào)用startService()啟動(dòng)Service扬跋、調(diào)用stopService()停止服務(wù)
步驟4:在AndroidManifest.xml里注冊(cè)Service
屬性說(shuō)明:
-
android:name
Service的類(lèi)名 -
android:label
Service的名字,若不設(shè)置,默認(rèn)為Service類(lèi)名 -
android:icon
Service的圖標(biāo) -
android:permission
聲明此Service的權(quán)限,提供了該權(quán)限的應(yīng)用才能控制或連接此服務(wù) -
android:process
表示該服務(wù)是否在另一個(gè)進(jìn)程中運(yùn)行(遠(yuǎn)程服務(wù)) 不設(shè)置默認(rèn)為本地服務(wù)凌节;remote則設(shè)置成遠(yuǎn)程服務(wù) -
android:enabled
是否可用即是否可以被系統(tǒng)實(shí)例化 -
android:exported
是否能被其他應(yīng)用隱式調(diào)用钦听。 默認(rèn)值是由service中有無(wú)intent-filter決定的,如果有intent-filter倍奢,默認(rèn)值為true朴上,否則為false。為false的情況下卒煞,即使有intent-filter匹配痪宰,也無(wú)法打開(kāi),即無(wú)法被其他應(yīng)用隱式調(diào)用
2. 可通信Service
實(shí)例Demo
步驟1:在新建子類(lèi)繼承Service類(lèi)畔裕,并新建一個(gè)子類(lèi)繼承自Binder類(lèi)衣撬、寫(xiě)入與Activity關(guān)聯(lián)需要的方法、創(chuàng)建實(shí)例
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
System.out.println("執(zhí)行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("執(zhí)行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("執(zhí)行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("執(zhí)行了onBind()");
//返回實(shí)例
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("執(zhí)行了onUnbind()");
return super.onUnbind(intent);
}
//新建一個(gè)子類(lèi)繼承自Binder類(lèi)
class MyBinder extends Binder {
public void service_connect_Activity() {
System.out.println("Service關(guān)聯(lián)了Activity,并在Activity執(zhí)行了Service的方法");
}
}
}
步驟2:在Activity通過(guò)調(diào)用MyBinder類(lèi)中的public方法來(lái)實(shí)現(xiàn)Activity與Service的聯(lián)系扮饶,即實(shí)現(xiàn)了Activity指揮Service干什么Service就去干什么的功能
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button startService;
private Button stopService;
private Button bindService;
private Button unbindService;
private MyService.MyBinder myBinder;
//創(chuàng)建ServiceConnection的匿名類(lèi)
private ServiceConnection connection = new ServiceConnection() {
//重寫(xiě)onServiceConnected()方法和onServiceDisconnected()方法
//在Activity與Service建立關(guān)聯(lián)和解除關(guān)聯(lián)的時(shí)候調(diào)用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity與Service解除關(guān)聯(lián)的時(shí)候調(diào)用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//實(shí)例化Service的內(nèi)部類(lèi)myBinder
//通過(guò)向下轉(zhuǎn)型得到了MyBinder的實(shí)例
myBinder = (MyService.MyBinder) service;
//在Activity調(diào)用Service類(lèi)的方法
myBinder.service_connect_Activity();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.startService);
stopService = (Button) findViewById(R.id.stopService);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService = (Button) findViewById(R.id.bindService);
unbindService = (Button) findViewById(R.id.unbindService);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//點(diǎn)擊啟動(dòng)Service
case R.id.startService:
//構(gòu)建啟動(dòng)服務(wù)的Intent對(duì)象
Intent startIntent = new Intent(this, MyService.class);
//調(diào)用startService()方法-傳入Intent對(duì)象,以此啟動(dòng)服務(wù)
startService(startIntent);
break;
//點(diǎn)擊停止Service
case R.id.stopService:
//構(gòu)建停止服務(wù)的Intent對(duì)象
Intent stopIntent = new Intent(this, MyService.class);
//調(diào)用stopService()方法-傳入Intent對(duì)象,以此停止服務(wù)
stopService(stopIntent);
break;
//點(diǎn)擊綁定Service
case R.id.bindService:
//構(gòu)建綁定服務(wù)的Intent對(duì)象
Intent bindIntent = new Intent(this, MyService.class);
//調(diào)用bindService()方法,以此停止服務(wù)
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//參數(shù)說(shuō)明
//第一個(gè)參數(shù):Intent對(duì)象
//第二個(gè)參數(shù):上面創(chuàng)建的Serviceconnection實(shí)例
//第三個(gè)參數(shù):標(biāo)志位
//這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關(guān)聯(lián)后自動(dòng)創(chuàng)建Service
//這會(huì)使得MyService中的onCreate()方法得到執(zhí)行具练,但onStartCommand()方法不會(huì)執(zhí)行
break;
//點(diǎn)擊解綁Service
case R.id.unbindService:
//調(diào)用unbindService()解綁服務(wù)
//參數(shù)是上面創(chuàng)建的Serviceconnection實(shí)例
unbindService(connection);
break;
default:
break;
}
}
}
3. 前臺(tái)Service
前臺(tái)Service和后臺(tái)Service(普通)最大的區(qū)別就在于:
-
前臺(tái)Service在下拉通知欄有顯示通知(如下圖),但后臺(tái)Service沒(méi)有甜无;
前臺(tái)Service優(yōu)先級(jí)較高扛点,不會(huì)由于系統(tǒng)內(nèi)存不足而被回收;后臺(tái)Service優(yōu)先級(jí)較低岂丘,當(dāng)系統(tǒng)出現(xiàn)內(nèi)存不足情況時(shí)陵究,很有可能會(huì)被回收
//用法很簡(jiǎn)單,只需要在原有的Service類(lèi)對(duì)onCreate()方法進(jìn)行稍微修改即可
@Override
public void onCreate() {
super.onCreate();
System.out.println("執(zhí)行了onCreat()");
//添加下列代碼將后臺(tái)Service變成前臺(tái)Service
//構(gòu)建"點(diǎn)擊通知后打開(kāi)MainActivity"的Intent對(duì)象
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
//新建Builer對(duì)象
Notification.Builder builer = new Notification.Builder(this);
builer.setContentTitle("前臺(tái)服務(wù)通知的標(biāo)題");//設(shè)置通知的標(biāo)題
builer.setContentText("前臺(tái)服務(wù)通知的內(nèi)容");//設(shè)置通知的內(nèi)容
builer.setSmallIcon(R.mipmap.ic_launcher);//設(shè)置通知的圖標(biāo)
builer.setContentIntent(pendingIntent);//設(shè)置點(diǎn)擊通知后的操作
Notification notification = builer.getNotification();//將Builder對(duì)象轉(zhuǎn)變成普通的notification
startForeground(1, notification);//讓Service變成前臺(tái)Service,并在系統(tǒng)的狀態(tài)欄顯示出來(lái)
}
4. 使用場(chǎng)景
2.2.4 其他
1. Service和Thread的區(qū)別
Service和Thread之間沒(méi)有任何關(guān)系奥帘,之所以有不少人會(huì)把它們聯(lián)系起來(lái)铜邮,主要因?yàn)镾ervice的后臺(tái)概念
后臺(tái)的定義:后臺(tái)任務(wù)運(yùn)行完全不依賴(lài)UI,即使Activity被銷(xiāo)毀,或者程序被關(guān)閉松蒜,只要進(jìn)程還在返咱,后臺(tái)任務(wù)就可以繼續(xù)運(yùn)行
一般來(lái)說(shuō),會(huì)將Service和Thread聯(lián)合著用牍鞠,即在Service中再創(chuàng)建一個(gè)子線(xiàn)程(工作線(xiàn)程)去處理耗時(shí)操作邏輯,如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//新建工作線(xiàn)程
new Thread(new Runnable() {
@Override
public void run() {
// 開(kāi)始執(zhí)行后臺(tái)任務(wù)
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void service_connect_Activity() {
//新建工作線(xiàn)程
new Thread(new Runnable() {
@Override
public void run() {
// 執(zhí)行具體的下載任務(wù)
}
}).start();
}
}
2. IntentService
(1) 定義
Android里的一個(gè)封裝類(lèi)评姨,繼承四大組件之一的Service
(2) 作用
處理異步請(qǐng)求 & 實(shí)現(xiàn)多線(xiàn)程
(3) 使用場(chǎng)景
線(xiàn)程任務(wù)需按順序在后臺(tái)執(zhí)行
- 最常見(jiàn)的場(chǎng)景:離線(xiàn)下載
- 不符合多個(gè)數(shù)據(jù)同時(shí)請(qǐng)求的場(chǎng)景:所有的任務(wù)都在同一個(gè)Thread looper里執(zhí)行
(4) 使用步驟
步驟1:定義 IntentService的子類(lèi)
需傳入線(xiàn)程名稱(chēng)难述、復(fù)寫(xiě)onHandleIntent()方法
public class myIntentService extends IntentService {
/**
* 在構(gòu)造函數(shù)中傳入線(xiàn)程名字
**/
public myIntentService() {
// 調(diào)用父類(lèi)的構(gòu)造函數(shù)
// 參數(shù) = 工作線(xiàn)程的名字
super("myIntentService");
}
/**
* 復(fù)寫(xiě)onHandleIntent()方法
* 根據(jù) Intent實(shí)現(xiàn) 耗時(shí)任務(wù) 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根據(jù) Intent的不同,進(jìn)行不同的事務(wù)處理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 復(fù)寫(xiě)onStartCommand()方法
* 默認(rèn)實(shí)現(xiàn) = 將請(qǐng)求的Intent添加到工作隊(duì)列里
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步驟2:在Manifest.xml中注冊(cè)服務(wù)
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步驟3:在Activity中開(kāi)啟Service服務(wù)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服務(wù)只會(huì)開(kāi)啟1個(gè)工作線(xiàn)程
// 在onHandleIntent()函數(shù)里吐句,依次處理傳入的Intent請(qǐng)求
// 將請(qǐng)求通過(guò)Bundle對(duì)象傳入到Intent胁后,再傳入到服務(wù)里
// 請(qǐng)求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 請(qǐng)求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次啟動(dòng)
}
}
測(cè)試結(jié)果
3. 進(jìn)程優(yōu)先級(jí)
- 前臺(tái)進(jìn)程:某個(gè)Activity可見(jiàn),獲得焦點(diǎn)
- 可見(jiàn)進(jìn)程:某個(gè)Activity可見(jiàn)嗦枢,但是沒(méi)有焦點(diǎn)
- 服務(wù)進(jìn)程:有一個(gè)服務(wù)在后臺(tái)運(yùn)行
- 后臺(tái)進(jìn)程:沒(méi)有任何服務(wù)攀芯,打開(kāi)一個(gè)Activity然后最小化(容易被回收)
- 空進(jìn)程:沒(méi)有任何活動(dòng)組件存在的進(jìn)程(容易被回收)
4. AIDL
(1) 簡(jiǎn)介
為了實(shí)現(xiàn)跨進(jìn)程通信(IPC),實(shí)現(xiàn)進(jìn)程之間的數(shù)據(jù)交換
(2) 步驟
①:服務(wù)端創(chuàng)建.aidl文件
②:服務(wù)端創(chuàng)建Service文虏,并在onBind中返回一個(gè)IBinder(接口對(duì)象(代理人))
IBinder binder = new IMyAidlInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
return num1 + num2;
}
};
③:客戶(hù)端創(chuàng)建(復(fù)制)和服務(wù)端一樣的.aidl文件(包名也必須一致)
④:客戶(hù)端創(chuàng)建ServiceConnection的子類(lèi)侣诺,并實(shí)現(xiàn)其onServiceConnected(ComponentName name, IBinder service),在方法中(service就是中間人)iMyAdil = IMyAidlInterface.Stub.asInterface(service)
氧秘,客戶(hù)端可以使用服務(wù)端的方法了
⑤:客戶(hù)端bindService
(3) AIDL支持的數(shù)據(jù)類(lèi)型(支持其實(shí)就是定義aidl的時(shí)候年鸳,參數(shù)可以使用的類(lèi)型)
基本數(shù)據(jù)類(lèi)型(除short),String丸相,CharSequence搔确,List(僅支持ArrayList),Map(僅支持HashMap)灭忠,Parcelable
注意:
①:非基本數(shù)據(jù)類(lèi)型膳算,需要用in,out,inout指定數(shù)據(jù)的走向
②:復(fù)雜類(lèi)型(如Book)必須實(shí)現(xiàn)Parcelable,且需要Book.aidl(內(nèi)容parcelable Book;)—— 包名必須和Book.java相同弛作,無(wú)論是否相同的包涕蜂,都需要導(dǎo)入包
③:AIDL接口中只支持方法,不支持聲明靜態(tài)變量
2.3 IPC與多進(jìn)程
2.3.1 IPC簡(jiǎn)介
線(xiàn)程:① CPU最小的調(diào)度單元 ② 一種有限的系統(tǒng)資源
進(jìn)程:一個(gè)執(zhí)行單元映琳。一般指一個(gè)程序或一個(gè)應(yīng)用宇葱。一個(gè)進(jìn)程可以包含多個(gè)線(xiàn)程。
IPC:進(jìn)程間通信刊头。
多線(xiàn)程的情況
1)因?yàn)槟承┰蜃陨硇枰捎枚噙M(jìn)程模式來(lái)實(shí)現(xiàn)疹尾。比如:某些模塊需要運(yùn)行在單獨(dú)進(jìn)程;為了加大一個(gè)應(yīng)用可以使用的內(nèi)存劳翰。
2)需要從其他應(yīng)用獲取數(shù)據(jù)请祖。
2.3.2 Android中的多進(jìn)程模式
1. 開(kāi)啟多進(jìn)程模式
在Android中一個(gè)應(yīng)用開(kāi)啟多進(jìn)程唯一辦法:給四大組件在AndroidMenifest.xml中指定android:process
屬性。
<service
android:name=".MyService"
android:process=":remote" />
<service
android:name=".SecondService"
android:process="com.example.xiang.myapplication.remote" />
默認(rèn)進(jìn)程名是包名穿肄∧昃郑“:remote”是一種省略寫(xiě)法际看,完整名為“com.example.xiang.myapplication:remote”進(jìn)程名,以“:”開(kāi)頭矢否,屬于當(dāng)前應(yīng)用的私有進(jìn)程仲闽,其他應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程中。
2. 多進(jìn)程模式的運(yùn)行機(jī)制
Android系統(tǒng)為==每一個(gè)進(jìn)程分配了一個(gè)獨(dú)立的虛擬機(jī)==僵朗,不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間赖欣。將導(dǎo)致存在如下問(wèn)題:
1)靜態(tài)成員和單例完全失效
2)線(xiàn)程同步機(jī)制完全失效
3)SharedPreferences的可靠性下降
4)Application會(huì)多次創(chuàng)建
不同的進(jìn)程擁有獨(dú)立的虛擬機(jī)、Application和內(nèi)存空間验庙,導(dǎo)致通過(guò)內(nèi)存來(lái)共享數(shù)據(jù)顶吮,都會(huì)共享失敗。
Android的IPC方式
1)Intent
2)文件共享方式
3)Binder(AIDL和Messenger)
4)ContentProvider
5)Socket
2.3.3 IPC的基礎(chǔ)概念
為什么要序列化粪薛?
1)永久性保存對(duì)象的字節(jié)序列到本地文件中
2)通過(guò)序列化在網(wǎng)絡(luò)中傳遞對(duì)象
3)通過(guò)序列化在進(jìn)程間傳遞對(duì)象
1. Serializable接口
Serializable是Java提供的一個(gè)序列化接口悴了,是一個(gè)空接口,為對(duì)象提供標(biāo)準(zhǔn)的序列化和反序列化操作违寿。
private static final long serialVersionUID = 8154678445665565611L;
serialVersionUID是用來(lái)輔助序列化和序列化的過(guò)程湃交,原則上==序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類(lèi)的serialVersionUID相同才能夠正常地被反序列化==。一般我們應(yīng)該手動(dòng)指定serialVersionUID的值藤巢,比如1L(或者根據(jù)類(lèi)結(jié)構(gòu)生成hash值)巡揍。若不指定,反序列化時(shí)當(dāng)前類(lèi)有所改變(比如增加或者刪除了成員變量)菌瘪,那么系統(tǒng)會(huì)==根據(jù)類(lèi)結(jié)構(gòu)重新計(jì)算當(dāng)前類(lèi)的hash值==并賦給serialVersionUID腮敌,導(dǎo)致serialVersionUID不一致,于是反序列化失敗俏扩,程序就會(huì)crash糜工。若手動(dòng)指定了,可以在很大程度上避免序列化失敗录淡。比如當(dāng)版本升級(jí)后捌木,可能刪除/增加了某個(gè)成員變量,程序仍然能夠最大限度的恢復(fù)數(shù)據(jù)嫉戚。(若類(lèi)結(jié)構(gòu)發(fā)生了非常規(guī)改變刨裆,比如改了類(lèi)名,修改了成員變量的類(lèi)型彬檀,還是會(huì)反序列化失敺小)
靜態(tài)成員變量屬于類(lèi)不屬于對(duì)象,不會(huì)參加序列化
用transient標(biāo)記的成員變量不會(huì)參與序列化
2. Parcelable接口
主要基于Parcel實(shí)現(xiàn)窍帝。
Serializable接口是JAVA中的序列化接口努潘,==使用簡(jiǎn)單開(kāi)銷(xiāo)大==(序列化和反序列化過(guò)程中包含大量的IO操作)。Parcelable接口是Android的序列化方式,==使用麻煩效率高==疯坤。
1)在使用內(nèi)存的時(shí)候报慕,Parcelable比Serializable性能高(Serializable在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的GC)压怠,所以推薦使用Parcelable
2)Parcelable不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤(pán)上的情況眠冈,因?yàn)樵谕饨缬凶兓那闆r下,Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性菌瘫。==將對(duì)象序列化到存儲(chǔ)設(shè)備==或==將對(duì)象序列化后通過(guò)網(wǎng)絡(luò)傳輸==蜗顽,建議使用Serializable接口。
3. Binder
Binder是什么突梦?
a) 實(shí)現(xiàn)了IBinder接口的一個(gè)類(lèi)。
b) 跨進(jìn)程通信的==媒介==羽利。
c) ServiceManager連接==各種Manager和相應(yīng)的ManagerService的橋梁==宫患。
(1) AIDL接口的創(chuàng)建
在Android開(kāi)發(fā)中,Binder主要用在Service中这弧,包括Messenger(底層其實(shí)是AIDL)和AIDL娃闲。
//Book.java
package com.example.xiang.myapplication;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
/**
* 幾乎所有情況都返回0
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 實(shí)現(xiàn)序列化
* @param parcel
* @param i
*/
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
/**
* 實(shí)現(xiàn)反序列化
*/
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
}
//Book.aidl
package com.example.xiang.myapplication;
parcelable Book;
// IBookManager.aidl
package com.example.xiang.myapplication;
import com.example.xiang.myapplication.Book;//必須導(dǎo)入,否則報(bào)錯(cuò)
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Book.aidl是Book類(lèi)在AIDL中的聲明匾浪。IBookManager.aidl是定義的一個(gè)接口皇帮,雖然Book類(lèi)和IBookManager在同一個(gè)包中,但是還是要顯示導(dǎo)入Book類(lèi)蛋辈。目錄結(jié)構(gòu)如下:
(新建Book.aidl時(shí)候属拾,直接填Book為名字時(shí)候會(huì)報(bào)錯(cuò),只有先創(chuàng)建完之后再RENAME才不會(huì)報(bào)錯(cuò))
(2) Binder類(lèi)分析
系統(tǒng)為IBookManager.aidl自動(dòng)生成的Binder類(lèi)
package com.example.xiang.myapplication;
public interface IBookManager extends android.os.IInterface {
public static abstract class Stub extends Binder implements IBookManager {
//Binder的唯一標(biāo)識(shí)
private static final String DESCRIPTOR = "com.example.xiang.myapplication.IBookManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data,
Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
List<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
Book _arg0;
if ((0 != data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
List<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//_data寫(xiě)入?yún)?shù)
//發(fā)起遠(yuǎn)程調(diào)用冷溶,當(dāng)前線(xiàn)程掛起
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);
}
public List<Book> getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
Binder類(lèi)中比較重要的方法有2個(gè):==transact
方法和onTransact
方法==
-
asInterface
//將服務(wù)端的Binder對(duì)象轉(zhuǎn)換為客戶(hù)端所需的AIDL接口類(lèi)型的對(duì)象渐白,如果C/S位于同一進(jìn)程,此方法返回就是服務(wù)端的==Stub對(duì)象==本身逞频,否則返回的就是系統(tǒng)封裝后的==Stub.Proxy對(duì)象== -
onTransact
//運(yùn)行在服務(wù)端 -
Proxy#getBookList
//運(yùn)行在客戶(hù)端纯衍,內(nèi)部實(shí)現(xiàn)過(guò)程如下:首先創(chuàng)建該方法所需要的輸入型對(duì)象Parcel對(duì)象_data,輸出型Parcel對(duì)象_reply和返回值對(duì)象List苗胀。然后把該方法的參數(shù)信息寫(xiě)入_data(如果有參數(shù))襟诸;接著調(diào)用transact方法發(fā)起RPC( 遠(yuǎn)程過(guò)程調(diào)用),同時(shí)當(dāng)前線(xiàn)程掛起(因此不能在UI線(xiàn)程中發(fā)起遠(yuǎn)程請(qǐng)求)基协;然后服務(wù)端的onTransact方法會(huì)被調(diào)用(服務(wù)端的Binder方法==運(yùn)行在線(xiàn)程池==歌亲,所以需要采用同步方式實(shí)現(xiàn)),直到RPC過(guò)程返回后澜驮,當(dāng)前線(xiàn)程繼續(xù)執(zhí)行应结,并從_reply中取出RPC過(guò)程的返回結(jié)果,最后返回_reply中的數(shù)據(jù)。
AIDL的本質(zhì):==系統(tǒng)提供的一個(gè)快速實(shí)現(xiàn)Binder的工具而已==鹅龄。
(3) 遠(yuǎn)程服務(wù)端Service的實(shí)現(xiàn)
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
//CopyOnWriteArrayList支持并發(fā)讀/寫(xiě)
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android開(kāi)發(fā)藝術(shù)探索"));
mBookList.add(new Book(2, "Android進(jìn)階之光"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
//注冊(cè)Service
<service
android:name="com.example.service.MessengerService"
android:process=":remote" />
AIDL方法(getBookList和addBook)是運(yùn)行在Binder線(xiàn)程池中的揩慕,所以需要處理線(xiàn)程同步,這里采用CopyOnWriteArrayList來(lái)進(jìn)行自動(dòng)的線(xiàn)程同步扮休。
(4) 客戶(hù)端的實(shí)現(xiàn)
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
Log.d(TAG, "查詢(xún)圖書(shū)列表:" + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
服務(wù)端的方法可能需要很久才能執(zhí)行完畢迎卤,上面這樣寫(xiě)的目的是為了更好的了解AIDL的實(shí)現(xiàn)步驟。
對(duì)象是不能跨進(jìn)程直接傳輸?shù)溺枳梗瑢?duì)象跨進(jìn)程傳輸?shù)谋举|(zhì)是反序列化過(guò)程蜗搔,這也是為什么AIDL中定義的對(duì)象必須實(shí)現(xiàn)Parcelable接口。
4. Binder的兩個(gè)重要方法
linkToDeath(DeathRecipient recipient, int flags)
//設(shè)置Binder的死亡代理八堡。當(dāng)Binder死亡時(shí)樟凄,系統(tǒng)會(huì)回調(diào)DeathRecipient的binderDied()
方法,所以需要在此方法中移除之前綁定的binder代理(調(diào)用unlinkToDeath
)并重新綁定遠(yuǎn)程服務(wù)
unlinkToDeath(DeathRecipient recipient, int flags)
//移除死亡代理
isBinderAlive()
//判斷Binder是否死亡
DeathRecipient是IBinder的一個(gè)內(nèi)部接口兄渺,里面只有一個(gè)方法binderDied()
5. 補(bǔ)充說(shuō)明
- 自定義的Parcelable對(duì)象(如上例中的Book類(lèi))缝龄,必須新建一個(gè)和它同名的AIDL文件(Book.aidl),并添加相應(yīng)的內(nèi)容挂谍。
- 自定義Parcelable對(duì)象和AIDL對(duì)象必須要顯式import進(jìn)來(lái)叔壤。
- 除了基本數(shù)據(jù)類(lèi)型的其他類(lèi)型參數(shù),都需要標(biāo)上方向:in口叙、out或者inout炼绘。
- AIDL接口中只支持方法,不支持聲明靜態(tài)常量妄田。
- 建議把所有和AIDL相關(guān)的類(lèi)和文件全部放在同一個(gè)包中俺亮,好處是,若客戶(hù)端在另一應(yīng)用(模塊)疟呐,復(fù)制整個(gè)包即可铅辞。
- AIDL的包結(jié)構(gòu)在服務(wù)端和客戶(hù)端必須保持一致,否則運(yùn)行出錯(cuò)萨醒。
AIDL文件支持的數(shù)據(jù)類(lèi)型
1)基本數(shù)據(jù)類(lèi)型(int斟珊、long、char富纸、boolean等)
2)String和CharSequence
3)List:只支持ArrayList囤踩,里面的每個(gè)元素必須被AIDL所支持
4)Map:只支持HashMap,里面的每個(gè)元素必須被AIDL所支持晓褪,包括key和value
5)Parcelable:所有實(shí)現(xiàn)了Parcelable接口的對(duì)象
6)AIDL:所有AIDL接口本身也可以在AIDL文件中使用
2.4 BroadcastReceiver
2.4.1 簡(jiǎn)介
1. 定義
系統(tǒng)級(jí)的監(jiān)聽(tīng)器堵漱,默認(rèn)運(yùn)行在本進(jìn)程的主線(xiàn)程
2. 作用
用于監(jiān)聽(tīng)?wèi)?yīng)用發(fā)出的廣播消息,并做出響應(yīng)
最常見(jiàn)的應(yīng)用場(chǎng)景
a. 不同組件之間通信(包括應(yīng)用內(nèi) / 不同應(yīng)用之間)
b. Android系統(tǒng)在特定情況下與App之間的消息通信(如當(dāng)電話(huà)呼入時(shí)涣仿、網(wǎng)絡(luò)可用時(shí))
c. 多線(xiàn)程通信
3. 實(shí)現(xiàn)原理
1)自定義廣播接收者BroadcastReceiver勤庐,并復(fù)寫(xiě)onRecvice()方法示惊;
2)通過(guò)Binder機(jī)制向AMS(Activity Manager Service)進(jìn)行注冊(cè);
3)廣播發(fā)送者通過(guò)Binder機(jī)制向AMS發(fā)送廣播愉镰;
4)AMS查找符合相應(yīng)條件(IntentFilter/Permission等)的BroadcastReceiver米罚,將廣播發(fā)送到BroadcastReceiver(一般情況下是Activity)相應(yīng)的消息循環(huán)隊(duì)列中;
5)消息循環(huán)執(zhí)行拿到此廣播丈探,回調(diào)BroadcastReceiver中的onReceive()方法录择。
2.4.2 使用流程
1. 自定義BroadcastReceiver
新建一個(gè)BroadcastReceiver子類(lèi),并重寫(xiě)onReceive(Context context, Intent intent)
方法
注意
a)廣播接收器運(yùn)行在UI線(xiàn)程碗降,因此隘竭,onReceive方法不能執(zhí)行耗時(shí)操作,否則將導(dǎo)致ANR
b)執(zhí)行耗時(shí)操作讼渊,考慮通過(guò)Intent啟動(dòng)一個(gè)Service(不應(yīng)考慮啟動(dòng)新的進(jìn)程动看,原因:BroadcastReceiver生命周期短,子線(xiàn)程沒(méi)有結(jié)束爪幻,BroadcastReceiver進(jìn)程結(jié)束了菱皆,由于新進(jìn)程沒(méi)有任何活動(dòng)的組件,當(dāng)內(nèi)存緊張系統(tǒng)優(yōu)先結(jié)束該進(jìn)程笔咽,導(dǎo)致不能執(zhí)行完成)
c)監(jiān)聽(tīng)到就會(huì)創(chuàng)建實(shí)例搔预,觸發(fā)onReceive()方法霹期,執(zhí)行完該方法叶组,銷(xiāo)毀實(shí)例
d)系統(tǒng)通過(guò)Intent激發(fā)BrocastReceiver組件時(shí),找不到也不會(huì)報(bào)錯(cuò)(區(qū)別Activity)
2. 配置
注冊(cè)的方式分為兩種:靜態(tài)注冊(cè)历造、動(dòng)態(tài)注冊(cè)
(1) 靜態(tài)注冊(cè)
在AndroidManifest.xml里通過(guò)標(biāo)簽聲明甩十,當(dāng)此App首次啟動(dòng)時(shí),系統(tǒng)會(huì)自動(dòng)實(shí)例化mBroadcastReceiver類(lèi)吭产,并注冊(cè)到系統(tǒng)中侣监。
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的發(fā)出的廣播
//默認(rèn)值是由receiver中有無(wú)intent-filter決定的:如果有intent-filter,默認(rèn)值為true臣淤,否則為false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//繼承BroadcastReceiver子類(lèi)的類(lèi)名
android:name=".mBroadcastReceiver"
//具有相應(yīng)權(quán)限的廣播發(fā)送者發(fā)送的廣播才能被此BroadcastReceiver所接收
android:permission="string"
//BroadcastReceiver運(yùn)行所處的進(jìn)程
//默認(rèn)為app的進(jìn)程橄霉,可以指定獨(dú)立的進(jìn)程
//注:Android四大基本組件都可以通過(guò)此屬性指定自己的獨(dú)立進(jìn)程
android:process="string" >
//用于指定此廣播接收器將接收的廣播類(lèi)型
//本示例中給出的是用于接收網(wǎng)絡(luò)狀態(tài)改變時(shí)發(fā)出的廣播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
(2) 動(dòng)態(tài)注冊(cè)
調(diào)用Context#registerReceiver(BroadcastReceiver receiver, IntentFilter filter) Intent
來(lái)注冊(cè)到系統(tǒng)中。所有匹配該Intent的BrocastReceiver都有可能被啟動(dòng)邑蒋。
注意
動(dòng)態(tài)廣播最好在Activity的onResume()注冊(cè)姓蜂、onPause()注銷(xiāo),不銷(xiāo)毀會(huì)導(dǎo)致內(nèi)存泄漏医吊。(重復(fù)注冊(cè)钱慢、重復(fù)注銷(xiāo)也不允許)。不在onCreate() & onDestory() 或 onStart() & onStop()注冊(cè)卿堂、注銷(xiāo)是因?yàn)椋?br> 當(dāng)系統(tǒng)因?yàn)閮?nèi)存不足(優(yōu)先級(jí)更高的應(yīng)用需要內(nèi)存)要回收Activity占用的資源時(shí)束莫,Activity在執(zhí)行完onPause()方法后就會(huì)被銷(xiāo)毀懒棉,有些生命周期方法onStop(),onDestory()就不會(huì)執(zhí)行览绿。當(dāng)再回到此Activity時(shí)策严,是從onCreate方法開(kāi)始執(zhí)行。
假設(shè)我們將廣播的注銷(xiāo)放在onStop()挟裂,onDestory()方法里的話(huà)享钞,有可能在Activity被銷(xiāo)毀后還未執(zhí)行onStop(),onDestory()方法诀蓉,即廣播仍還未注銷(xiāo)栗竖,從而導(dǎo)致內(nèi)存泄露。但是渠啤,onPause()一定會(huì)被執(zhí)行狐肢,從而保證了廣播在App死亡前一定會(huì)被注銷(xiāo),從而防止內(nèi)存泄露沥曹。(詳見(jiàn)Activity生命周期圖)
(3) 兩種注冊(cè)方式的區(qū)別
3. 廣播發(fā)送者向AMS發(fā)送廣播
調(diào)用Context#sendBroadcast(Intent intent)
或sendOrderedBroadcast(Intent intent, String receiverPermission)
方法來(lái)發(fā)送廣播
2.4.3 廣播的分類(lèi)
廣播的類(lèi)型主要分為5類(lèi):
- 普通廣播(Normal Broadcast)
- 有序廣播(Ordered Broadcast)
- App應(yīng)用內(nèi)廣播(Local Broadcast)
- 系統(tǒng)廣播(System Broadcast)
-
粘性廣播(Sticky Broadcast)已廢棄
1. 普通廣播
sendBroadcast(Intent intent)
發(fā)送的廣播份名。
同一時(shí)刻被所有的接收者接收到,消息傳遞的效率高妓美。
2. 有序廣播
sendOrderedBroadcast(Intent intent, String receiverPermission)
發(fā)送的廣播僵腺。
按預(yù)先聲明的優(yōu)先級(jí)依次接收廣播,接收者可以將數(shù)據(jù)傳遞給下一個(gè)接收者壶栋,也可以終止廣播的傳播(abortBroadcast()
方法)
設(shè)置優(yōu)先級(jí):
①:<inter-filter.../>元素的priority屬性辰如,范圍在-1000~1000之間
②:調(diào)用InterFilter的setPriority(int priority)
3. 應(yīng)用內(nèi)廣播
Android中的廣播可以跨App直接通信(exported對(duì)于有intent-filter情況下默認(rèn)值為true)
可能出現(xiàn)的問(wèn)題
- 其他App針對(duì)性發(fā)出與當(dāng)前App intent-filter相匹配的廣播,由此導(dǎo)致當(dāng)前App不斷接收廣播并處理贵试;
- 其他App注冊(cè)與當(dāng)前App一致的intent-filter用于接收廣播琉兜,獲取廣播具體信息。
即會(huì)出現(xiàn)安全性 & 效率性的問(wèn)題毙玻。
解決方案
使用App應(yīng)用內(nèi)廣播(Local Broadcast)
- App應(yīng)用內(nèi)廣播可理解為一種局部廣播豌蟋,廣播的發(fā)送者和接收者都同屬于一個(gè)App。
- 相比于全局廣播(普通廣播)桑滩,App應(yīng)用內(nèi)廣播優(yōu)勢(shì)體現(xiàn)在:安全性高 & 效率高
具體使用1 - 將全局廣播設(shè)置成局部廣播
- 注冊(cè)廣播時(shí)將exported屬性設(shè)置為false梧疲,使得非本App內(nèi)部發(fā)出的此廣播不被接收;
- 在廣播發(fā)送和接收時(shí)运准,增設(shè)相應(yīng)權(quán)限permission幌氮,用于權(quán)限驗(yàn)證;
- 發(fā)送廣播時(shí)指定該廣播接收器所在的包名戳吝,此廣播將只會(huì)發(fā)送到此包中的App內(nèi)與之相匹配的有效廣播接收器中浩销。
通過(guò)intent#setPackage(packageName)
指定包名
具體使用2 - 使用封裝好的LocalBroadcastManager類(lèi)
Android Support包提供了一個(gè)工具,是用來(lái)在同一個(gè)應(yīng)用內(nèi)的不同組件間發(fā)送Broadcast的听哭。
使用方式上與全局廣播幾乎相同慢洋,只是注冊(cè)/取消注冊(cè)廣播接收器和發(fā)送廣播時(shí)將參數(shù)的context變成了LocalBroadcastManager的單一實(shí)例
注:對(duì)于LocalBroadcastManager方式發(fā)送的應(yīng)用內(nèi)廣播塘雳,只能通過(guò)LocalBroadcastManager動(dòng)態(tài)注冊(cè),不能靜態(tài)注冊(cè)
//步驟1:實(shí)例化BroadcastReceiver子類(lèi) & IntentFilter mBroadcastReceiver
mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//步驟2:實(shí)例化LocalBroadcastManager的實(shí)例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//步驟3:設(shè)置接收廣播的類(lèi)型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//步驟4:調(diào)用LocalBroadcastManager單一實(shí)例的registerReceiver()方法進(jìn)行動(dòng)態(tài)注冊(cè)
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注冊(cè)應(yīng)用內(nèi)廣播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//發(fā)送應(yīng)用內(nèi)廣播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);
2.4.4 補(bǔ)充說(shuō)明
1. context返回值
對(duì)于不同注冊(cè)方式的廣播接收器回調(diào)OnReceive(Context context普筹,Intent intent)中的context返回值是不一樣的:
- 對(duì)于靜態(tài)注冊(cè)(全局+應(yīng)用內(nèi)廣播)败明,回調(diào)onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
- 對(duì)于全局廣播的動(dòng)態(tài)注冊(cè)太防,回調(diào)onReceive(context, intent)中的context返回值是:Activity Context妻顶;
- 對(duì)于應(yīng)用內(nèi)廣播的動(dòng)態(tài)注冊(cè)(LocalBroadcastManager方式),回調(diào)onReceive(context, intent)中的context返回值是:Application Context蜒车。
- 對(duì)于應(yīng)用內(nèi)廣播的動(dòng)態(tài)注冊(cè)(非LocalBroadcastManager方式)讳嘱,回調(diào)onReceive(context, intent)中的context返回值是:Activity Context;
2. 方法
-
onReceive(Context context, Intent intent)
收到廣播時(shí)回調(diào)的方法 abortBroadcast()
-
setResultExtras(Bundle extras)
將處理結(jié)果放入廣播中 -
getResultExtras(boolean makeMap) Bundle
獲取上一接收者存入的數(shù)據(jù)酿愧,boolean:true沥潭,Bundle為null,創(chuàng)建一個(gè)空的 getResultData() String
setResultData(String data)
3. 常見(jiàn)系統(tǒng)廣播
常用的系統(tǒng)廣播的Action常量(對(duì)應(yīng)類(lèi)似android.intent.action.BOOT_COMPLETED字符串)
- ACTION_BOOT_COMPLETED :系統(tǒng)啟動(dòng)完成
- ACTION_SHUTDOWN :系統(tǒng)關(guān)閉
- ACTION_POWER_CONNECTED :連接電源
- ACTION_POWER_DISCONNECTED :與電源斷開(kāi)
- ACTION_BATTERY_CHANGED :電量低
- android.provider.Telephony.SMS_REVEIVED :收到短信
2.5 ContentProvider
2.5.1 ContentProvider
ContentProvider為存儲(chǔ)和獲取數(shù)據(jù)提供統(tǒng)一的接口嬉挡,可以在不同的應(yīng)用程序之間共享數(shù)據(jù)钝鸽。如果你不需要在多個(gè)應(yīng)用之間共享數(shù)據(jù),你可以直接通過(guò)SQLite來(lái)操作數(shù)據(jù)庫(kù)庞钢。
使用ContentProvider拔恰,主要有以下幾個(gè)理由:
-
ContentProvider提供了對(duì)底層數(shù)據(jù)存儲(chǔ)方式的抽象。比如下圖中基括,底層使用了SQLite數(shù)據(jù)庫(kù)颜懊,在用了ContentProvider封裝后,即使你把數(shù)據(jù)庫(kù)換成MongoDB阱穗,也不會(huì)對(duì)上層數(shù)據(jù)使用層代碼產(chǎn)生影響饭冬。
- Android框架中的一些類(lèi)需要ContentProvider類(lèi)型數(shù)據(jù)使鹅。如果你想讓你的數(shù)據(jù)可以使用在如SyncAdapter, Loader, CursorAdapter等類(lèi)上揪阶,那么你就需要為你的數(shù)據(jù)做一層ContentProvider封裝
- 最主要的原因,是ContentProvider為應(yīng)用間的數(shù)據(jù)交互提供了一個(gè)安全的環(huán)境患朱。它準(zhǔn)許你把自己的應(yīng)用數(shù)據(jù)根據(jù)需求開(kāi)放給其他應(yīng)用進(jìn)行增鲁僚、刪、改裁厅、查冰沙,而不用擔(dān)心直接開(kāi)放數(shù)據(jù)庫(kù)權(quán)限而帶來(lái)的安全問(wèn)題。
onCreate() boolean //當(dāng)?shù)谝淮卧L(fǎng)問(wèn)ContentProvider执虹,創(chuàng)建完對(duì)象之后調(diào)用拓挥,唯一的生命周期方法
insert(Uri uri, ContentValues values) Uri //根據(jù)Uri插入values對(duì)應(yīng)的數(shù)據(jù)
delete(Uri uri, String selection, String[] selectionArgs) int //根據(jù)Uri刪除select條件所匹配的全部記錄
update(Uri uri, ContentValues values, String selection, String[] selectionArgs) int //根據(jù)Uri修改select條件所匹配的全部記錄
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) Cursor //查詢(xún),projection:選擇的指定的列
getType(Uri uri) String //返回當(dāng)前Uri所代表的MIME類(lèi)型袋励。
2.5.2 ContentResolver
Android為我們提供了ContentResolver來(lái)統(tǒng)一管理與不同ContentProvider間的操作侥啤,通過(guò)URI來(lái)區(qū)別不同的ContentProvider当叭。
ContentResolver 類(lèi)也提供了與ContentProvider類(lèi)相對(duì)應(yīng)的四個(gè)方法:
insert(Uri uri, ContentValues values) Uri
delete(Uri uri, String selection, String[] selectionArgs) int
update(Uri uri, ContentValues values, String selection, String[] selectionArgs) int
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) Cursor
ContentProvider中的URI有固定格式,如下圖:
Scheme:URI的命名空間標(biāo)識(shí)
Authority:授權(quán)信息盖灸,用以區(qū)別不同的ContentProvider
Path:表名蚁鳖,用以區(qū)分ContentProvider中不同的數(shù)據(jù)表
Id:Id號(hào),用以區(qū)別表中的不同數(shù)據(jù)
2.5.3 URI
1. URI
URI:通用資源標(biāo)志符 —— Uniform Resource Identifier
URI類(lèi):java.net.URI赁炎,是Java提供的一個(gè)類(lèi)醉箕,代表了URI的一個(gè)實(shí)例
Uri類(lèi):android.net.Uri,擴(kuò)展了JAVA中URI的一些功能來(lái)適用于Android開(kāi)發(fā)
2. URI的結(jié)構(gòu)
(1) 基本結(jié)構(gòu)
[scheme:]scheme-specific-part[#fragment]
[scheme:][//authority][path][?query][#fragment]
[scheme:][//host:port][path][?query][#fragment]
http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
在android中,除了scheme、authority是必須要有的浪腐,其他都可以不要晋被,Android中可用的每種資源( 圖像、視頻片段等)都可以用URI來(lái)表示
(2) 代碼中提取
Uri.parse(String uriString) //返回一個(gè)Uri對(duì)象
getScheme() //獲取Uri中的scheme字符串部分继蜡,在這里即,http
getSchemeSpecificPart() //獲取Uri中的scheme-specific-part:部分,這里是://www.java2s.com:8080/yourpath/fileName.htm?
getFragment() //獲取Uri中的Fragment部分剧辐,即harvic
getAuthority() //獲取Uri中Authority部分,即www.java2s.com:8080
getPath() //獲取Uri中path部分邮府,即/yourpath/fileName.htm
getQuery() //獲取Uri中的query部分荧关,即stove=10&path=32&id=4
getHost() //獲取Authority中的Host字符串,即www.java2s.com
getPost() //獲取Authority中的Port字符串褂傀,即8080
getPathSegments() List<String> //依次提取出Path的各個(gè)部分的字符串忍啤,以字符串?dāng)?shù)組的形式輸出
getQueryParameter(String key) //根據(jù)傳進(jìn)去path中某個(gè)Key的字符串,返回對(duì)應(yīng)的值
(3) 絕對(duì)URI和相對(duì)URI
絕對(duì)URI:以scheme組件起始的完整格式仙辟,如http://fsjohnhuang.cnblogs.com同波。表示以對(duì)標(biāo)識(shí)出現(xiàn)的環(huán)境無(wú)依賴(lài)的方式引用資源
相對(duì)URI:不以scheme組件起始的非完整格式,如fsjohnhuang.cnblogs.com叠国。表示以對(duì)標(biāo)識(shí)出現(xiàn)的環(huán)境有依賴(lài)的方式引用資源
(4) 不透明URI和分層URI
不透明URI:scheme-specific-part組件不是以正斜杠(/)起始的未檩,如mailto:fsjohnhuang@xxx.com
分層URI:scheme-specific-part組件是以正斜杠(/)起始的,如http://fsjohnhuang.com
(5) UriMatcher工具類(lèi)
因?yàn)閁ri代表了要操作的數(shù)據(jù)粟焊,所以我們很經(jīng)常需要解析Uri冤狡,并從Uri中獲取數(shù)據(jù)。Android系統(tǒng)提供了兩個(gè)用于操作Uri的工具類(lèi)项棠,分別為UriMatcher和ContentUris悲雳。掌握它們的使用,會(huì)便于我們的開(kāi)發(fā)工作香追。
UriMatcher
UriMatcher本質(zhì)上是一個(gè)文本過(guò)濾器合瓢,用在contentProvider中幫助我們過(guò)濾,分辨出查詢(xún)者想要查詢(xún)哪個(gè)數(shù)據(jù)表
UriMatcher(int code) //code:匹配未成功時(shí)返回的標(biāo)志碼透典,一般置為:UriMatcher.NO_MATCH(值為-1)
//向UriMatcher中注冊(cè)Uri晴楔,其中authority和path組合成一個(gè)Uri迁央,code標(biāo)識(shí)該Uri對(duì)應(yīng)的標(biāo)識(shí)碼,path中*表示可匹配任意文本滥崩,#表示只能匹配數(shù)字
addURI(String authority, String path, int code) void
match(Uri uri) int //該Uri是否匹配岖圈,若匹配返回注冊(cè)時(shí)候的標(biāo)志碼,否則返回-1
matcher.addURI("com.xx","person/id/#", 2)
匹配content://com.xx/person/id/1钙皮,any://com.xx/person/id/12
ContentUris工具類(lèi)
其實(shí)就是在末尾加上一個(gè)id
ContentUris.withAppendedId(Uri contentUri, long id) //為路徑加上ID部分
ContentUris.parseId(Uri contentUri) //解析Uri中的ID值
3. URL
URL = URI(scheme組件為部分已知的網(wǎng)絡(luò)協(xié)議) + 與scheme組件標(biāo)識(shí)的網(wǎng)絡(luò)協(xié)議匹配的協(xié)議處理器(URL Protocol Handler)蜂科,是URI子集。
- URI的scheme組件在URL中稱(chēng)為protocol組件短条,一般http导匣、https、ftp茸时、file贡定、data、jar等可都。
- URL Protocol Handler則是一種資源定位器和根據(jù)協(xié)議建立的約束規(guī)則與資源通信的讀寫(xiě)機(jī)制缓待,用于定位、讀寫(xiě)資源渠牲。
2.5.4 ContentObserver
1. 基本認(rèn)知
內(nèi)容觀察者旋炒,觀察指定的Uri引起數(shù)據(jù)庫(kù)變化后通知主線(xiàn)程,然后根據(jù)需求做處理签杈。首先在需要監(jiān)測(cè)ContentProvider的應(yīng)用中進(jìn)行注冊(cè)(ContentResolver調(diào)用方法的地方)瘫镇,在ContentProvider中要做的就是當(dāng)數(shù)據(jù)變化時(shí)進(jìn)行通知。
ContentResolver相關(guān)方法:
//傳遞一個(gè)ContentObserver的子類(lèi)對(duì)象進(jìn)去答姥,會(huì)回調(diào)其onChange(boolean selfChange)
registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
//取消對(duì)注冊(cè)的那個(gè)Uri的觀察铣除,這里傳進(jìn)去的就是在registerContentObserver中傳遞進(jìn)去的ContentObserver對(duì)象。
unregisterContentObserver(ContentObserver observer)
notifyChange(Uri uri, ContentObserver observer)//在Provider中調(diào)用鹦付,通知觀察uri的觀察者尚粘,observer可以傳null
registerContentObserver方法是注冊(cè)一個(gè)觀察者實(shí)例,當(dāng)指定的Uri發(fā)生改變時(shí)睁壁,這個(gè)實(shí)例會(huì)回調(diào)實(shí)例對(duì)象做相應(yīng)處理背苦。uri:需要觀察的Uri互捌,notifyForDescendents:如果為true表示以這個(gè)Uri為開(kāi)頭的所有Uri都會(huì)被匹配到潘明,如果為false表示精確匹配,即只會(huì)匹配這個(gè)給定的Uri秕噪。
舉個(gè)例子钳降,假如有這么幾個(gè)Uri:
① content://com.example.studentProvider/student
② content://com.example.studentProvider/student/#
③ content://com.example.studentProvider/student/10
④ content://com.example.studentProvider/student/teacher
假如觀察的Uri為content://com.example.studentProvider/student,當(dāng)notifyForDescendents為true時(shí)則以這個(gè)Uri開(kāi)頭的Uri的數(shù)據(jù)變化時(shí)都會(huì)被捕捉到腌巾,在這里也就是①②③④的Uri的數(shù)據(jù)的變化都能被捕捉到遂填,當(dāng)notifyForDescendents為false時(shí)則只有①中Uri變化時(shí)才能被捕捉到铲觉。
2. 實(shí)現(xiàn)一個(gè)ContentObserver
直接創(chuàng)建一個(gè)類(lèi)繼承ContentObserver,實(shí)現(xiàn)其onChange(boolean selfChange)
方法吓坚,當(dāng)指定的Uri的數(shù)據(jù)發(fā)生變化時(shí)會(huì)回調(diào)這個(gè)方法撵幽。在此方法中,調(diào)用構(gòu)造函數(shù)ContentObserver(Handlerhandler)
中傳入的 Handler對(duì)象發(fā)送消息到Handler中礁击,做相應(yīng)的處理盐杂。
2.5.5 數(shù)據(jù)共享
如何讓其他應(yīng)用也可以訪(fǎng)問(wèn)此應(yīng)用中的數(shù)據(jù)?
1. android:sharedUserId
向此應(yīng)用設(shè)置一個(gè)android:sharedUserId哆窿,然后需要訪(fǎng)問(wèn)此數(shù)據(jù)的應(yīng)用也設(shè)置同一個(gè)sharedUserId链烈,具有同樣的sharedUserId的應(yīng)用間可以共享數(shù)據(jù)。
不足:
1)不夠安全
2)無(wú)法做到對(duì)不同數(shù)據(jù)設(shè)置不同讀寫(xiě)權(quán)限的管理
2. android:exported
- android:exported 設(shè)置此provider是否可以被其他應(yīng)用使用挚躯。
- android:readPermission 該provider的讀權(quán)限的標(biāo)識(shí)
- android:writePermission 該provider的寫(xiě)權(quán)限標(biāo)識(shí)
- android:permission 該provider的讀寫(xiě)權(quán)限標(biāo)識(shí)
- android:grantUriPermissions 臨時(shí)權(quán)限標(biāo)識(shí)强衡,true時(shí),意味著該provider下所有數(shù)據(jù)均可被臨時(shí)使用码荔;false時(shí)漩勤,則反之。但可以通過(guò)設(shè)置<grantUriPermission>標(biāo)簽來(lái)指定哪些路徑可以被臨時(shí)使用缩搅。舉個(gè)例子锯七,比如你開(kāi)發(fā)了一個(gè)郵箱應(yīng)用,其中含有附件需要第三方應(yīng)用打開(kāi)誉己,但第三方應(yīng)用又沒(méi)有向你申請(qǐng)?jiān)摳郊淖x權(quán)限眉尸,但如果你設(shè)置了此標(biāo)簽,則可以在start第三方應(yīng)用時(shí)巨双,傳入FLAG_GRANT_READ_URI_PERMISSION或FLAG_GRANT_WRITE_URI_PERMISSION來(lái)讓第三方應(yīng)用臨時(shí)具有讀寫(xiě)該數(shù)據(jù)的權(quán)限噪猾。
實(shí)例:
1)聲明一個(gè)權(quán)限
<permission android:name="me.pengtao.READ" android:protectionLevel="normal"/>
2)配置
<provider
android:authorities="me.pengtao.contentprovidertest"
android:name=".provider.TestProvider"
android:readPermission="me.pengtao.READ"
android:exported="true">
</provider>
3)其他應(yīng)用中可以使用以下權(quán)限來(lái)對(duì)TestProvider進(jìn)行訪(fǎng)問(wèn)<uses-permission android:name="me.pengtao.READ"/>
對(duì)不同的數(shù)據(jù)表有不同的權(quán)限操作,要如何做呢筑累?Android為這種場(chǎng)景提供了provider的子標(biāo)簽<path-permission>
袱蜡,path-permission包括了以下幾個(gè)標(biāo)簽。
<path-permission android:path="string"
android:pathPrefix="string"
android:pathPattern="string"
android:permission="string"
android:readPermission="string"
android:writePermission="string" />
2.5.6 開(kāi)發(fā)ContentProvider步驟
1. 步驟一:暴露數(shù)據(jù)
- 開(kāi)發(fā)一個(gè)ContentProvider的子類(lèi)慢宗,默認(rèn)需要實(shí)現(xiàn)上面的6個(gè)方法坪蚁。
數(shù)據(jù)訪(fǎng)問(wèn)的方法(如:insert和update)可能被多個(gè)線(xiàn)程同時(shí)調(diào)用,此時(shí)必須是線(xiàn)程安全的镜沽。其他方法(如: onCreate())只能被應(yīng)用的主線(xiàn)程調(diào)用敏晤,它應(yīng)當(dāng)避免冗長(zhǎng)的操作。ContentResolver(內(nèi)容解析者)請(qǐng)求被自動(dòng)轉(zhuǎn)發(fā)到合適的內(nèi)容提供者實(shí)例,所以子類(lèi)不需要擔(dān)心跨進(jìn)程調(diào)用的細(xì)節(jié)缅茉。
實(shí)例代碼
實(shí)現(xiàn)的ContentProvider子類(lèi)中:
private final static int TEST = 100;
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = TestContract.CONTENT_AUTHORITY;//必須和清單文件中配置的一致
matcher.addURI(authority, TestContract.PATH_TEST, TEST);
return matcher;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor cursor = null;
switch (buildUriMatcher().match(uri)) {
case TEST://匹配不同的表
cursor = db.query(TestContract.TestEntry.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, null);
break;
}
return cursor;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Uri returnUri;
long _id;
switch ( buildUriMatcher().match(uri)) {
case TEST:
_id = db.insert(TestContract.TestEntry.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = TestContract.TestEntry.buildUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
default:
throw new android.database.SQLException("Unknown uri: " + uri);
}
return returnUri;
}
- 在清單文件中配置
<provider
android:authorities="me.pengtao.contentprovidertest" //一般為包名.含義
android:name=".provider.TestProvider"
android:exprorted="true"/>//是否允許其他應(yīng)用調(diào)用
2. 步驟二:獲取ContentResolver對(duì)象嘴脾,并使用
- 獲取ContentResolver對(duì)象。Context的方法:getContentResolver(),因此Activity译打,Service都能獲得該對(duì)象
- 調(diào)用ContentResolver的insert耗拓,delete,update奏司,query方法
ContentValues contentValues = new ContentValues();
contentValues.put(TestContract.TestEntry.COLUMN_NAME, "peng");
contentValues.put(TestContract.TestEntry._ID, System.currentTimeMillis());
getContentResolver().insert(TestContract.TestEntry.CONTENT_URI, contentValues);
Cursor cursor = getContentResolver().query(TestContract.TestEntry.CONTENT_URI, null, null, null, null);
try {
Log.e("ContentProviderTest", "total data number = " + cursor.getCount());
cursor.moveToFirst();
Log.e("ContentProviderTest", "total data number = " + cursor.getString(1));
} finally {
cursor.close();
}
2.6 四大組件的工作過(guò)程
2.6.1 四大組件的運(yùn)行狀態(tài)
Android的四大組件除了BroadcastReceiver以外乔询,都需要在AndroidManifest文件注冊(cè),BroadcastReceiver可以通過(guò)代碼注冊(cè)韵洋。調(diào)用方式上哥谷,除了ContentProvider以外的三種組件都需要借助intent。
Activity
是一種展示型組件麻献,用于向用戶(hù)直接地展示一個(gè)界面们妥,并且可以接收用戶(hù)的輸入信息從而進(jìn)行交互,扮演的是一個(gè)前臺(tái)界面的角色勉吻。Activity的啟動(dòng)由intent觸發(fā)监婶,有隱式和顯式兩種方式。一個(gè)Activity可以有特定的啟動(dòng)模式齿桃,finish方法結(jié)束Activity運(yùn)行惑惶。
Service
是一種計(jì)算型組件,在后臺(tái)執(zhí)行一系列計(jì)算任務(wù)短纵。它==本身還是運(yùn)行在主線(xiàn)程中的==带污,所以耗時(shí)的邏輯仍需要單獨(dú)的線(xiàn)程去完成。Activity只有一種狀態(tài):?jiǎn)?dòng)狀態(tài)香到。而service有兩種:?jiǎn)?dòng)狀態(tài)和綁定狀態(tài)鱼冀。當(dāng)service處于綁定狀態(tài)時(shí),外界可以很方便的和service進(jìn)行通信悠就,而在啟動(dòng)狀態(tài)中是不可與外界通信的千绪。Service可以停止,需要靈活采用stopService
和unBindService
BroadcastReceiver
是一種消息型組件,用于在不同的組件乃至不同的應(yīng)用之間傳遞消
息梗脾。
- 靜態(tài)注冊(cè)
在清單文件中進(jìn)行注冊(cè)廣播, 這種廣播在應(yīng)用安裝時(shí)會(huì)被系統(tǒng)解析, 此種形式的廣播不需要應(yīng)用啟動(dòng)就可以接收到相應(yīng)的廣播. - 動(dòng)態(tài)注冊(cè)
需要通過(guò)Context.registerReceiver()來(lái)實(shí)現(xiàn), 并在不需要的時(shí)候通過(guò)Context#unRegisterReceiver()
來(lái)解除廣播. 此種形態(tài)的廣播要應(yīng)用啟動(dòng)才能注冊(cè)和接收廣播. 在實(shí)際開(kāi)發(fā)中通過(guò)Context的一系列的send方法來(lái)發(fā)送廣播, 被發(fā)送的廣播會(huì)被系統(tǒng)發(fā)送給感興趣的廣播接收者,發(fā)送和接收的過(guò)程的匹配是通過(guò)廣播接收者的<intent-filter>來(lái)描述的.可以實(shí)現(xiàn)低耦合的觀察者模式, 觀察者和被觀察者之間可以沒(méi)有任何耦合. 但廣播不適合來(lái)做耗時(shí)操作.
ContentProvider
是一種數(shù)據(jù)共享型組件荸型,用于向其他組件乃至其他應(yīng)用共享數(shù)據(jù)。在它內(nèi)部維持著一份數(shù)據(jù)集合, 這個(gè)數(shù)據(jù)集合既可以通過(guò)數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn), 也可以采用其他任何類(lèi)型來(lái)實(shí)現(xiàn), 例如list或者map. ContentProvider對(duì)數(shù)據(jù)集合的具體實(shí)現(xiàn)并沒(méi)有任何要求.要注意處理好內(nèi)部的insert, delete, update, query方法的線(xiàn)程同步, 因?yàn)檫@幾個(gè)方法是==在Binder線(xiàn)程池被調(diào)用==.
2.6.2 Activity的工作過(guò)程
(1) Activity的所有 startActivity 重載方法最終都會(huì)調(diào)用 startActivityForResult炸茧。
(2) 調(diào)用 mInstrumentation.execStartActivity.execStartActivity()方法瑞妇。
(3) 代碼中啟動(dòng)Activity的真正實(shí)現(xiàn)是由ActivityManagerNative.getDefault().startActivity()方法完成的. ActivityManagerService簡(jiǎn)稱(chēng)AMS. AMS繼承自ActivityManagerNative(), 而ActivityManagerNative()繼承自Binder并實(shí)現(xiàn)了IActivityManager這個(gè)Binder接口, 因此AMS也是一個(gè)Binder, 它是IActivityManager的具體實(shí)現(xiàn).ActivityManagerNative.getDefault()本質(zhì)是一個(gè)IActivityManager類(lèi)型的Binder對(duì)象, 因此==具體實(shí)現(xiàn)是AMS==.
(4) 在ActivityManagerNative中, AMS這個(gè)Binder對(duì)象采用單例模式對(duì)外提供, Singleton是一個(gè)單例封裝類(lèi). 第一次調(diào)用它的get()方法時(shí)會(huì)通過(guò)create方法來(lái)初始化AMS這個(gè)Binder對(duì)象, 在后續(xù)調(diào)用中會(huì)返回這個(gè)對(duì)象.
(5) AMS的startActivity()過(guò)程
- checkStartActivityResult () 方法檢查啟動(dòng)Activity的結(jié)果( 包括檢查有無(wú)在
manifest注冊(cè)) - Activity啟動(dòng)過(guò)程經(jīng)過(guò)兩次轉(zhuǎn)移, 最后又轉(zhuǎn)移到了mStackSupervisor.startActivityMayWait()這個(gè)方法, 所屬類(lèi)為ActivityStackSupervisor. 在startActivityMayWait()內(nèi)部又調(diào)用了startActivityLocked()這里會(huì)返回結(jié)果碼就是之前checkStartActivityResult()用到的。
- 方法最后會(huì)調(diào)用startActivityUncheckedLocked(), 然后又調(diào)用了ActivityStack#resumeTopActivityLocked(). 這個(gè)時(shí)候啟動(dòng)過(guò)程已經(jīng)從ActivityStackSupervisor轉(zhuǎn)移到了ActivityStack類(lèi)中.
(6) 在最后的 ActivityStackSupervisor. realStartActivityLocked() 中梭冠,調(diào)用了 app.thread.scheduleLaunchActivity() 方法辕狰。 這個(gè)app.thread是ApplicationThread 類(lèi)型,繼承于 IApplicationThread 是一個(gè)Binder類(lèi)妈嘹,內(nèi)部是各種啟動(dòng)/停止 Service/Activity的接口柳琢。
(7) 在ApplicationThread中, scheduleLaunchActivity() 用來(lái)啟動(dòng)Activity润脸,里面的實(shí)現(xiàn)就是發(fā)送一個(gè)Activity的消息( 封裝成 從ActivityClientRecord 對(duì)象) 交給Handler處理柬脸。這個(gè)Handler有一個(gè)簡(jiǎn)潔的名字 H 。
(8) 在H的 handleMessage() 方法里毙驯,通過(guò) handleLaunchActivity() 方法完成Activity對(duì)象的創(chuàng)建和啟動(dòng),并且ActivityThread通過(guò)handleResumeActivity()方法來(lái)調(diào)用被啟動(dòng)的onResume()這一生命周期方法倒堕。PerformLaunchActivity()主要完成了如下幾件事: - 從ActivityClientRecord對(duì)象中獲取待啟動(dòng)的Activity組件信息
- 通過(guò) Instrumentation 的 newActivity 方法使用類(lèi)加載器創(chuàng)建Activity對(duì)象
- 通過(guò) LoadedApk 的makeApplication方法嘗試創(chuàng)建Application對(duì)象,通過(guò)類(lèi)加載器實(shí)現(xiàn)( 如果Application已經(jīng)創(chuàng)建過(guò)了就不會(huì)再創(chuàng)建)
- 創(chuàng)建 ContextImpl 對(duì)象并通過(guò)Activity的 attach 方法完成一些重要數(shù)據(jù)的初始化(ContextImpl是一個(gè)很重要的數(shù)據(jù)結(jié)構(gòu), 它是Context的具體實(shí)現(xiàn), Context中的大部分邏輯都是由ContentImpl來(lái)完成的. ContextImpl是通過(guò)Activity的attach()方法來(lái)和Activity建立關(guān)聯(lián)的,除此之外, 在attach()中Activity還會(huì)完成Window的創(chuàng)建并建立自己和Window的關(guān)聯(lián), 這樣當(dāng)Window接收到外部輸入事件收就可以將事件傳遞給Activity.)
- 通過(guò) mInstrumentation.callActivityOnCreate(activity, r.state) 方法調(diào)用Activity的 onCreate 方法
2.6.3 Service的工作過(guò)程
- 啟動(dòng)狀態(tài):執(zhí)行后臺(tái)計(jì)算
- 綁定狀態(tài):用于其他組件與Service交互
兩種狀態(tài)是可以共存的
1. Service的啟動(dòng)過(guò)程
(1) Service的啟動(dòng)從 ContextWrapper 的 startService 開(kāi)始
(2) 在ContextWrapper中爆价,大部分操作通過(guò)一個(gè) ContextImpl 對(duì)象mBase實(shí)現(xiàn)
(3) 在ContextImpl中垦巴, mBase.startService() 會(huì)調(diào)用 startServiceCommon 方法,而
startServiceCommon方法又會(huì)通過(guò) ActivityManagerNative.getDefault() ( 實(shí)際上就是AMS) 這個(gè)對(duì)象來(lái)啟動(dòng)一個(gè)服務(wù)铭段。
(4) AMS會(huì)通過(guò)一個(gè) ActiveService 對(duì)象( 輔助AMS進(jìn)行Service管理的類(lèi)骤宣,包括Service的啟動(dòng),綁定和停止等) mServices來(lái)完成啟動(dòng)Service: mServices.startServiceLocked() 。
(5) 在mServices.startServiceLocked()最后會(huì)調(diào)用 startServiceInnerLocked() 方法:將Service的信息包裝成一個(gè) ServiceRecord 對(duì)象序愚,ServiceRecord一直貫穿著整個(gè)Service的啟動(dòng)過(guò)程憔披。通過(guò) bringUpServiceLocked()方法來(lái)處理,bringUpServiceLocked()又調(diào)用了 realStartServiceLocked() 方法爸吮,這才真正地去啟動(dòng)一個(gè)Service了芬膝。
(6) realStartServiceLocked()方法的工作如下:
- app.thread.scheduleCreateService() 來(lái)創(chuàng)建Service并調(diào)用其onCreate()生命周期方法
- sendServiceArgsLocked() 調(diào)用其他生命周期方法,如onStartCommand()
- app.thread對(duì)象是 IApplicationThread 類(lèi)型形娇,實(shí)際上就是一個(gè)Binder锰霜,具體實(shí)現(xiàn)是ApplicationThread繼承ApplictionThreadNative
(7) 具體看Application對(duì)Service的啟動(dòng)過(guò)程app.thread.scheduleCreateService():通過(guò) sendMessage(H.CREATE_SERVICE , s) ,這個(gè)過(guò)程和Activity啟動(dòng)過(guò)程類(lèi)似桐早,同時(shí)通過(guò)發(fā)送消息給Handler H來(lái)完成的癣缅。
(8) H會(huì)接受這個(gè)CREATE_SERVICE消息并通過(guò)ActivityThread的 handleCreateService() 來(lái)完成Service的最終啟動(dòng)。
(9) handleCreateService()完成了以下工作:
- 通過(guò)ClassLoader創(chuàng)建Service對(duì)象
- 創(chuàng)建Service內(nèi)部的Context對(duì)象
- 創(chuàng)建Application哄酝,并調(diào)用其onCreate()( 只會(huì)有一次)
- 通過(guò) service.attach() 方法建立Service與context的聯(lián)系( 與Activity類(lèi)似)
- 調(diào)用service的 onCreate() 生命周期方法所灸,至此,Service已經(jīng)啟動(dòng)了
- 將Service對(duì)象存儲(chǔ)到ActivityThread的一個(gè)ArrayMap中
2. Service的綁定過(guò)程
和service的啟動(dòng)過(guò)程類(lèi)似的:
(1) Service的綁定是從 ContextWrapper 的 bindService 開(kāi)始
(2) 在ContextWrapper中炫七,交給 ContextImpl 對(duì)象 mBase.bindService()
(3) 最終會(huì)調(diào)用ContextImpl的 bindServiceCommon 方法爬立,這個(gè)方法完成兩件事:
- 將客戶(hù)端的ServiceConnection轉(zhuǎn)化成 ServiceDispatcher.InnerConnection 對(duì)象。ServiceDispatcher連接ServiceConnection和InnerConnection万哪。這個(gè)過(guò)程通過(guò) LoadedApk 的 getServiceDispatcher 方法來(lái)實(shí)現(xiàn)侠驯,將客戶(hù)端的ServiceConnection和ServiceDispatcher的映射關(guān)系存在一個(gè)ArrayMap中。
- 通過(guò)AMS來(lái)完成Service的具體綁定過(guò)程 ActivityManagerNative.getDefault().bindService()
(4) AMS中奕巍,bindService()方法再調(diào)用 bindServiceLocked() 吟策,bindServiceLocked()再調(diào)用 bringUpServiceLocked() ,bringUpServiceLocked()又會(huì)調(diào)用 realStartServiceLocked() 的止。
(5) AMS的realStartServiceLocked()會(huì)調(diào)用 ActiveServices 的requrestServiceBindingLocked() 方法檩坚,最終是調(diào)用了ServiceRecord對(duì)象r的 app.thread.scheduleBindService() 方法。
(6) ApplicationThread的一系列以schedule開(kāi)頭的方法,內(nèi)部都通過(guò)Handler H來(lái)中轉(zhuǎn):scheduleBindService()內(nèi)部也是通過(guò) sendMessage(H.BIND_SERVICE , s)
(7) 在H內(nèi)部接收到BIND_SERVICE這類(lèi)消息時(shí)就交給 ActivityThread 的handleBindService() 方法處理:
- 根據(jù)Servcie的token取出Service對(duì)象
- 調(diào)用Service的 onBind() 方法匾委,至此拖叙,Service就處于綁定狀態(tài)了。
- 這時(shí)客戶(hù)端還不知道已經(jīng)成功連接Service赂乐,需要調(diào)用客戶(hù)端的binder對(duì)象來(lái)調(diào)用客戶(hù)端的ServiceConnection中的 onServiceConnected() 方法薯鳍,這個(gè)通過(guò) ActivityManagerNative.getDefault().publishService() 進(jìn)行。ActivityManagerNative.getDefault()就是AMS挨措。
(8) AMS的publishService()交給ActivityService對(duì)象 mServices 的 publishServiceLocked() 來(lái)處理挖滤,核心代碼就一句話(huà) c.conn.connected(r.name,service) 。對(duì)象c的類(lèi)型是 ConnectionRecord 浅役,c.conn就是ServiceDispatcher.InnerConnection對(duì)象斩松,service就是Service的onBind方法返回的Binder對(duì)象。
(9) c.conn.connected(r.name,service)內(nèi)部實(shí)現(xiàn)是交給了mActivityThread.post(new RunnConnection(name ,service,0)); 實(shí)現(xiàn)觉既。ServiceDispatcher的mActivityThread是一個(gè)Handler惧盹,其實(shí)就是ActivityThread中的H。這樣一來(lái)RunConnection就經(jīng)由H的post方法從而運(yùn)行在主線(xiàn)程中奋救,因此客戶(hù)端ServiceConnection中的方法是在主線(xiàn)程中被回調(diào)的岭参。
(10) RunConnection的定義如下:
- 繼承Runnable接口, run() 方法的實(shí)現(xiàn)也是簡(jiǎn)單調(diào)用了ServiceDispatcher的 doConnected 方法尝艘。
- 由于ServiceDispatcher內(nèi)部保存了客戶(hù)端的ServiceConntion對(duì)象演侯,可以很方便地調(diào)用ServiceConntion對(duì)象的 onServiceConnected 方法。
- 客戶(hù)端的onServiceConnected方法執(zhí)行后背亥,Service的綁定過(guò)程也就完成了秒际。
- 根據(jù)步驟8、9狡汉、10service綁定后通過(guò)ServiceDispatcher通知客戶(hù)端的過(guò)程可以說(shuō)明ServiceDispatcher起著連接ServiceConnection和InnerConnection的作用娄徊。 至于Service的停止和解除綁定的過(guò)程,系統(tǒng)流程都是類(lèi)似的盾戴。
2.6.4 BroadcastReceiver的工作過(guò)程
簡(jiǎn)單回顧一下廣播的使用方法, 首先定義廣播接收者, 只需要繼承BroadcastReceiver并重寫(xiě)onReceive()方法即可. 定義好了廣播接收者, 還需要注冊(cè)廣播接收者, 分為兩種靜態(tài)注冊(cè)或者動(dòng)態(tài)注冊(cè). 注冊(cè)完成之后就可以發(fā)送廣播了.
1. 廣播的注冊(cè)過(guò)程
(1) 動(dòng)態(tài)注冊(cè)的過(guò)程是從ContextWrapper#registerReceiver()開(kāi)始的. 和Activity或者Service一樣. ContextWrapper并沒(méi)有做實(shí)際的工作, 而是將注冊(cè)的過(guò)程直接交給了ContextImpl來(lái)完成.
(2) ContextImpl#registerReceiver()方法調(diào)用了本類(lèi)的registerReceiverInternal()方法.
(3) 系統(tǒng)首先從mPackageInfo獲取到IIntentReceiver對(duì)象, 然后再采用跨進(jìn)程的方式向AMS發(fā)送廣播注冊(cè)的請(qǐng)求. 之所以采用IIntentReceiver而不是直接采用BroadcastReceiver, 這是因?yàn)樯鲜鲎?cè)過(guò)程中是一個(gè)進(jìn)程間通信的過(guò)程. 而B(niǎo)roadcastReceiver作為Android中的一個(gè)組件是不能直接跨進(jìn)程傳遞的. 所有需要通過(guò)IIntentReceiver來(lái)中轉(zhuǎn)一下.
(4) IIntentReceiver作為一個(gè)Binder接口, 它的具體實(shí)現(xiàn)是LoadedApk.ReceiverDispatcher.InnerReceiver, ReceiverDispatcher的內(nèi)部同時(shí)保存了BroadcastReceiver和InnerReceiver, 這樣當(dāng)接收到廣播的時(shí)候, ReceiverDispatcher可以很方便的調(diào)用BroadcastReceiver#onReceive()方法. 這里和Service很像有同樣的類(lèi), 并且內(nèi)部類(lèi)中同樣也是一個(gè)Binder接口.
(5) 由于注冊(cè)廣播真正實(shí)現(xiàn)過(guò)程是在AMS中, 因此跟進(jìn)AMS中, 首先看registerReceiver()方法, 這里只關(guān)心里面的核心部分. 這段代碼最終會(huì)把遠(yuǎn)程的InnerReceiver對(duì)象以及IntentFilter對(duì)象存儲(chǔ)起來(lái), 這樣整個(gè)廣播的注冊(cè)就完成了.
2. 廣播的發(fā)送和接收過(guò)程
廣播的發(fā)送有幾種:普通廣播寄锐、有序廣播和粘性廣播,他們的發(fā)送/接收流程是類(lèi)似的尖啡,因此只分析普通廣播的實(shí)現(xiàn)橄仆。
(1) 廣播的發(fā)送和接收, 本質(zhì)就是一個(gè)過(guò)程的兩個(gè)階段. 廣播的發(fā)送仍然開(kāi)始于ContextImpl#sendBroadcase()方法, 之所以不是Context, 那是因?yàn)镃ontext#sendBroad()是一個(gè)抽象方法. 和廣播的注冊(cè)過(guò)程一樣, ContextWrapper#sendBroadcast()仍然什么都不做, 只是把事情交給了ContextImpl去處理.
(2) ContextImpl里面也幾乎什么都沒(méi)有做, 內(nèi)部直接向AMS發(fā)起了一個(gè)異步請(qǐng)求用于發(fā)送廣播.
(3) 調(diào)用AMS#broadcastIntent()方法,繼續(xù)調(diào)用broadcastIntentLocked()方法衅斩。
(4) 在broadcastIntentLocked()內(nèi)部, 會(huì)根據(jù)intent-filter查找出匹配的廣播接收者并經(jīng)過(guò)一系列的條件過(guò)濾. 最終會(huì)將滿(mǎn)足條件的廣播接收者添加到BroadcastQueue中, 接著B(niǎo)roadcastQueue就會(huì)將廣播發(fā)送給相應(yīng)廣播接收者.
(5) BroadcastQueue#scheduleBroadcastsLocked()方法內(nèi)并沒(méi)有立即發(fā)送廣播, 而是發(fā)送了一個(gè)BROADCAST_INTENT_MSG類(lèi)型的消息, BroadcastQueue收到消息后會(huì)調(diào)用processNextBroadcast()方法盆顾。
(6) 無(wú)序廣播存儲(chǔ)在mParallelBroadcasts中, 系統(tǒng)會(huì)遍歷這個(gè)集合并將其中的廣播發(fā)送給他們所有的接收者, 具體的發(fā)送過(guò)程是通過(guò)deliverToRegisteredReceiverLocked()方法實(shí)現(xiàn). deliverToRegisteredReceiverLocked()負(fù)責(zé)將一個(gè)廣播發(fā)送給一個(gè)特定的接收者, 它的內(nèi)部調(diào)用了performReceiverLocked方法來(lái)完成具體發(fā)送過(guò)程.
(7) performReceiverLocked()方法調(diào)用的ApplicationThread#scheduleRegisteredReceiver()實(shí)現(xiàn)比較簡(jiǎn)單, 它通過(guò)InnerReceiver來(lái)實(shí)現(xiàn)廣播的接收
(8) scheduleRegisteredReceiver()方法中,receiver.performReceive()中的receiver對(duì)應(yīng)著IIntentReceiver類(lèi)型的接口. 而具體的實(shí)現(xiàn)就是ReceiverDispatcher$InnerReceiver. 這兩個(gè)嵌套的內(nèi)部類(lèi)是所屬在LoadedApk中的畏梆。
(9) 又調(diào)用了LoadedApk$ReceiverDispatcher#performReceive()的方法.在performReceiver()這個(gè)方法中, 會(huì)創(chuàng)建一個(gè)Args對(duì)象并通過(guò)mActivityThread的post方法執(zhí)行args中的邏輯. 而這些類(lèi)的本質(zhì)關(guān)系就是:
- Args: 實(shí)現(xiàn)類(lèi)Runnable
- mActivityThread: 是一個(gè)Handler, 就是ActivityThread中的mH. mH就是ActivityThread$H. 這個(gè)內(nèi)部類(lèi)H以前說(shuō)過(guò).
(10) 實(shí)現(xiàn)Runnable接口的Args中BroadcastReceiver#onReceive()方法被執(zhí)行了, 也就是說(shuō)應(yīng)用已經(jīng)接收到了廣播, 同時(shí)onReceive()方法是在廣播接收者的主線(xiàn)程中被調(diào)用的.
android 3.1開(kāi)始就增添了兩個(gè)標(biāo)記為. 分別是FLAG_INCLUDE_STOPPED_PACKAGES, FLAG_EXCLUDE_STOPPED_PACKAGES. 用來(lái)控制廣播是否要對(duì)處于停止的應(yīng)用起作用.
- FLAG_INCLUDE_STOPPED_PACKAGES: 包含停止應(yīng)用, 廣播會(huì)發(fā)送給已停止的應(yīng)用.
- FLAG_EXCLUDE_STOPPED_PACKAGES: 不包含已停止應(yīng)用, 廣播不會(huì)發(fā)送給已停止的應(yīng)用
在android 3.1開(kāi)始, 系統(tǒng)就為所有廣播默認(rèn)添加了FLAG_EXCLUDE_STOPPED_PACKAGES標(biāo)識(shí)您宪。 當(dāng)這兩個(gè)標(biāo)記共存的時(shí)候以FLAG_INCLUDE_STOPPED_PACKAGES(非默認(rèn)項(xiàng)為主).
應(yīng)用處于停止分為兩種
- 應(yīng)用安裝后未運(yùn)行
- 被手動(dòng)或者其他應(yīng)用強(qiáng)停
開(kāi)機(jī)廣播同樣受到了這個(gè)標(biāo)志位的影響. 從Android 3.1開(kāi)始處于停止?fàn)顟B(tài)的應(yīng)用同樣無(wú)法接受到開(kāi)機(jī)廣播, 而在android 3.1之前處于停止的狀態(tài)也是可以接收到開(kāi)機(jī)廣播的.
2.6.5 ContentProvider的工作機(jī)制
ContentProvider是一種內(nèi)容共享型組件, 它通過(guò)Binder向其他組件乃至其他應(yīng)用提供數(shù)據(jù). 當(dāng)ContentProvider所在的進(jìn)程啟動(dòng)時(shí), ContentProvider會(huì)同時(shí)啟動(dòng)并發(fā)布到AMS中. 要注意:這個(gè)時(shí)候ContentProvider的onCreate()方法是先于Application的onCreate()執(zhí)行的,這一點(diǎn)在四大組件是少有的現(xiàn)象.
(1) 當(dāng)一個(gè)應(yīng)用啟動(dòng)時(shí)宪巨,入口方法是ActivityThread的main方法磷杏,其中創(chuàng)建ActivityThread的實(shí)例并創(chuàng)建主線(xiàn)程的消息隊(duì)列;
(2) ActivityThread的attach方法中會(huì)遠(yuǎn)程調(diào)用ActivityManagerService的attachApplication揖铜,并將ApplicationThread提供給AMS茴丰,ApplicationThread主要用于ActivityThread和AMS之間的通信达皿;
(3) ActivityManagerService的attachApplication會(huì)調(diào)用ApplicationThread的bindApplication方法天吓,這個(gè)方法會(huì)通過(guò)H切換到ActivityThread中去執(zhí)行,即調(diào)用handleBindApplication方法峦椰;
(4) handleBindApplication方法會(huì)創(chuàng)建Application對(duì)象并加載ContentProvider龄寞,注意是先加載ContentProvider,然后調(diào)用Application的onCreate方法汤功。
(5) ContentProvider啟動(dòng)后, 外界就可以通過(guò)它所提供的增刪改查這四個(gè)接口來(lái)操作ContentProvider中的數(shù)據(jù)源, 這四個(gè)方法都是通過(guò)Binder來(lái)調(diào)用的, 外界無(wú)法直接訪(fǎng)問(wèn)ContentProvider, 它只能通過(guò)AMS根據(jù)URI來(lái)獲取到對(duì)應(yīng)的ContentProvider的Binder接口IContentProvider, 然后再通過(guò)IContentProvider來(lái)訪(fǎng)問(wèn)ContentProvider中的數(shù)據(jù)源.
ContentProvider的android:multiprocess屬性決定它是否是單實(shí)例物邑,默認(rèn)值是false,也就是默認(rèn)是單實(shí)例滔金。當(dāng)設(shè)置為true時(shí)色解,每個(gè)調(diào)用者的進(jìn)程中都存在一個(gè)ContentProvider對(duì)象。
當(dāng)調(diào)用ContentProvider的insert餐茵、delete科阎、update、query方法中的任何一個(gè)時(shí)忿族,如果ContentProvider所在的進(jìn)程沒(méi)有啟動(dòng)的話(huà)锣笨,那么就會(huì)觸發(fā)ContentProvider的創(chuàng)建,并伴隨著ContentProvider所在進(jìn)程的啟動(dòng)道批。
以query調(diào)用為例
(1) 首先會(huì)獲取IContentProvider對(duì)象, 不管是通過(guò)acquireUnstableProvider()方法還是直接通過(guò)acquireProvider()方法, 他們的本質(zhì)都是一樣的, 最終都是通過(guò)acquireProvider方法來(lái)獲取ContentProvider.
(2) ApplicationContentResolver#acquireProvider()方法并沒(méi)有處理任何邏輯, 它直接調(diào)用了ActivityThread#acquireProvider()
(3) 從ActivityThread中查找是否已經(jīng)存在了ContentProvider了, 如果存在那么就直接返回. ActivityThread中通過(guò)mProviderMap來(lái)存儲(chǔ)已經(jīng)啟動(dòng)的ContentProvider對(duì)象, 這個(gè)集合的存儲(chǔ)類(lèi)型ArrayMap mProviderMap. 如果目前ContentProvider沒(méi)有啟動(dòng), 那么就發(fā)送一個(gè)進(jìn)程間請(qǐng)求給AMS讓其啟動(dòng)項(xiàng)目目標(biāo)ContentProvider, 最后再通過(guò)installProvider()方法來(lái)修改引用計(jì)數(shù).
(4) AMS是如何啟動(dòng)ContentProvider的呢?首先會(huì)啟動(dòng)ContentProvider所在的進(jìn)程, 然后再啟動(dòng)ContentProvider. 啟動(dòng)進(jìn)程是由AMS#startProcessLocked()方法來(lái)完成, 其內(nèi)部主要是通過(guò)Process#start()方法來(lái)完成一個(gè)新進(jìn)程的啟動(dòng), 新進(jìn)程啟動(dòng)后其入口方法為ActivityThread#main()方法错英。
(5) ActivityThread#main()是一個(gè)靜態(tài)方法, 在它的內(nèi)部首先會(huì)創(chuàng)建ActivityThread實(shí)例并調(diào)用attach()方法來(lái)進(jìn)行一系列初始化, 接著就開(kāi)始進(jìn)行消息循環(huán). ActivityThread#attach()方法會(huì)將Application對(duì)象通過(guò)AMS#attachApplication方法跨進(jìn)程傳遞給AMS, 最終AMS會(huì)完成ContentProvider的創(chuàng)建過(guò)程.
(6) AMS#attachApplication()方法調(diào)用了attachApplication(), 然后又調(diào)用了ApplicationThread#bindApplication(), 這個(gè)過(guò)程也屬于進(jìn)程通信.bindApplication()方法會(huì)發(fā)送一個(gè)BIND_APPLICATION類(lèi)型的消息給mH, 這是一個(gè)Handler, 它收到消息后會(huì)調(diào)用ActivityThread#handleBindApplication()方法.
(7) ActivityThread#handlerBindApplication()則完成了Application的創(chuàng)建以及ContentProvider 可以分為如下四個(gè)步驟:
- 創(chuàng)建ContentProvider和Instrumentation
- 創(chuàng)建Application對(duì)象
- 啟動(dòng)當(dāng)前進(jìn)程的ContentProvider并調(diào)用onCreate()方法. 主要內(nèi)部實(shí)現(xiàn)是installContentProvider()完成了ContentProvider的啟動(dòng)工作, 首先會(huì)遍歷當(dāng)前進(jìn)程的ProviderInfo的列表并一一調(diào)用installProvider()方法來(lái)啟動(dòng)他們, 接著將已經(jīng)啟動(dòng)的ContentProvider發(fā)布到AMS中, AMS會(huì)把他們存儲(chǔ)在ProviderMap中, 這樣一來(lái)外部調(diào)用者就可以直接從AMS中獲取到ContentProvider. installProvider()內(nèi)部通過(guò)類(lèi)加載器創(chuàng)建的ContentProvider實(shí)例并在方法中調(diào)用了attachInfo(), 在這內(nèi)部調(diào)用了ContentProvider#onCreate()
- 調(diào)用Application#onCreate()
經(jīng)過(guò)了上述的四個(gè)步驟, ContentProvider已經(jīng)啟動(dòng)成功, 并且其所在的進(jìn)程的Application也已經(jīng)成功, 這意味著ContentProvider所在的進(jìn)程已經(jīng)完成了整個(gè)的啟動(dòng)過(guò)程, 然后其他應(yīng)用就可以通過(guò)AMS來(lái)訪(fǎng)問(wèn)這個(gè)ContentProvider了.
當(dāng)拿到了ContentProvider以后, 就可以通過(guò)它所提供的接口方法來(lái)訪(fǎng)問(wèn)它. 這里要注意: 這里的ContentProvider并不是原始的ContentProvider. 而是ContentProvider的Binder類(lèi)型對(duì)象IContentProvider, 而IContentProvider的具體實(shí)現(xiàn)是ContentProviderNative和ContentProvider.Transport. 后者繼承了前者.
如果還用query方法來(lái)解釋流程: 那么最開(kāi)始其他應(yīng)用通過(guò)AMS獲取到ContentProvider的Binder對(duì)象就是IContentProvider. 而IContentProvider的實(shí)際實(shí)現(xiàn)者是ContentProvider.Transport. 因此實(shí)際上外部應(yīng)用調(diào)用的時(shí)候本質(zhì)上會(huì)以進(jìn)程間通信的方式調(diào)用ContentProvider.Transport的query()方法。