項目中有VOIP的需求,類似微信語音聊天的界面蚓耽。這種類型的界面通常會要求可以最小化并保持運行狀態(tài)渠牲。最小化自然想到懸浮窗,懸浮窗的實現(xiàn)以及注意事項不在本文討論范圍步悠,本文討論的是怎樣讓Activity不可見但又不finish呢签杈?
通過簡單的搜索可以比較輕松的看到網(wǎng)上提供的實現(xiàn)方式:將Activity設(shè)置成singleTask,搭配taskAffinity屬性使用贤徒,目的是讓此Activity獨立運行在一個task中芹壕。
說一下Task,我們知道Android中每個Activity運行在一個task中接奈,由ActivityManager中的RunningTaskInfo統(tǒng)一管理。通常情況下通孽,一個Application中的所有Activity都默認(rèn)是同一個task序宦,除非在AndroidManifest.xml文件中對某Activity設(shè)置了taskAffinity,可以使此Activity運行在指定名稱的task中背苦。
由于接手項目時已經(jīng)是這種實現(xiàn)方式互捌,大部分設(shè)備上也沒有問題,達(dá)到了我們想要的效果行剂,具體實現(xiàn)細(xì)節(jié)如下:
1秕噪、在AndroidManifest.xml中將ActivityVoip設(shè)置成singleTask,設(shè)置android:taskAffinity為"com.xx.xx.xx"厚宰。
2腌巾、加入屬性android:excludeFromRecents遂填,讓該task不在最近任務(wù)列表中顯示。此處強調(diào)一下澈蝙,如果剛啟動ActivityVoip吓坚,馬上呼出最近任務(wù)界面還是能看到兩個task,一個是ActivityVoip界面所屬的task灯荧,一個是APP所在的默認(rèn)task礁击,這是沒問題的,官方有說明逗载。
3哆窿、點擊ActivityVoip中的最小化按鈕,生成懸浮窗的同時moveTaskToBack(true)將當(dāng)前task退到后臺厉斟。懸浮窗的權(quán)限注意控制更耻。
<activity
android:name="ui.ActivityVoip"
android:exported="false"
android:launchMode="singleTask"
android:excludeFromRecents="true"
android:taskAffinity="com.xx.xx.xx"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
以上,似乎完美實現(xiàn)了這個功能捏膨,可突然測試說華為手機最小化通話頁面或者直接講應(yīng)用退到后臺后秧均,再冷啟動任意一個其他app都會導(dǎo)致通話斷開,懸浮窗消失号涯!
此處吐槽一下國產(chǎn)系統(tǒng)特別是華為的UI目胡,亂改什么Android,留下那么多坑链快。經(jīng)過測試誉己,華為在launcher冷啟動一個app時會將設(shè)置為android:excludeFromRecents="true"并且設(shè)置了taskAffinity的task干掉。域蜗。巨双。
最佳的方案當(dāng)然是Activity和Voip狀態(tài)分離,Activity只負(fù)責(zé)展示霉祸,即使被無情kill掉筑累,再啟動時還是可以正確展示相關(guān)信息。
可如今想要最簡單的方式解決這個問題要怎么辦呢丝蹭?經(jīng)過幾個小時的研究和嘗試慢宗,將singleTask改為singleInstance,去掉taskAffinity和excludeFromRecents屬性奔穿,也可以實現(xiàn)Activity獨立運行的目的镜沽,但怎么可能沒有其他問題呢?T_T 贱田。當(dāng)啟動ActivityVoip后缅茉,退到后臺,再從最近任務(wù)回到通話界面男摧,按返回鍵頁面finish()后并沒有返回上個頁面蔬墩,而是退到了系統(tǒng)Launcher上译打,給人的感覺像是app崩掉了,然而事實是并沒有筹我。原因也比較簡單扶平,ActivityManager最頂端的task并不是主APP的task,解決方案自然想到finish()通話界面前召喚主app的task回來蔬蕊。代碼如下:
private void moveAppToFront() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> recentList = am.getRunningTasks(30);
recentList.remove(0); //去掉當(dāng)前 Activity
for (ActivityManager.RunningTaskInfo info : recentList) {
if (info.topActivity.getPackageName().equals(getPackageName())) {
am.moveTaskToFront(info.id, 0);
return;
}
}
}
還有沒有坑有待觀察结澄。。岸夯。麻献。
更新:
經(jīng)過測試SingleInstance也不能解決問題。原因在于singleInstance啟動模式在不同機型表現(xiàn)不同猜扮,有的機型會在最近任務(wù)中顯示兩個task勉吻,有的則顯示一個。顯示一個自然沒問題旅赢,顯示兩個又要考慮在清單文件中添加android:excludeFromRecents="true"齿桃。然而雖然添加這個屬性后,華為手機冷啟動其他應(yīng)用不會destroy這個頁面煮盼,但是最近任務(wù)中整個app的task都不見了短纵,這也是singleInstance和singleTask的區(qū)別之一。
接下來還是研究一下微信的實現(xiàn)吧僵控,首先通過 adb命令打印出微信的Activity堆棧信息香到,打印之前最好清楚最近任務(wù),免得不好找报破。
上面兩張圖是打印出來的最小化微信語音界面前后的堆棧信息悠就,發(fā)現(xiàn)微信VideoActivity也就是通話界面的taskId和聊天頁面的是一樣的,也就是說微信并沒有采用SingleTask和SingleInstance中任何一種啟動方式3湟住9Fⅰ!而且最小化實際上就是finish了VideoActivity蔽氨,所以藐唠。。也跟我想到的第一種解決方案一樣鹉究,最保險的做法就是持久化voip的狀態(tài),Activity只是用來顯示踪宠。唉自赔,前人挖坑后人跳~