保活在 Android 繞不開的話題,有些 APP 爲了保活煞費苦心。近來發現打開知乎,在近期任務卻看不到知乎,知乎是怎麼做到這麼強悍的。下面分享來自 NanBox 博客,來揭開知乎是如何做到在近期任務完美隱藏的:
不知道大家平時使用 APP 的時候,有沒有碰到過下面這種情況:
這是我在最近使用知乎的時候出現的,可以看到在任務列表裏面看不到知乎,但很明顯它還在運行中。你現在打開知乎看大概率是正常的,原因後面會提到。
通常要殺掉一個 APP 的進程,最直接的方法的就是在任務列表裏把對應的任務劃掉。於是,保活黑科技又增加了一種新思路:如果在任務列表裏把應用隱藏掉 ,那用戶不就殺不掉了?
事先說明一下,本文並不是教大家怎麼做保活,僅探討這是怎麼做的,並藉此聊一聊 Android 的多任務。而且對於這樣的體驗,我是真的被噁心到了。
怎麼殺掉它?
任務列表並不是殺掉 APP 的唯一途徑,我們先看看要怎樣才能殺掉這種應用。
adb
我們可以通過 adb shell ps 查看系統當前運行的所有進程,和預期的一樣,在裏面找到了還在運行中的知乎:
然後可以使用 adb shell am force-stop com.zhihu.android 強制殺掉知乎進程。
系統設置
對於普通用戶,也還是有辦法的。進入系統設置,在應用設置裏找到知乎,點擊強行停止:
但無論是哪種方法,都是比較麻煩的,真心希望大家不要這樣搞。
excludeFromRecents
其實,Android 是允許我們在任務列表裏隱藏的,而且很簡單,只要在清單中聲明瞭android:excludeFromRecents=“true” 就好了。
我們新建一個項目試一下,把 MainActivity 加上這個配置:
<activity
android:name=".MainActivity"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
這樣就能達到不在任務列表顯示的效果。
但僅僅如此還達不到知乎的效果,因爲首次打開它是有在任務列表顯示的。下面引入多任務的另一個概念。
taskAffinity
之所以叫任務列表,是因爲這裏顯示的是當前在執行的任務,而不是當前運行的應用。只不過在默認情況下,一個應用就對應一個任務。
每個任務會有一個 TaskAffinity,可以把它理解爲任務名,默認情況下 TaskAffinity 是應用的包名。我們可以用 taskAffinity 屬性給 Activity 配置不同的任務名,讓一個 APP 擁有多個任務。
無論是 excludeFromRecents 還是 taskAffinity,它們只對棧內的根 Activity 生效,其實它們作用的是任務棧 Task,而不是 Activity。
舉個例子,我們增加一個 SecondActivity,清單配置如下:
<activity
android:name=".MainActivity"
android:label="Task 1"
android:taskAffinity="com.nanbox.task1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:label="Task 2"
android:launchMode="singleTask"
android:taskAffinity="com.nanbox.task2" />
當兩個 Activity 都開啓後,任務列表就會出現兩個任務。
taskAffinity 經常會和 singleTask 搭配使用,當啓動一個 singleTask 的 Activity 時,系統會先比對當前的和新的 taskAffinity,如果不一致就會在一個新的 Task 裏啓動 Activity。
另外,不僅一個應用可以有多個任務,不同應用也可以屬於同一個任務,任務是可以跨進程的。這種使用場景應該比較少,這裏就不展開講了。
騷操作
基於上面的多任務,假如我們一個應用有兩個任務,一個可見一個不可見,用戶只能在任務列表裏殺掉可見的任務,不可見的任務還可以繼續跑,那豈不是可以一定程度上保活?
我們來試一下,還是上面的代碼,不過這次讓 Task 2 在任務列表中不可見:
<activity
android:name=".MainActivity"
android:label="Task 1"
android:taskAffinity="com.nanbox.task1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:excludeFromRecents="true"
android:label="Task 2"
android:launchMode="singleTask"
android:taskAffinity="com.nanbox.task2" />
我們把啓動過的 SecondActivity 存起來,在 MainActivity 中判斷,如果 SecondActivity 已經存在就直接啓動它,以便恢復到應用上一次的狀態:
Activity activity = ActivityProvider.getActivity();
if (activity != null) {
Intent intent = new Intent(this, activity.getClass());
startActivity(intent);
finishAndRemoveTask();
}
於是就有了這種效果:
GIF 重複播放不太好體現效果,感興趣的可以自己跑一下看看。
首次啓動可以在任務列表中看到 Task1,當啓動 Task2 並結束 Task1 之後,任務列表就變成空了,但點擊桌面應用可以恢復到正在運行的 Task2。
這也是爲什麼知乎首次啓動是正常的,用着用着纔可能出現這種情況。