Android Push Notification 구현 (커스텀 알림음 포함)
참조 사이트 : https://firebase.google.com/docs/cloud-messaging/android/client?hl=ko
실제 구현 예제 : https://medium.com/2kyung19/firebase-android-%EC%97%B0%EB%8F%99-%EB%B0%8F-push-%EC%95%8C%EB%A6%BC%EB%B3%B4%EB%82%B4%EA%B8%B0-ec6c57b2eb68
(조금 예전버전으로, 더이상 사용하지 않는 FirebaseInstanceIdService를 사용함. 이는 FirebaseMessagingService 내부로 통합되었음)
https://herojoon-dev.tistory.com/23?category=797826
** Flow 설명
- Firebase 에서 FCM 메세지 발생시, 안드로이드 앱에 구현한 서비스에서 이를 수신 -> 수신받은 메세지를 내부 notification으로 noti 발생.
- 따라서, Firebase에서 메세지를 전달해줄 앱 등록 필요.
- 앱에서는 Firebase 연동 sdk 및 코드 세팅 필요
- 메세지를 수신할 서비스 등록 및 해당 클래스 구현 필요
- 클래스에서 메세지 수신시 내부 noti를 통해 noti 발생
- 앱을 한번은 실행을 해주어야 메세지 수신이 가능함.
1. Google Firebase 세팅
- https://console.firebase.google.com/ 으로 접속하여 프로젝트 생성 및 android 앱의 패키지 네임 등록
- google-services.json 을 다운로드 하여, Android Studio 의 프로젝트에 추가 (Project 탭에서 앱 명칭 -> app 폴더 하위에 추가. (gradle과 같은 위치)
- 2개의 gradle (app 에 있는 gradle과 전역 gradle) 에 각각 세팅 맟 sdk 사용 추가 (https://firebase.google.com/docs/cloud-messaging/android/client?hl=ko#kotlin+ktx_1 참조)
2. Android Studio 에서 세팅
- 메뉴 중 Tools -> FireBase 클릭 -> 우측에서 1, Connect your app to Firebase 및 Add FCM to your app 에서 클릭을 통해 설정
3. Manifest 수정
- 인터넷 접근권한 추가
- <application> 태그 위에 <uses-permission android:name="android.permission.INTERNET " /> 추가
- uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 추가
- </activity> 하단에 다음의 코드 추가. 서비스는 현재 구현되어 있지 않으므로 오류가 날 것임.
- FCM으로 부터 메세지를 수신받고 기기로 알림을 보내는 클래스 파일을 service android:name에 작성한 동일한 이름으로 만들어 줘야 한다.
<service android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
4. 서비스 추가
- Android Studio 에서 클래스 추가 -> 클래스명은 Manifest의 Service에 등록한 MyFirebaseMessagingService 로 생성.
- 26버전 미만, 이상의 처리가 다르다.
- 최신버전에서는 channel id와 name 설정을 추가로 해야한다.
package com.example.pushtest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.view.textclassifier.ConversationActions;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@NonNull
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if(remoteMessage.getNotification()!= null)
{
sendNotification(remoteMessage.getNotification().getTitle(),remoteMessage.getNotification().getBody());
}
}
private void sendNotification(String messageTitle, String body)
{
Intent intent = new Intent(this,MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT);
String channelID = "sajuid";
String channelName = "sajuname";
//실제 사용할 효과음을 res -> raw에 등록 후 사용.
Uri defaultSound = Uri.parse("android.resource://"+ getPackageName() + "/raw/cancel");
NotificationCompat.Builder notiBuilder = new NotificationCompat.Builder(this,channelID).setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(messageTitle).setContentText(body).setAutoCancel(true).setSound(defaultSound).setContentIntent(pendingIntent);
NotificationManager notimanager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(channelID,channelName,NotificationManager.IMPORTANCE_HIGH);
chan.setSound(defaultSound, new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
notimanager.createNotificationChannel(chan);
}
notimanager.notify(0,notiBuilder.build());
}
@NonNull
@Override
public void onNewToken(String s)
{
super.onNewToken(s);
sendTokenRegist(s);
}
@NonNull
private void sendTokenRegist(String token)
{
//구현된 백엔드 서버에 전송 필요
}
}
4. 1. MainActivity
- Push Notification을 수신하기 위한 토큰값을 검색해야함.
- onCreate에 구현
package com.example.pushtest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Debug;
import android.util.Log;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(new OnCompleteListener<String>() {
@Override
public void onComplete(@NonNull Task<String> task) {
if(!task.isSuccessful())
{
return;
}
String token = task.getResult();
Log.d("haha",token);
}
});
}
}
5. 메세지 전송
- firebase 콘솔에 접속 (https://console.firebase.google.com/)
- 프로젝트 선택 후 Cloud Messaging 선택
- 새 알림 클릭하여 내용 작성 후 전달
6. 특정 사용자에게만 메세지 전달하기
6. 1. firebase 콘솔에 세팅이 필요함.
- firebase 콘솔 접속 -> 톱니바퀴 모양의 설정 클릭 -> 서비스계정 클릭 -> 새 비공개키 생성 클릭
6. 2. 특정 기기에만 push 전송 (서버)
- 특정 사용자에게만 메세지 전달하는 방법
- 서버에서 구현 필요 (참조 : https://firebase.google.com/docs/cloud-messaging/send-message?hl=ko#python)
- firebase -> 프로젝트 -> 설정 -> 서비스계정 에서 firebase admin SDK 에서 비공개키 생성을 통한 json 필요
- 참조 2 : https://herojoon-dev.tistory.com/23?category=797826
- 구현 상세
- 클라이언트에서 token값을 검색 -> 로그인시 회원 아이디와 토큰값을 서버에 전달
- 서버에서 push 알림 생성시, 저장된 토큰값을 이용하여 FCM 서버에 메세지 전달
- FCM에서 해당 토큰을 사용중인 클라이언트에게 push 전달
7. 커스텀 알림음
- 사용할 알림음을 project -> app -> res -> raw에 저장
- raw 폴더가 없을경우 생성
- 파일 확장자는 영문자 소문자 및 _ 만 가능함.
- 안드로이드 최신버전의 경우, NotificationChannel 을 사용하게 되는데, 해당 변수에서 setSound를 통해 커스텀 알림음을 등록해야 한다.
- background 이거나 앱실행중이 아닐경우 : https://mintpot.synology.me:30000/boards/50/topics/516
// cancel 이라는 파일명의 음성파일이 res -> raw에 있어야 함.
Uri defaultSound = Uri.parse("android.resource://"+ getPackageName() + "/raw/cancel");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(channelID,channelName,NotificationManager.IMPORTANCE_HIGH);
chan.setSound(defaultSound, new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
notimanager.createNotificationChannel(chan);
}
notimanager.notify(0,notiBuilder.build());