【Kotlin/Android Studio】通知の実装方法!アプリ内からリクエスト(ローカル通知)
この記事からわかること
- Android Studio/Kotlinで通知機能の実装方法
- アプリ内から通知リクエストを送信するには?
- ローカル通知とは?
- チャンネルとは?
- ヘッドアップ通知を表示する
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
参考文献:公式リファレンス:通知を作成する
参考文献:公式リファレンス:通知の概要
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
- Android:13
Androidアプリの通知機能
ローカル通知
ローカル通知とはオフラインで実行される通知機能のことを指します。デバイス内からプッシュ通知リクエストを送信し、デバイス内で処理されて通知が届くような仕組みになっています。
ローカル通知は事前準備などは必要なく、Kotlinのコードを記述するだけで実装することができます。
通知リクエストを送信するタイミングはアプリ内からしか操作できません。リマインダーやアラーム機能など自分でセットして使用するプッシュ通知などはこのローカル通知を使用して実装されています。
リモート通知
リモート通知とはオンラインで実行される通知機能のことを指します。ローカル通知とは異なり、プッシュ通知リクエストをオンライン上から送信し、デバイス内で処理されて通知が届くような仕組みになっています。
Firebaseの「Cloud Messaging」などを利用してリモート通知を送信することができます。以下はiOSですが実装方法です。
通知機能を実装する方法
流れ
- パーミッションの追加
- 通知許可ダイアログの表示
- 通知チャンネルの作成
- 通知の作成
- 通知の発行
パーミッションの追加
アプリで通知を利用したい場合は「AndroidManifest.xml」内にパーミッションを宣言する必要があります。今回は通知を利用したいのでPOST_NOTIFICATIONS
を追加しておきます。
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
通知許可ダイアログの表示
Manifest.permission
にはレベルが振られており、POST_NOTIFICATIONS
はマニフェストファイルだけでなくアプリ実行時に許可を得る必要のあるdangerous
レベルになっているので通知許可ダイアログも実装していきます。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 許可ダイアログを表示
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
private val launcher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
// ダイアログの結果で処理を分岐
if (result) {
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT)
.show()
} else {
Toast.makeText(this, "否認されました", Toast.LENGTH_SHORT)
.show()
}
}
実装方法の詳細は以下の記事を参考にしてください。
通知チャンネルの作成
通知の実装自体の肝となるのはここからになります。まずは必要となるIDをそれぞれ準備しておきます。
val CHANNEL_ID = "CHANNEL_ID"
val NOTIFY_ID = 1
Androidで通知機能を実装するためにはチャンネルを作成する必要があります。
// 1:通知チャンネルの作成
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "チャンネル名"
val descriptionText = "チャンネルの説明"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// チャンネルをシステムに登録
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
通知の作成
チャンネルを作成したら通知本体を作成して発行するまでを実装していきます。「2:通知タップ時でアプリを起動する」の指定は自由ですが通知をタップした時にアプリが起動する方が自然なのでタップ時に起動させたいActivityを指定しています。
// 通知の作成と送信
private fun sendNotify() {
// 2:通知タップ時でアプリを起動する
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
// 3:通知オブジェクトの作成
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("通知のタイトル")
.setContentText("通知の本文")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
// 4:通知の発行
// 次で実装
}
通知の発行
最後に通知の送信処理です。notify
メソッドの引数に通知IDと先ほど作成したbuilder
オブジェクトをbuild
して渡します。
// 4:通知の発行
with(NotificationManagerCompat.from(this)) {
notify(NOTIFY_ID, builder.build())
}
最後にonCreate
内でそれぞれを呼び出してボタンのイベントに紐づければ完成です。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 許可ダイアログを表示
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
// チャンネルの生成
createNotificationChannel()
// 通知を発行ボタン
val buttonNotification: Button = findViewById(R.id.button)
buttonNotification.setOnClickListener {
// 通知の作成と送信
sendNotify()
}
}
ボタンを押して通知が届くと上部のステータスバーの左側にアイコンが表示されるようになります。
ステータスバーを下にスワイプすると通知ドロワーを確認することが可能です。
コード全体
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import android.Manifest
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
class MainActivity : AppCompatActivity() {
val CHANNEL_ID = "CHANNEL_ID"
val NOTIFY_ID = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 許可ダイアログを表示
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
// チャンネルの生成
createNotificationChannel()
// 通知を発行ボタン
val buttonNotification: Button = findViewById(R.id.button)
buttonNotification.setOnClickListener {
// 通知の作成と送信
sendNotify()
}
}
// 1:通知チャンネルの作成
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "チャンネル名"
val descriptionText = "チャンネルの説明"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// チャンネルをシステムに登録
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
// 通知の作成と送信
private fun sendNotify() {
// 2:通知タップ時でアプリを起動する
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
// 3:通知オブジェクトの作成
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("通知のタイトル")
.setContentText("通知の本文")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
// 4:通知の発行
with(NotificationManagerCompat.from(this)) {
notify(NOTIFY_ID, builder.build())
}
}
private val launcher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
// ダイアログの結果で処理を分岐
if (result) {
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT)
.show()
} else {
Toast.makeText(this, "否認されました", Toast.LENGTH_SHORT)
.show()
}
}
}
チャンネルとは?
Androidの通知はAndroid 8.0(APIレベル2)以降からチャンネルという単位でグループ分けされています。チャンネル名やチャンネルの説明、チャンネルの重要度などを開発者側が設定でき、ユーザー側はアプリに登録されている通知チャンネルの有効/無効を切り替えて受け取る通知を選択することができるようになっています。
ユーザーはデバイスの「設定アプリ」>「アプリ」>「通知」を開くと以下のように登録されているチャンネルが表示されるので必要な通知チャンネルのみに制限することが可能になっています。
チャンネルと通知の重要度
チャンネルの重要度は緊急〜低までの4つから指定でき、それぞれのレベルによって通知の挙動も変化します。
val importance = NotificationManager.IMPORTANCE_DEFAULT
ユーザーへの表示の重要度 | 重要度 |
---|---|
緊急 音が鳴り、ヘッドアップ通知として表示 |
IMPORTANCE_HIGH |
高 音が鳴る |
IMPORTANCE_DEFAULT |
中 音は鳴らない |
IMPORTANCE_LOW |
低 音は鳴らず、ステータスバーにも表示されない |
IMPORTANCE_MIN |
ヘッドアップ通知を表示する
アプリが起動している場合でも通知を上部に表示するヘッドアップ通知を実装したい場合は以下の条件にマッチする必要があります。
条件
- バイブレーションを許可する
- チャンネルの重要度を緊急にする
- 通知の重要度を緊急にする
バイブレーションを許可する
<uses-permission android:name="android.permission.VIBRATE"/>
チャンネルの重要度を緊急にする
val importance = NotificationManager.IMPORTANCE_HIGH
通知の重要度を緊急にする
.setPriority(NotificationCompat.PRIORITY_HIGH)
表示されない場合の解決方法
設定をしたのに表示されない時はアプリの通知設定がデバイスにキャッシュされている可能性があります。アプリをアンインストールして再度インストールすると正常にヘッドアップ通知が表示されるようになりました。
任意の指定した未来の時間に通知を発行する方法
通知はアプリ内から発行して即座に届くより、数時間後や数日後に届くリマインダーのような使い方をすることのほうが多いと思います。以下の記事で「20XX年のX月XX日のXX時XX分に通知を発行する」方法を紹介しているので参考にしてください。
iOSでの実装はこちら
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。