woshidan's blog

そんなことよりコードにダイブ。

Androidの通知チャンネルの振る舞いをプロジェクトのtargetSDKVersionを変えて少し試してみた

Android OのEaster Eggはoctopusでした*1が、OのコードネームはOreoでしたね。さて、Android Oではユーザーが通知をより便利に管理するために通知チャンネルが導入されました。

通知チャンネルがなんたるかや主要な扱い方については公式他に任せるとして、この記事については、通知チャンネルについて自分が試してみた少しマイナーなことについてまとめようと思います。

TL;DR

  • Android Oの端末でも、アプリのtargetSDKVersionが25以下なら通知チャンネルを作成せずとも通知は届く
  • Android Oの端末でも、アプリのtargetSDKVersionが25以下かつライブラリのtargetSDKVersionが26で、ライブラリ側で通知チャンネルを作成しているならば、その通知チャンネルが作成&利用可能であると同時に「Uncategorized」 という通知チャンネルが作成される
  • Android Oの端末で、アプリのtargetSDKVersionが26なら、通知チャンネルを作成しないと通知が届かないし、その旨のToastが出る
  • 通知チャンネルを作成したあと、同じIDを指定して作成し直すことで「チャンネル名」などのチャンネルの設定が更新できるが、バッジの有無の設定は更新できない

以下、検証用のコードなどを置いておきます。

Android Oの端末でも、アプリのtargetSDKVersionが25以下の場合

// アプリのプロジェクトのbuild.gradle
android {
    compileSdkVersion 25
    buildToolsVersion '25.0.3'

    defaultConfig {
        applicationId 'com.example.woshidan.test'
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1"
    }
...
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Notification.Builder builder = new Notification.Builder(this)
                .setSmallIcon(android.R.drawable.ic_btn_speak_now)
                .setContentTitle("Title")
                .setContentText("Text");

        final NotificationManager notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify("tag", 1234, builder.build());

届いた通知は以下のような形で表示され、通知チャンネルの表示は出ないようです。

Android Oの端末でも、アプリのtargetSDKVersionが25以下かつライブラリのtargetSDKVersionが26で、ライブラリ側で通知チャンネルを作成している場合

のように、ライブラリプロジェクトを作成して、ライブラリプロジェクト側で targetSdkVersion を26にします。

// libraryプロジェクトのbuild.gradle
apply plugin: 'com.android.library'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
// アプリ側のプロジェクトのbuild.gradle
dependencies {
    ...
    compile project(path: ':mylibrary')
}
package com.example.woshidan.mylibrary;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.support.annotation.RequiresApi;

/**
 * Created by woshidan on 2017/08/22.
 */

public class NotificationChannelCreator {
    @RequiresApi(api = 26)
    public static void createChannel(final Context context) {
        NotificationChannel channel = new NotificationChannel("my_channel_id", "通知テストチャンネル", NotificationManager.IMPORTANCE_DEFAULT);
        channel.setDescription("テストの説明です");
        channel.setShowBadge(true);

        // create or update the Notification channel
        final NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.createNotificationChannel(channel);
    }
}
// アプリ側のプロジェクト
public class MainActivity extends Activity {

    @RequiresApi(api = 26)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        NotificationChannelCreator.createChannel(this);

        // 通知チャンネルを指定しないで通知を送る
        // ここで通知を送っていない場合でもUncategorizedチャンネルはできる
        final Notification.Builder builder = new Notification.Builder(this)
                .setSmallIcon(android.R.drawable.ic_btn_speak_now)
                .setContentTitle("Title")
                .setContentText("Text");

        final NotificationManager notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify("tag", 1234, builder.build());

Android Oの端末で、アプリのtargetSDKVersionが26の場合

// アプリのプロジェクトのbuild.gradle
android {
    compileSdkVersion 26
    buildToolsVersion '26.0.1'

    defaultConfig {
        applicationId 'com.example.woshidan.test'
        minSdkVersion 16
        targetSdkVersion 26
        versionCode 1
        versionName "1"
    }
...
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Notification.Builder builder = new Notification.Builder(this)
                .setSmallIcon(android.R.drawable.ic_btn_speak_now)
                .setContentTitle("Title")
                .setContentText("Text");

        final NotificationManager notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify("tag", 1234, builder.build());

通知チャンネルの更新について

        NotificationChannel channel = new NotificationChannel("my_channel_id", "通知テストチャンネル", NotificationManager.IMPORTANCE_DEFAULT);
        channel.setDescription("テストの説明です");
        channel.setShowBadge(true);

        // create or update the Notification channel
        final NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.createNotificationChannel(channel);

一度上記のコードで通知チャンネルを作成すると、下記のようなチャンネルができます。

その後、下記のコードで更新すると

        NotificationChannel channel = new NotificationChannel("my_channel_id", "通知テストチャンネル Updated", NotificationManager.IMPORTANCE_HIGH);
        channel.setDescription("テストの説明を変えました");
        channel.setShowBadge(false);

        // create or update the Notification channel
        final NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.createNotificationChannel(channel);

こうなります。

現場からは以上です。