1. 程式人生 > >Android 8.0 + Service開啟方式相容處理

Android 8.0 + Service開啟方式相容處理

Android 8.0 + ,對後臺服務進行了限制了。如果依然採用之前startService()方式。

會導致問題。

前後臺服務的一些區別:

類別 區別 應用
前臺服務 會在通知一欄顯示 ONGOING 的 Notification, 當服務被終止的時候,通知一欄的 Notification 也會消失,這樣對於使用者有一定的通知作用。 常見的如音樂播放服務
後臺服務 預設的服務即為後臺服務,即不會在通知一欄顯示 ONGOING 的 Notification。 當服務被終止的時候,使用者是看不到效果的。 某些不需要執行或終止提示的服務,如天氣更新,日期同步,郵件同步等。



有人可能會問,後臺服務我們可以自己建立 ONGOING 的 Notification 這樣就成為前臺服務嗎?答案是否定的,前臺服務是在做了上述工作之後需要呼叫 startForeground ( android 2.0 及其以後版本 )或 setForeground (android 2.0 以前的版本)使服務成為 前臺服務。這樣做的好處在於,當服務被外部強制終止掉的時候,ONGOING 的 Notification 任然會移除掉。

api 8.0+ 、9.0 關於服務的行為變更。

8.0

後臺執行限制

Android 8.0 為提高電池續航時間而引入的變更之一是,當您的應用進入已快取狀態時,如果沒有活動的元件,系統將解除應用具有的所有喚醒鎖。
此外,為提高裝置效能,系統會限制未在前臺執行的應用的某些行為。具體而言:
* 現在,在後臺執行的應用對後臺服務的訪問受到限制。
* 應用無法使用其清單註冊大部分隱式廣播(即,並非專門針對此應用的廣播)。
預設情況下,這些限制僅適用於針對 O 的應用。不過,使用者可以從 Settings 螢幕為任意應用啟用這些限制,即使應用並不是以 O 為目標平臺。
Android 8.0 還對特定函式做出了以下變更:
* 如果針對 Android 8.0 的應用嘗試在不允許其建立後臺服務的情況下使用 startService() 函式,則該函式將引發一個 IllegalStateException。
* 新的 Context.startForegroundService() 函式將啟動一個前臺服務。現在,即使應用在後臺執行,系統也允許其呼叫 Context.startForegroundService()。不過,應用必須在建立服務後的五秒內呼叫該服務的 startForeground() 函式。
如需瞭解詳細資訊,請參閱後臺執行限制。

9.0

前臺服務

針對 Android 9 或更高版本並使用前臺服務的應用必須請求 FOREGROUND_SERVICE 許可權。 這是普通許可權,因此,系統會自動為請求許可權的應用授予此許可權。
如果針對 Android 9 或更高版本的應用嘗試建立一個前臺服務且未請求 FOREGROUND_SERVICE,則系統會引發 SecurityException。

網友推薦的解決方式:

原來startService()需要根據sdk版本進行相容

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION
_CODES.O) { context.startForegroundService(intent); } else { context.startService(intent); }
**系統說明在呼叫 context.startForegroundService(intent);服務後5s內需要呼叫**
startForeground(1, notification);
根據網友提供方案,在 api =26 級別可以正常跑起來,但是在api =27 級別下,啟動直接崩潰,具體crash 異常日誌如下:
09-03 16:13:28.563 6124-6124/com.nuoyuan.nyd E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.nuoyuan.nyd, PID: 6124
    android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel= pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1768)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
這裡牽扯到android 8.0 關於 Notification 的行為變更 ###通知

在 Android 8.0 中,我們已重新設計通知,以便為管理通知行為和設定提供更輕鬆和更統一的方式。這些變更包括:

    Android 8.0 中的通知長按選單。

    圖 1. 使用者可以長按應用啟動器圖示以檢視 Android 8.0 中的通知。

  • 通知渠道:Android 8.0 引入了通知渠道,其允許您為要顯示的每種通知型別建立使用者可自定義的渠道。使用者介面將通知渠道稱之為通知類別。要了解如何實現通知渠道的資訊,請參閱通知渠道指南。
  • 通知標誌:Android 8.0 引入了對在應用啟動器圖示上顯示通知標誌的支援。通知標誌可反映某個應用是否存在與其關聯、並且使用者尚未予以清除也未對其採取行動的通知。通知標誌也稱為通知點。要了解如何調整通知標誌,請參閱通知標誌指南。
  • 休眠:使用者可以將通知置於休眠狀態,以便稍後重新顯示它。重新顯示時通知的重要程度與首次顯示時相同。應用可以移除或更新已休眠的通知,但更新休眠的通知並不會使其重新顯示。
  • 通知超時:現在,使用 建立通知時您可以設定超時。您可以使用此函式指定一個持續時間,超過該持續時間後,通知應取消。如果需要,您可以在指定的超時持續時間之前取消通知。
  • 通知設定:當您使用 Intent 從通知建立指向應用通知設定的連結時,您可以呼叫 setSettingsText() 來設定要顯示的文字。此係統可以提供以下 Extra 資料和 Intent,用於過濾應用必須向用戶顯示的設定:EXTRA_CHANNEL_IDNOTIFICATION_TAGNOTIFICATION_ID
  • 背景顏色:您現在可以設定和啟用通知的背景顏色。只能在使用者必須一眼就能看到的持續任務的通知中使用此功能。例如,您可以為與駕車路線或正在進行的通話有關的通知設定背景顏色。您還可以使用 設定所需的背景顏色。這樣做將允許您使用 啟用通知的背景顏色設定。
  • 訊息樣式:現在,使用 類的通知可在其摺疊形式中顯示更多內容。對於與訊息有關的通知,您應使用 類。您還可以使用新的 函式,通過向與訊息相關的通知新增歷史訊息為會話提供上下文。

根據上面提示,Notification 需要新增 channelId 才可以正常使用

如下修改,可以正常相容 api 級別 8.0 ,8.1 + 的service 正常開啟

public class UploadFilesIntentService extends IntentService {
    private static final String UPLOAD_FILE = "com.nuoyuan.statistic.action.UPLOAD_FILE";
    private static String loadUrlPath = "";
    private static SttcHeadParams mHeadParams;
    public static final String CHANNEL_ID_STRING = "nyd001";
    @Override
    public void onCreate() {
        super.onCreate();
        //適配8.0service
        NotificationManager notificationManager = (NotificationManager) MyApp.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel mChannel = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mChannel = new NotificationChannel(CHANNEL_ID_STRING, "諾秒貸", NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(mChannel);
            Notification notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
            startForeground(1, notification);
        }
    }

    public UploadFilesIntentService() {
        super("UploadFilesIntentService");
    }

    public static void startActionFoo(Context context, String loadPath, SttcHeadParams headParams) {
        Intent intent = new Intent(context, UploadFilesIntentService.class);
        intent.setAction(UPLOAD_FILE);
        mHeadParams = headParams;
        loadUrlPath = loadPath;
//開啟服務相容
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            context.startForegroundService(intent);
        } else {
            context.startService(intent);
        }
    }

……
……
…….
}