類似微信語(yǔ)音聊天界面,SingleInstance、SingleTask實(shí)際遇到的問(wèn)題

項(xiàng)目中有VOIP的需求,類似微信語(yǔ)音聊天的界面。這種類型的界面通常會(huì)要求可以最小化并保持運(yùn)行狀態(tài)。最小化自然想到懸浮窗,懸浮窗的實(shí)現(xiàn)以及注意事項(xiàng)不在本文討論范圍,本文討論的是怎樣讓Activity不可見(jiàn)但又不finish呢?

通過(guò)簡(jiǎn)單的搜索可以比較輕松的看到網(wǎng)上提供的實(shí)現(xiàn)方式:將Activity設(shè)置成singleTask,搭配taskAffinity屬性使用,目的是讓此Activity獨(dú)立運(yùn)行在一個(gè)task中。

說(shuō)一下Task,我們知道Android中每個(gè)Activity運(yùn)行在一個(gè)task中,由ActivityManager中的RunningTaskInfo統(tǒng)一管理。通常情況下,一個(gè)Application中的所有Activity都默認(rèn)是同一個(gè)task,除非在AndroidManifest.xml文件中對(duì)某Activity設(shè)置了taskAffinity,可以使此Activity運(yùn)行在指定名稱的task中。

由于接手項(xiàng)目時(shí)已經(jīng)是這種實(shí)現(xiàn)方式,大部分設(shè)備上也沒(méi)有問(wèn)題,達(dá)到了我們想要的效果,具體實(shí)現(xiàn)細(xì)節(jié)如下:

1、在AndroidManifest.xml中將ActivityVoip設(shè)置成singleTask,設(shè)置android:taskAffinity為"com.xx.xx.xx"。
2、加入屬性android:excludeFromRecents,讓該task不在最近任務(wù)列表中顯示。此處強(qiáng)調(diào)一下,如果剛啟動(dòng)ActivityVoip,馬上呼出最近任務(wù)界面還是能看到兩個(gè)task,一個(gè)是ActivityVoip界面所屬的task,一個(gè)是APP所在的默認(rèn)task,這是沒(méi)問(wèn)題的,官方有說(shuō)明。
3、點(diǎn)擊ActivityVoip中的最小化按鈕,生成懸浮窗的同時(shí)moveTaskToBack(true)將當(dāng)前task退到后臺(tái)。懸浮窗的權(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" />

以上,似乎完美實(shí)現(xiàn)了這個(gè)功能,可突然測(cè)試說(shuō)華為手機(jī)最小化通話頁(yè)面或者直接講應(yīng)用退到后臺(tái)后,再冷啟動(dòng)任意一個(gè)其他app都會(huì)導(dǎo)致通話斷開(kāi),懸浮窗消失!

此處吐槽一下國(guó)產(chǎn)系統(tǒng)特別是華為的UI,亂改什么Android,留下那么多坑。經(jīng)過(guò)測(cè)試,華為在launcher冷啟動(dòng)一個(gè)app時(shí)會(huì)將設(shè)置為android:excludeFromRecents="true"并且設(shè)置了taskAffinity的task干掉。。。

最佳的方案當(dāng)然是Activity和Voip狀態(tài)分離,Activity只負(fù)責(zé)展示,即使被無(wú)情kill掉,再啟動(dòng)時(shí)還是可以正確展示相關(guān)信息。

可如今想要最簡(jiǎn)單的方式解決這個(gè)問(wèn)題要怎么辦呢?經(jīng)過(guò)幾個(gè)小時(shí)的研究和嘗試,將singleTask改為singleInstance,去掉taskAffinity和excludeFromRecents屬性,也可以實(shí)現(xiàn)Activity獨(dú)立運(yùn)行的目的,但怎么可能沒(méi)有其他問(wèn)題呢?T_T 。當(dāng)啟動(dòng)ActivityVoip后,退到后臺(tái),再?gòu)淖罱蝿?wù)回到通話界面,按返回鍵頁(yè)面finish()后并沒(méi)有返回上個(gè)頁(yè)面,而是退到了系統(tǒng)Launcher上,給人的感覺(jué)像是app崩掉了,然而事實(shí)是并沒(méi)有。原因也比較簡(jiǎn)單,ActivityManager最頂端的task并不是主APP的task,解決方案自然想到finish()通話界面前召喚主app的task回來(lái)。代碼如下:

   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;
            }
        }
    }

還有沒(méi)有坑有待觀察。。。。

更新:

經(jīng)過(guò)測(cè)試SingleInstance也不能解決問(wèn)題。原因在于singleInstance啟動(dòng)模式在不同機(jī)型表現(xiàn)不同,有的機(jī)型會(huì)在最近任務(wù)中顯示兩個(gè)task,有的則顯示一個(gè)。顯示一個(gè)自然沒(méi)問(wèn)題,顯示兩個(gè)又要考慮在清單文件中添加android:excludeFromRecents="true"。然而雖然添加這個(gè)屬性后,華為手機(jī)冷啟動(dòng)其他應(yīng)用不會(huì)destroy這個(gè)頁(yè)面,但是最近任務(wù)中整個(gè)app的task都不見(jiàn)了,這也是singleInstance和singleTask的區(qū)別之一。

接下來(lái)還是研究一下微信的實(shí)現(xiàn)吧,首先通過(guò) adb命令打印出微信的Activity堆棧信息,打印之前最好清楚最近任務(wù),免得不好找。


before min window.png
min window.png

上面兩張圖是打印出來(lái)的最小化微信語(yǔ)音界面前后的堆棧信息,發(fā)現(xiàn)微信VideoActivity也就是通話界面的taskId和聊天頁(yè)面的是一樣的,也就是說(shuō)微信并沒(méi)有采用SingleTask和SingleInstance中任何一種啟動(dòng)方式?。?!而且最小化實(shí)際上就是finish了VideoActivity,所以。。也跟我想到的第一種解決方案一樣,最保險(xiǎn)的做法就是持久化voip的狀態(tài),Activity只是用來(lái)顯示。唉,前人挖坑后人跳~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容