第2章Android四大組件

2.1 Activity

2.1.1 Activity的生命周期全面分析

典型情況下的生命周期:在用戶(hù)參與的情況下膝晾,Activity所經(jīng)過(guò)的生命周期改變区宇。
異常情況下的生命周期:Activity被系統(tǒng)回收或者設(shè)備的Configuration發(fā)生改變從而導(dǎo)致Activity被銷(xiāo)毀重建。

1. 典型情況下的生命周期分析

Activity生命周期
  • ==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)毀重建。生命周期如下:


異常情況下Activity的重建過(guò)程

當(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

這是最普通旺上、最常用的后臺(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)如下:

AIDL目錄結(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ù)。
Binder工作機(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 
ContentResolver角色

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ù)

  1. 開(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;
}

  1. 在清單文件中配置
<provider    
    android:authorities="me.pengtao.contentprovidertest" //一般為包名.含義 
    android:name=".provider.TestProvider"
    android:exprorted="true"/>//是否允許其他應(yīng)用調(diào)用

2. 步驟二:獲取ContentResolver對(duì)象嘴脾,并使用

  1. 獲取ContentResolver對(duì)象。Context的方法:getContentResolver(),因此Activity译打,Service都能獲得該對(duì)象
  2. 調(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可以停止,需要靈活采用stopServiceunBindService

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ò)程

image
image

(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ò)程

image
image

和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ò)程

image
image

(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ī)廣播的.

更多參考1奈懒、參考2參考3

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)象.


image
image

(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()方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末隆豹,一起剝皮案震驚了整個(gè)濱河市椭岩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌璃赡,老刑警劉巖判哥,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鉴吹,居然都是意外死亡姨伟,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)豆励,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)夺荒,“玉大人瞒渠,你說(shuō)我怎么就攤上這事〖级螅” “怎么了伍玖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)剿吻。 經(jīng)常有香客問(wèn)我窍箍,道長(zhǎng),這世上最難降的妖魔是什么丽旅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任椰棘,我火速辦了婚禮,結(jié)果婚禮上榄笙,老公的妹妹穿的比我還像新娘邪狞。我一直安慰自己,他們只是感情好茅撞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布帆卓。 她就那樣靜靜地躺著,像睡著了一般米丘。 火紅的嫁衣襯著肌膚如雪剑令。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天拄查,我揣著相機(jī)與錄音吁津,去河邊找鬼。 笑死靶累,一個(gè)胖子當(dāng)著我的面吹牛腺毫,可吹牛的內(nèi)容都是我干的卦方。 我是一名探鬼主播深寥,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼磨镶!你這毒婦竟也來(lái)了邪蛔?” 一聲冷哼從身側(cè)響起急黎,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侧到,沒(méi)想到半個(gè)月后勃教,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匠抗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年故源,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汞贸。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绳军,死狀恐怖印机,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情门驾,我是刑警寧澤射赛,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站奶是,受9級(jí)特大地震影響楣责,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聂沙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一秆麸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逐纬,春花似錦蛔屹、人聲如沸削樊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)漫贞。三九已至甸箱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迅脐,已是汗流浹背芍殖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谴蔑,地道東北人豌骏。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像隐锭,于是被迫代替她去往敵國(guó)和親窃躲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,496評(píng)論 25 707
  • 1.什么是Activity?問(wèn)的不太多,說(shuō)點(diǎn)有深度的 四大組件之一,一般的,一個(gè)用戶(hù)交互界面對(duì)應(yīng)一個(gè)activit...
    JoonyLee閱讀 5,728評(píng)論 2 51
  • 姓名:劉蕓 時(shí)間真的好快荞怒,轉(zhuǎn)眼又第7周了洒琢,這周基本在外地。跟舅姆褐桌、小姨她們?nèi)フ憬又菸髑啻蹇磸能?chē)上摔下來(lái)的姨夫衰抑。還...
    云變閱讀 200評(píng)論 0 0
  • 第一次來(lái)鄭州找瑞嫻,第二次來(lái)的時(shí)候她已經(jīng)離開(kāi)拉這座城恋技,我也以另外的一種身份出現(xiàn)在這里拇舀,如今我們都是實(shí)習(xí)生,面臨這...
    糖豆Y閱讀 319評(píng)論 0 0
  • 最好的時(shí)光、最好的我們 ——《最好的我們》感 ?洛枳愛(ài)盛淮南全世界都不知道...
    珺瑜閱讀 471評(píng)論 2 0