介紹
遠(yuǎn)程View捞镰,它表示的是一個View結(jié)構(gòu),它可以在其他進(jìn)程中顯示毙替,為了跨進(jìn)程更新它的界面岸售,RemoteViews提供了一組基礎(chǔ)的操作來實現(xiàn)這個效果。
RemoteViews在Android中的使用場景有兩種:
- 通知欄
- 桌面小部件
桌面小部件
- 定義小部件布局
- 定義小部件配置信息< appwidget-provider
屬性 | 含義 |
---|---|
android:initialLayout | 指定小部件的初始化布局 |
android:minHeight | 小部件最小高度 |
android:minWidth | 小部件最小寬度 |
android:previewImage | 小部件列表顯示的圖標(biāo) |
android:updatePeriodMillis | 小部件自動更新的周期 |
android:widgetCategory | 小部件顯示的位置厂画,home_screen表示只在桌面上顯示 |
- 定義小部件的實現(xiàn)類,extends AppWidgetProvider
- 清單文件聲明
<receiver android:name=".CustomAppWidgetProvider">
<meta-data
android:name="android.appwidget.provider" // name固定
android:resource="@xml/app_widget_provider_info" /> // xml
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> // 必須加的凸丸,它作為小部件的標(biāo)識存在
<action android:name="com.shenhuniurou.appwidgetprovider.click.one" /> // 其他action對應(yīng)點擊動作
<action android:name="com.shenhuniurou.appwidgetprovider.click.two" />
<action android:name="com.shenhuniurou.appwidgetprovider.click.three" />
<action android:name="com.shenhuniurou.appwidgetprovider.click.four" />
</intent-filter>
</receiver>
總結(jié):
- 當(dāng)小部件一被添加到桌面時會調(diào)用Provider中的onUpdate方法,在這個方法中我們會通過AppWidgetManager去更新小部件的界面
- 通過RemoteViews來操作木羹,setOnClickPendingIntent給每個按鈕設(shè)置了點擊時會發(fā)送的廣播動作甲雅,而在清單文件中我們聲明小部件時已經(jīng)將這些廣播動作都加到intent-filter解孙,所以當(dāng)我們點擊桌面上該小部件中的某個按鈕時,就會發(fā)送對應(yīng)的廣播抛人,而小部件監(jiān)聽了這個廣播弛姜,接收到廣播后再onReceive方法中根據(jù)動作來分別處理點擊事件。
- 對小部件的一些其他操作方法(比如onEnabled妖枚、onDisabled廷臼、onDeleted)的廣播也會在onReceive中接收到,然后分發(fā)給不同的方法绝页。
PendingIntent
將要發(fā)生的意圖荠商,和Intent的區(qū)別就在于一個是立即執(zhí)行的一個是在未來某個時候執(zhí)行。(通知中點擊通知時跳轉(zhuǎn)頁面)
給RemoteViews設(shè)置點擊事件续誉,就必須使用PendingIntent懊渡,通過setOnClickPendingIntent方法來設(shè)置涕蜂。
PendingIntent是通過send和cancel方法來發(fā)送和取消待執(zhí)行的Intent专酗。
PendingIntent支持三種待定意圖:
啟動activity(常見的通知)
啟動Activity它有兩種滥比,啟動單個和啟動多個,當(dāng)使用getActivities時臼隔,實際上啟動的是Intent數(shù)組中最后一個activity嘹裂,如果要讓最后一個activity返回時不退出app而是退回到上一個activity,實現(xiàn)方式可參照我上面第一個按鈕的點擊處理摔握。啟動Service
發(fā)送廣播
PendingIntent 方法參數(shù)
getActivity寄狼、getService、getBroadcast這三個方法的參數(shù)意義都是相同的氨淌,第一個上下文泊愧,第三個待定的意圖,第二個requestCode表示PendingIntent發(fā)送方的請求碼盛正,多數(shù)情況下設(shè)置為0即可拼卵,另外requestCode會影響到第四個參數(shù)flags的效果。flags這個標(biāo)志位表示執(zhí)行效果蛮艰。
PendingIntent的匹配規(guī)則
如果兩個PendingIntent它們內(nèi)部的Intent相同,且requestCode也相同雀彼,那么這兩個PendingIntent就是相同的壤蚜;
Intent相同的情況
如果兩個Intent的ComponentName和intent-filter都相同,那么這兩個Intent就是相同的
(Extras不參與Intent的匹配過程徊哑,就是它不同袜刷,只要ComponentName和intent-filter相同,Intent都算相同的莺丑。)
flags 執(zhí)行效果
FLAG_ONE_SHOT:表示當(dāng)前描述的PendingIntent只能被使用一次著蟹,然后它就會自動cancel墩蔓,如果后續(xù)還有相同的PendingIntent,那么它們的send方法就會調(diào)用失敗萧豆。如果通知欄消息使用這種標(biāo)記位奸披,同類型的通知就只會被打開一次,后續(xù)的通知將無法點開涮雷。
FLAG_NO_CREATE:表示當(dāng)前描述的PendingIntent不會主動創(chuàng)建阵面,如果當(dāng)前PendingIntent之前不存在,那么getActivities等這些方法會直接返回null洪鸭,獲取PendingIntent失敗样刷。它無法單獨使用。
FLAG_CANCEL_CURRENT:表示當(dāng)前描述的PendingIntent如果已經(jīng)存在览爵,就cancel它置鼻,然后系統(tǒng)會創(chuàng)建一個新的。
FLAG_UPDATE_CURRENT:表示當(dāng)前描述的PendingIntent如果已經(jīng)存在蜓竹,那么它會被更新箕母,內(nèi)部的Intent中的Extras也會被更新。
RemoteViews的內(nèi)部機(jī)制
通知欄和桌面小部件分別由NotificationManager和AppWidgetManager來管理的梅肤,而NotificationManager和AppWidgetManager是通過Binder分別和SystemServer進(jìn)程中的NotificationManagerService以及AppWidgetService進(jìn)行通信司蔬。
因此,通知欄和桌面小部件中的布局文件實際上是在NotificationManagerService和AppWidgetService中被加載的姨蝴,而他們運行在SystemServer中俊啼,這其實已經(jīng)和我們自己的app進(jìn)程構(gòu)成了跨進(jìn)程通信。
理論分析
- 首先RemoteViews會通過Binder傳遞到SystemServer進(jìn)程
(因為RemoteViews實現(xiàn)了Parcelable接口左医,可以跨進(jìn)程傳輸) - 系統(tǒng)會根據(jù)RemoteViews中的包名等信息去獲取到該app的資源
- 然后通過LayoutInflater去加載RemoteViews中的布局文件授帕。
在SystemServer進(jìn)程中加載后的布局文件是一個普通的View,只不過對于我們的app進(jìn)程來說浮梢,它是一個遠(yuǎn)程View也就是RemoteViews跛十。 - 接著系統(tǒng)會對View執(zhí)行一系列界面更新任務(wù)
這些任務(wù)就是之前我們通過set方法提交的,set方法對View的更新操作并不是立刻執(zhí)行的
在RemoteViews內(nèi)部會記錄所有的更新操作秕硝,具體的執(zhí)行要等到RemoteViews被完全加載以后芥映,這樣RemoteViews就可以在SystemServer中進(jìn)程中顯示了,這就是我們所看到的通知欄消息和桌面小部件远豺。
當(dāng)需要更新RemoteViews時奈偏,我們又需要調(diào)用一系列set方法通過NotificationManager和AppWidgetManager來提交更新任務(wù),具體更新操作也是在SystemServer進(jìn)程中完成的躯护。
理論上講系統(tǒng)完全可以通過Binder去支持所有的View和View操作惊来,但是這樣做代價太大,View的方法太多了棺滞,另外大量的IPC操作會影響效率裁蚁。
為了解決這個問題矢渊,系統(tǒng)并沒有通過Binder去直接支持View的跨進(jìn)程訪問,而是提供了一個Action的概念枉证。
Action代表一個View操作矮男,Action同樣實現(xiàn)了Parcelable接口。系統(tǒng)首先將View操作封裝到Action對象并將這些對象跨進(jìn)程傳輸?shù)竭h(yuǎn)程進(jìn)程刽严,接著在遠(yuǎn)程進(jìn)程中執(zhí)行Action對象中的具體操作昂灵。
在我們的app中每調(diào)用一次set方法,RemoteViews中就會添加一個對應(yīng)的Action對象舞萄,當(dāng)我們通過NotificationManager和AppWidgetManager來提交我們的更新時眨补,這些Action對象就會傳輸?shù)竭h(yuǎn)程進(jìn)程并在遠(yuǎn)程進(jìn)程中依次執(zhí)行。
遠(yuǎn)程進(jìn)程通過RemoteViews的apply方法來進(jìn)行View的更新操作倒脓,apply方法內(nèi)部是去遍歷所有的Action對象并調(diào)用它們的apply方法撑螺,具體的View更新操作是由Action對象的apply方法來完成。
好處
- 不需要定義大量的Binder接口
- 通過在遠(yuǎn)程進(jìn)程中批量執(zhí)行RemoteViews的更新操作從而避免了大量的IPC操作崎弃,這就提高了程序的性能甘晤。
RemoteViews的優(yōu)缺點
優(yōu)點:實際開發(fā)中,跨進(jìn)程通信我們可以選擇AIDL去實現(xiàn)饲做,但是如果對界面的更新比較頻繁线婚,這時會有效率問題,而且AIDL接口可能會變得很復(fù)雜盆均,但如果采用RemoteViews來實現(xiàn)就沒有這個問題了
缺點:僅支持一些常見的View塞弊,而對于自定義View是不支持的。