2020年了,Android后臺?;钸€有戲嗎?看我如何優(yōu)雅的實現(xiàn)!
Posted on 2019-12-27 14:51 Jack Jiang 閱讀(423) 評論(0) 編輯 收藏1、引言
對于移動端IM應(yīng)用和消息推送應(yīng)用的開發(fā)者來說,Android后臺?;钸@件事是再熟悉不過了。
自從Android P(即Android 8.0)出現(xiàn)以后,Android已經(jīng)從系統(tǒng)層面將后臺?;钸@條路給堵死了(詳見:《Android P正式版即將到來:后臺應(yīng)用?;睢⑾⑼扑偷恼嬲瑝?/a>》),曾今那些層出不窮的?;詈诳萍寄苡玫囊苍絹碓缴倭耍ㄔ斠姡骸?a target="_blank" style="color: #1d58d1; text-decoration-line: none;">全面盤點當(dāng)前Android后臺?;罘桨傅恼鎸嵾\行效果(截止2019年前)》。雖然可以自已對接廠商的ROOM級推送通道,但一方面各廠商的推送接口都不一樣(而且同一廠商不同的系統(tǒng)版本間也存在推送接口的兼容性問題),很不方便。另一方面要一家家引入各自的推送服務(wù)SDK包會讓APP變的很大,這讓APP的下載變的很不友好。
總之,Android應(yīng)用的后臺?;钤谀承﹫鼍跋?,還是有持續(xù)的需求。除了之前那些耳熟能詳?shù)谋;詈诳萍家酝?,在Android 9.0(甚至Android 10)時代,我們還有哪些?;罘椒梢杂??那么,請跟著本文作者的思路,看看更優(yōu)雅的后臺保活實現(xiàn)方法吧。
(本文同步發(fā)布于:http://www.52im.net/thread-2881-1-1.html)

2、關(guān)于作者
網(wǎng)名NanBox:畢業(yè)于華中科技大學(xué),現(xiàn)為"悅跑圈APP”高級Android開發(fā)工程師。主要負(fù)責(zé)公司 Android 項目,核心模塊的開發(fā)。涉及 GPS 定位、地圖、圖片編輯等功能。獨立開發(fā)了手表應(yīng)用項目。 在項目中應(yīng)入了 Flutter 跨平臺開發(fā)技術(shù),實現(xiàn)了原生和 Flutter 的混合開發(fā)。
本文作者樂于分享,平時會寫技術(shù)文章并分享在多個平臺,是掘金專欄作者的一員,文章總閱讀量超過 10 萬。在 GitHub 上有多個開源項目,多次在團(tuán)隊內(nèi)部進(jìn)行技術(shù)分享。是 Android 和 Flutter 官方中文文檔譯者。
3、相關(guān)文章
如果你想詳細(xì)了解目前Android平臺上后臺?;罴夹g(shù)的挑戰(zhàn),請閱讀:
如果你想回顧那些曾今出現(xiàn)的Android?;詈诳萍?,以下文章值得好好讀讀:
《全面盤點當(dāng)前Android后臺?;罘桨傅恼鎸嵾\行效果(截止2019年前)》
《應(yīng)用保活終極總結(jié)(一):Android6.0以下的雙進(jìn)程守護(hù)?;顚嵺`》
《應(yīng)用?;罱K極總結(jié)(二):Android6.0及以上的?;顚嵺`(進(jìn)程防殺篇)》
《應(yīng)用?;罱K極總結(jié)(三):Android6.0及以上的保活實踐(被殺復(fù)活篇)》
《Android進(jìn)程?;钤斀猓阂黄恼陆鉀Q你的所有疑問》
《Android端消息推送總結(jié):實現(xiàn)原理、心跳?;?、遇到的問題等》
《為何基于TCP協(xié)議的移動端IM仍然需要心跳保活機(jī)制?》
《微信團(tuán)隊原創(chuàng)分享:Android版微信后臺保活實戰(zhàn)分享(進(jìn)程?;钇?》
4、Android?;瞵F(xiàn)狀
我們知道,Android 系統(tǒng)會存在殺后臺進(jìn)程的情況,并且隨著系統(tǒng)版本的更新,殺進(jìn)程的力度還有越來越大的趨勢(見:《Android P正式版即將到來:后臺應(yīng)用?;?、消息推送的真正噩夢》)。
系統(tǒng)這種做法本身出發(fā)點是好的,因為可以節(jié)省內(nèi)存,降低功耗,也避免了一些流氓行為。
但有一部分應(yīng)用,應(yīng)用本身的使用場景就需要在后臺運行,用戶也是愿意讓它在后臺運行的,比如跑步類應(yīng)用、一些懶得對接廠商推送通道的IM應(yīng)用、消息推送資訊類應(yīng)用等。
一方面流氓軟件用各種流氓手段進(jìn)行保活,另一方面系統(tǒng)加大殺后臺的力度,導(dǎo)致我們一些真正需要在后臺運行的應(yīng)用被誤殺,苦不堪言。
5、優(yōu)雅的?;??
為了做到保活,出現(xiàn)了不少「黑科技」,比如 1 個像素的 Activity,播放無聲音頻,雙進(jìn)程互相守護(hù)等(可以讀讀這個系列:《應(yīng)用?;罱K極總結(jié)(一):Android6.0以下的雙進(jìn)程守護(hù)?;顚嵺`》、《應(yīng)用保活終極總結(jié)(二):Android6.0及以上的保活實踐(進(jìn)程防殺篇)》、《應(yīng)用?;罱K極總結(jié)(三):Android6.0及以上的保活實踐(被殺復(fù)活篇)》)。
這些做法可以說是很流氓了,甚至破壞了 Android 的生態(tài),好在隨著 Android 系統(tǒng)版本的更新,這些非常規(guī)的保活手段很多都已失效了。
對于那些確實需要在后臺運行的應(yīng)用,我們?nèi)绾巫龅絻?yōu)雅的?;钅??
6、加入后臺運行白名單,可以優(yōu)雅的實現(xiàn)?;?/h1>
從 Android 6.0 開始,系統(tǒng)為了省電增加了休眠模式,系統(tǒng)待機(jī)一段時間后,會殺死后臺正在運行的進(jìn)程。但系統(tǒng)會有一個后臺運行白名單,白名單里的應(yīng)用將不會受到影響,在原生系統(tǒng)下,通過:「設(shè)置」 - 「電池」 - 「電池優(yōu)化」 - 「未優(yōu)化應(yīng)用」,可以看到這個白名單。
通常會看到下面這兩位:

下次被產(chǎn)品說「 XXX 都可以?;睿瑸槭裁次覀儾恍?!」的時候,你就知道怎么懟回去了。大廠通過和手機(jī)廠商的合作,將自己的應(yīng)用默認(rèn)加入到白名單中。如果你在一個能談成這種合作的大廠,也就不用往下看了。
好在系統(tǒng)還沒有拋棄我們,允許我們申請把應(yīng)用加入白名單。
首先,在 AndroidManifest.xml 文件中配置一下權(quán)限:
<uses-permissionandroid:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
可以通過以下方法,判斷我們的應(yīng)用是否在白名單中:
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean isIgnoringBatteryOptimizations() {
boolean isIgnoring = false;
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if(powerManager != null) {
isIgnoring = powerManager.isIgnoringBatteryOptimizations(getPackageName());
}
return isIgnoring;
}
如果不在白名單中,可以通過以下代碼申請加入白名單:
@RequiresApi(api = Build.VERSION_CODES.M)
public void requestIgnoreBatteryOptimizations() {
try{
Intent intent = newIntent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:"+ getPackageName()));
startActivity(intent);
} catch(Exception e) {
e.printStackTrace();
}
}
申請時,應(yīng)用上會出現(xiàn)這樣一個窗口:

可以看到,這個系統(tǒng)彈窗會有影響電池續(xù)航的提醒,所以如果想讓用戶點允許,必須要有相關(guān)的說明。如果要判斷用戶是否點擊了允許,可以在申請的時候調(diào)用 startActivityForResult,在 onActivityResult 里再判斷一次是否在白名單中。
7、加入后臺運行白名單的多廠商適配方法
7.1 基本說明
Android 開發(fā)的一個難點在于,各大手機(jī)廠商對原生系統(tǒng)進(jìn)行了不同的定制,導(dǎo)致我們需要進(jìn)行不同的適配,后臺管理就是一個很好的體現(xiàn)。幾乎各個廠商都有自己的后臺管理,就算應(yīng)用加入了后臺運行白名單,仍然可能會被廠商自己的后臺管理干掉。
如果能把應(yīng)用加入廠商系統(tǒng)的后臺管理白名單,可以進(jìn)一步降低進(jìn)程被殺的概率。不同的廠商在不同的地方進(jìn)行設(shè)置,一般是在各自的「手機(jī)管家」,但更難的是,就算同一個廠商的系統(tǒng),不同的版本也可能是在不同地方設(shè)置。
最理想的做法是,我們根據(jù)不同手機(jī),甚至是不同的系統(tǒng)版本,給用戶呈現(xiàn)一個圖文操作步驟,并且提供一個按鈕,直接跳轉(zhuǎn)到指定頁面進(jìn)行設(shè)置。但需要對每個廠商每個版本進(jìn)行適配,工作量是比較大的。我使用真機(jī)測試了大部分主流 Android 廠商的手機(jī)后,整理出了部分手機(jī)的相關(guān)資料。
首先我們可以定義這樣兩個方法:
/**
* 跳轉(zhuǎn)到指定應(yīng)用的首頁
*/
private void showActivity(@NonNull String packageName) {
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
startActivity(intent);
}
/**
* 跳轉(zhuǎn)到指定應(yīng)用的指定頁面
*/
private void showActivity(@NonNull String packageName, @NonNull String activityDir) {
Intent intent = new Intent();
intent.setComponent(newComponentName(packageName, activityDir));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
以下是部分手機(jī)的廠商判斷,跳轉(zhuǎn)方法及對應(yīng)設(shè)置步驟,跳轉(zhuǎn)方法不保證在所有版本上都能成功跳轉(zhuǎn),都需要加 try catch。
7.2 華為
廠商判斷:
public boolean isHuawei() {
if(Build.BRAND == null) {
return false;
} else{
return Build.BRAND.toLowerCase().equals("huawei") || Build.BRAND.toLowerCase().equals("honor");
}
}
跳轉(zhuǎn)華為手機(jī)管家的啟動管理頁:
private void goHuaweiSetting() {
try{
showActivity("com.huawei.systemmanager",
"com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
} catch(Exception e) {
showActivity("com.huawei.systemmanager",
"com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
}
}
操作步驟:應(yīng)用啟動管理 -> 關(guān)閉應(yīng)用開關(guān) -> 打開允許自啟動。
7.3 小米
廠商判斷:
public static boolean isXiaomi() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("xiaomi");
}
跳轉(zhuǎn)小米安全中心的自啟動管理頁面:
private void goXiaomiSetting() {
showActivity("com.miui.securitycenter",
"com.miui.permcenter.autostart.AutoStartManagementActivity");
}
操作步驟:授權(quán)管理 -> 自啟動管理 -> 允許應(yīng)用自啟動。
7.4 OPPO
廠商判斷:
public static boolean isOPPO() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("oppo");
}
跳轉(zhuǎn) OPPO 手機(jī)管家:
private void goOPPOSetting() {
try{
showActivity("com.coloros.phonemanager");
} catch(Exception e1) {
try{
showActivity("com.oppo.safe");
} catch(Exception e2) {
try{
showActivity("com.coloros.oppoguardelf");
} catch(Exception e3) {
showActivity("com.coloros.safecenter");
}
}
}
}
操作步驟:權(quán)限隱私 -> 自啟動管理 -> 允許應(yīng)用自啟動。
7.5 VIVO
廠商判斷:
public static boolean isVIVO() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("vivo");
}
跳轉(zhuǎn) VIVO 手機(jī)管家:
private void goVIVOSetting() {
showActivity("com.iqoo.secure");
}
操作步驟:權(quán)限管理 -> 自啟動 -> 允許應(yīng)用自啟動。
7.6 魅族
廠商判斷:
public static boolean isMeizu() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("meizu");
}
跳轉(zhuǎn)魅族手機(jī)管家:
private void goMeizuSetting() {
showActivity("com.meizu.safe");
}
操作步驟:權(quán)限管理 -> 后臺管理 -> 點擊應(yīng)用 -> 允許后臺運行。
7.7 三星
廠商判斷:
public static boolean isSamsung() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("samsung");
}
跳轉(zhuǎn)三星智能管理器:
private void goSamsungSetting() {
try{
showActivity("com.samsung.android.sm_cn");
} catch(Exception e) {
showActivity("com.samsung.android.sm");
}
}
操作步驟:自動運行應(yīng)用程序 -> 打開應(yīng)用開關(guān) -> 電池管理 -> 未監(jiān)視的應(yīng)用程序 -> 添加應(yīng)用。
7.8 樂視
廠商判斷:
public static boolean isLeTV() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("letv");
}
跳轉(zhuǎn)樂視手機(jī)管家:
private void goLetvSetting() {
showActivity("com.letv.android.letvsafe",
"com.letv.android.letvsafe.AutobootManageActivity");
}
操作步驟:自啟動管理 -> 允許應(yīng)用自啟動。
7.9 錘子
廠商判斷:
public static boolean isSmartisan() {
return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("smartisan");
}
跳轉(zhuǎn)手機(jī)管理:
private void goSmartisanSetting() {
showActivity("com.smartisanos.security");
}
操作步驟:權(quán)限管理 -> 自啟動權(quán)限管理 -> 點擊應(yīng)用 -> 允許被系統(tǒng)啟動。
8、友商致敬?
在之前做的跑步應(yīng)用中,我在設(shè)置里增加了一個權(quán)限設(shè)置頁面,將上面提到的設(shè)置放在這里面。
最近發(fā)現(xiàn)友商某咚也跟進(jìn)了,圖 1 是我們做的,圖 2 是某咚做的:

某咚從設(shè)計、從我寫的不夠好的文案,甚至是我從十幾臺手機(jī)上一張一張截下來的圖,進(jìn)行了全方位的致敬。感謝某咚的認(rèn)可,但最近在某個發(fā)布會上聽到這么一句話:在致敬的同時,能不能說一句謝謝?
某咚的致敬,一方面說明了目前確實存在進(jìn)程容易被殺,?;铍y度大的問題,另一方面也說明了這種引導(dǎo)用戶進(jìn)行白名單設(shè)置的手段是有效的。
附錄:更多相關(guān)技術(shù)文章
《應(yīng)用保活終極總結(jié)(一):Android6.0以下的雙進(jìn)程守護(hù)?;顚嵺`》
《應(yīng)用?;罱K極總結(jié)(二):Android6.0及以上的?;顚嵺`(進(jìn)程防殺篇)》
《應(yīng)用保活終極總結(jié)(三):Android6.0及以上的保活實踐(被殺復(fù)活篇)》
《Android進(jìn)程保活詳解:一篇文章解決你的所有疑問》
《Android端消息推送總結(jié):實現(xiàn)原理、心跳?;?、遇到的問題等》
《為何基于TCP協(xié)議的移動端IM仍然需要心跳保活機(jī)制?》
《微信團(tuán)隊原創(chuàng)分享:Android版微信后臺?;顚崙?zhàn)分享(進(jìn)程保活篇)》
《微信團(tuán)隊原創(chuàng)分享:Android版微信后臺保活實戰(zhàn)分享(網(wǎng)絡(luò)?;钇?》
《移動端IM實踐:實現(xiàn)Android版微信的智能心跳機(jī)制》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
《Android P正式版即將到來:后臺應(yīng)用?;?、消息推送的真正噩夢》
《全面盤點當(dāng)前Android后臺保活方案的真實運行效果(截止2019年前)》
《一文讀懂即時通訊應(yīng)用中的網(wǎng)絡(luò)心跳包機(jī)制:作用、原理、實現(xiàn)思路等》
《融云技術(shù)分享:融云安卓端IM產(chǎn)品的網(wǎng)絡(luò)鏈路?;罴夹g(shù)實踐》
《正確理解IM長連接的心跳及重連機(jī)制,并動手實現(xiàn)(有完整IM源碼)》
《2020年了,Android后臺保活還有戲嗎?看我如何優(yōu)雅的實現(xiàn)!》
>> 更多同類文章 ……
(本文同步發(fā)布于:http://www.52im.net/thread-2881-1-1.html)
作者:Jack Jiang (點擊作者姓名進(jìn)入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時通訊開發(fā)交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時是【原創(chuàng)Java
Swing外觀工程BeautyEye】和【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文
歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處(也可前往 我的52im.net 找到我)。