前言
google在更新Android8.0后對Service的權限越發收緊。導致目前想要啟動服務必需實現服務的前台化(否則在服務啟動5秒后,系統將自動報錯)。下面我們就來看看如何在8.0上啟動服務。
看看8.0之前的版本怎么啟動Service
在看8.0啟動服務的方法之前,我們先看看8.0之前是怎么啟動服務的。這樣可以對比,也可以理解增加了那些部分。
1.在Activity啟動服務:
Intent intent = new Intent(MainActivity.this,MainService.class); startService(intent);
8.0之前是使用startService 直接啟動服務的。后續服務就可以在后台運行了
2.將服務前台化:
雖然8.0之前如果沒有明確需求,是可以不需要讓服務前台化通知欄顯示的,但是我們為了對比實現下8.0之前的服務前台化。
public void onCreate() { super.onCreate(); Notification notification = new Notification.Builder(this) .setContentTitle("主服務")//設置標題 .setContentText("運行中...")//設置內容 .setWhen(System.currentTimeMillis())//設置創建時間 .setSmallIcon(R.mipmap.ic_launcher)//設置狀態欄圖標 .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))//設置通知欄圖標 .build(); startForeground(1,notification); }
可以服務啟動后,可以在服務的onCreate()里直接實現服務前台化。
8.0版本怎么啟動Service
1.在Activity里啟動服務
Intent intent = new Intent(MainActivity.this,MainService.class); startForegroundService(intent);
可以看到啟動的方法變成了startForegroundService();
2.創建8.0版本必需實現的服務前台化
private static final String CHANNEL_ID = "NFCService"; public void onCreate() { super.onCreate(); NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); NotificationChannel Channel = new NotificationChannel(CHANNEL_ID,"主服務",NotificationManager.IMPORTANCE_HIGH); Channel.enableLights(true);//設置提示燈 Channel.setLightColor(Color.RED);//設置提示燈顏色 Channel.setShowBadge(true);//顯示logo Channel.setDescription("ytzn");//設置描述 Channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); //設置鎖屏可見 VISIBILITY_PUBLIC=可見 manager.createNotificationChannel(Channel); Notification notification = new Notification.Builder(this) .setChannelId(CHANNEL_ID) .setContentTitle("主服務")//標題 .setContentText("運行中...")//內容 .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher)//小圖標一定需要設置,否則會報錯(如果不設置它啟動服務前台化不會報錯,但是你會發現這個通知不會啟動),如果是普通通知,不設置必然報錯 .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)) .build(); startForeground(1,notification);//服務前台化只能使用startForeground()方法,不能使用 notificationManager.notify(1,notification); 這個只是啟動通知使用的,使用這個方法你只需要等待幾秒就會發現報錯了 }
8.0版本后多了一個需要創建的NotificationChannel,在Notification里多了一個需要設置的setChannelId();
8.0版本啟動服務注意點:
- 請注意8.0版本只能使用startForegroundService()啟動服務,不能在使用startService()
- 請注意在new NotificationChannel(CHANNEL_ID,"主服務",NotificationManager.IMPORTANCE_HIGH); 的第一個參數id為Channel的通道id需要與Notification的setChannelId()設置的id一致,否則會報前台服務無效的異常。
- 注意啟動服務前台化通知的方法是startForeground(),如果使用notificationManager.notify(),將不會關聯你啟動的服務,系統會將它作為一個普通通知處理
最后了解什么是NotificationChannel
Android O 引入了 通知渠道(Notification Channels),以提供統一的系統來幫助用戶管理通知,如果是針對 android O 為目標平台時,必須實現一個或者多個通知渠道,以向用戶顯示通知。若並不以 Android O 為目標平台,當應用運行在 android O 設備上時,其行為將與運行在 Android 7.0 上時相同。
開發者可以為需要發送的每個不同的通知類型創建一個通知渠道。還可以創建通知渠道來反映應用的用戶做出的選擇。例如,可以為聊天應用的用戶創建的每個聊天組建立單獨的通知渠道。
Android O 的用戶可以使用一致的系統 UI 管理大多數與通知有關的設置。所有發布至通知渠道的通知都具有相同的行為。當用戶修改任何下列特性的行為時,修改將作用於通知渠道:
- 重要性
- 聲音
- 光
- 振動
- 在鎖屏上顯示
- 替換免打擾模式
通知優先級和重要性
Android O 棄用了為單個通知設置優先級的功能。創建通知渠道時可以設置建議重要性級別。為通知渠道指定的重要性級別適用於發布至該渠道的所有通知消息。可以配置五個級別中的一個,這些級別代表着通知渠道可以打斷用戶的程度,范圍是 IMPORTANCE_NONE(0)至 IMPORTANCE_HIGH(4)。默認重要性級別為 3:在所有位置顯示,發出提示音,但不會對用戶產生視覺干擾。創建通知渠道后,只有系統可以修改其重要性。用戶可以在設置中找到。
創建通知渠道
要創建通知渠道,請執行下列操作:
- 構建一個在軟件包內具有唯一 ID 的通知渠道對象。
- 為該通知渠道對象配置所需的任何初始設置(例如提示音以及對用戶可見的可選說明)。
- 將通知渠道對象提交到通知管理器。
如果試圖使用初始值創建的通知渠道已存在,不會執行任何操作,因此啟動應用時可以放心地執行以上步驟序列。
創建通知渠道組
如果應用支持多個帳戶,則可為每個帳戶創建一個通知渠道組。通知渠道組用於對一款應用內的多個同名通知渠道進行管理。例如,一款社交網絡應用可能提供面向個人帳戶以及企業帳戶的支持。在此情境下,每個帳戶可能都需要多個功能和名稱相同的通知渠道。
一個包括 2 個通知渠道的個人帳戶:
- 帖子新增評論的通知。
- 聯系人推薦帖子的通知。
一個包括 2 個通知渠道的企業帳戶:
- 帖子新增評論的通知。
- 聯系人推薦帖子的通知。
在本例中,將與每個用戶帳戶相關的通知渠道組織成專用組可確保用戶能在 Settings 中輕松地進行區分。每個通知渠道組都必須在軟件包內具有唯一 ID,並具有用戶可見的名稱。下面這段代碼演示了如何創建通知渠道組。
// 通知渠道組的id. String group = "my_group_01"; // 用戶可見的通知渠道組名稱. CharSequence name = getString(R.string.group_name); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.createNotificationChannelGroup(new NotificationChannelGroup(group, name));
新建渠道組后,便可調用 setGroup()將某個新渠道關聯到該組。注意,只能在將渠道提交給通知管理器之前修改通知渠道與組之間的關聯。
創建通知
要創建通知,請調用 Notification.Builder.build(),它返回的 Notification對象包含的指定值。要發出通知,請通過調用 [notify()](https://developer.android.google.cn/reference/android/app/NotificationManager.html#notify(int, android.app.Notification))將 Notification對象傳遞給系統。
創建的通知Notification對象必須包含以下內容:
- 小圖標,由 setSmallIcon()設置
- 標題,由 setContentTitle()設置
- 詳細文本,由 setContentText()設置
- 有效的通知渠道 ID,由 setChannelId()設置
如果 應用是以 Android O 為目標平台並且在不指定有效通知渠道的情況下發布通知,那么通知將無法發布,系統會記錄錯誤。
注:可以在 Android O 中啟用一個新設置,當針對 Android O 的應用試圖在沒有通知渠道的情況下發布時,以 toast 形式顯示屏幕警告。要為運行 Android O 的開發設備啟用該設置,請轉到 Settings > Developer options,然后打開 Show notification channel warnings。
向渠道發布通知
下面這段代碼說明如何向通知渠道發布簡單通知。請注意,代碼利用渠道的 ID 將通知與通知渠道關聯起來。
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 為該通知設置一個id int notifyID = 1; // 通知渠道的id String CHANNEL_ID = "my_channel_01"; // Create a notification and set the notification channel. Notification notification = new Notification.Builder(MainActivity.this) .setContentTitle("New Message") .setContentText("You've received new messages.") .setSmallIcon(R.drawable.ic_notify_status) .setChannelId(CHANNEL_ID) .build(); // 發布通知 mNotificationManager.notify(id, notification);
讀取通知渠道設置
用戶可以修改通知渠道的設置,包括振動和提示音等行為。開發者可以調用以下兩個方法來發現用戶對通知渠道應用的設置:
- 要檢索單個通知渠道,可以調用 getNotificationChannel()。
- 要檢索歸屬的應用的所有通知渠道,可以調用 getNotificationChannels()。
更新通知渠道設置
一旦創建了通知渠道,其設置和行為就由用戶掌控。可以再次調用 createNotificationChannel()以重命名現有通知渠道,或更新其說明。以下示例代碼說明如何通過創建啟動 Activity 的 Intent 將用戶重定向到通知渠道的設置。在本例中,Intent 要求提供擴展數據,包括通知渠道的 ID 和應用的軟件包名稱。
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); intent.putExtra(Settings.EXTRA_CHANNEL_ID,mChannel.getId()); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());startActivity(intent);
刪除通知渠道
可以通過調用 deleteNotificationChannel()
來刪除通知渠道。作為一個垃圾信息預防機制,通知設置中將顯示已刪除渠道的數量。可以通過以下任一方法清除開發設備上的測試渠道:重新安裝應用;清除與應用副本關聯的數據。以下示例代碼演示了如何刪除通知渠道。
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 通知渠道的id String id = "my_channel_01"; NotificationChannel mChannel = mNotificationManager.getNotificationChannel(id); mNotificationManager.deleteNotificationChannel(mChannel);
