파이어베이스 FCM(Firebase)를 이용한 안드로이드 푸쉬(PUSH) 프로그램 개발
여태껏 안드로이드 웹뷰만을 개발해 오던 저에게 푸쉬(PUSH)기능을 넣으라는 의뢰가 들어왔습니다.
뭐 까라면 까야죠 ㅋㅋ
예전에 회사 다닐때 php 로 안드로이드 GCM 을 호출해본적은 있는데,
구글링을 통해 여러가지를 조사한 결과 크게 아래와 같은 정보를 입수했습니다.
- 구글에서 더이상 GCM(Google Clouding Message Service) 은 지원해주지 않기로 결정을 내림.
- 이제 GCM대신 FCM(파이어베이스 클라우딩 메세지) 를 적극권장함.
뭐 기술이 워낙 빠르게 바뀌고, 구글정책도 하루하루가 다르니 이런것도 이해하려니 했습니다.
FCM 사용방법에 대해서는 구글 파이어베이스 콘솔에 자세히 나와있는데, 순서를 간략하게나마 나열해 본다면
===== 파이어베이스에서 할일 =====
1. 파이어베이스 콘솔에 프로젝트 등록
https://console.firebase.google.com/
2. 프로젝트 등록후 "서버키"를 발급받음. (이 서버키는 메세지 발송시에 사용된다)
===== 안드로이드 스튜디오에서 할일 =====
build.gradle (Module:app)
dependencies {
implementation 'com.google.firebase:firebase-messaging:15.0.0'
implementation 'com.squareup.okhttp3:okhttp:3.2.0'
}apply plugin: 'com.google.gms.google-services'
build.gradle (Project:xxx)
dependencies {
classpath 'com.google.gms:google-services:3.2.0' // google-services plugin
}
위 소스를 추가후에, 안드로이드 스튜디오 우측상단에 싱크나우를 클릭해주면, 안드로이드 스튜디오가 알아서 모듈들을 다운받아서 설치한다.(정말 편리한 세상이얌)
MainActivity.java
OnCreate 안에 아래소스 추가
"news" 는 자기가 지정하기 나름인데 여기서 지정한 키워드가 파이어베이스에서 사용된다는 것만 알아두자.
//FCM 토픽추가
FirebaseMessaging.getInstance().subscribeToTopic("news");
FirebaseInstanceId.getInstance().getToken();
AndroidManifast.xml
application 안에 아래소스 추가
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
자바 클래스 생성
- MyFirebaseInstanceIDService.java
package 본인이만든패키지;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import java.io.IOException;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
// [START refresh_token]
@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + token);
// 생성등록된 토큰을 개인 앱서버에 보내 저장해 두었다가 추가 뭔가를 하고 싶으면 할 수 있도록 한다.
sendRegistrationToServer(token);
}
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("Token", token)
.build();
//request
Request request = new Request.Builder()
.url("토큰값을 받을 나의 서버url")
.post(body)
.build();
try {
client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- MyFirebaseMessagingService.java
package 본인이만든패키지;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
private static final String TAG = "FirebaseMsgService";
// [START receive_message]
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//추가한것
//sendNotification(remoteMessage.getData().get("message"));
sendNotification(
remoteMessage.getData().get("title"),
remoteMessage.getData().get("message"),
remoteMessage.getData().get("link")
);
}
private void sendNotification(String title, String messageBody, String link) {
Intent intent = new Intent(this, MainActivity.class);
//Log.d("start_link=", link);
Bundle bundle = new Bundle();
bundle.putString("url", link);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
===== 서버에서 할일 =====
저같은 경우는 서버가 Apache + PHP + Mysql 입니다.
서버는 개발자분들 상황에 따라서 다 다르겠지만 저는 제 서버 기준으로 설명을 드리겠습니다.
- <?php
- include_once "../lib/_config.php";
- //write_log("./log_post.txt", $_POST);
- //write_log("./log_get.txt", $_GET);
- /*
- Array
- (
- [Token] => ceky4svygSg:APA91bHGTnuMMLGTMhNXkqhPjiLY1WKpBu8PgPDE3lt2fjkpH_jDmYWNDRT8UHbZy_RytWBBQHhB3ux1ZzA8V7nO0x1welmY9uqr9ymiLWuHC5CFgL2ZBbkSuyUxsPeXTF4AWvbF4RK5F0VSdMVLPZR_LanX4pPiWw
- )
- */
- if($_POST["Token"])
- {
- $token = $_POST["Token"];
- $sql = "
- INSERT ".PREFIX."app_users
- SET token = '".$token."'
- ON DUPLICATE KEY UPDATE
- token = '".$token."'
- ";
- //write_log("./log_sql.txt", $sql);
- $core['db']->query($sql);
- }
- ?>
제가 작성한 소스 그대로 까발립니다. 수정된것도 없고 어려운것도 없는 소스이니 알아서 보시고 수정해서 사용하시길 ㅋ
제 서버에서 저 소스를 작성한 파일은 경로가 /api/fcm_register.php 파일입니다.
저 파일의 역할은
1. 사용자가 어플리케이션을 설치하면, 어플리케이션에서 최초 설치시에 위 fcm_register.php 파일로 디바이스 토큰값을 post 로 던집니다.
2. 서버에서는 그 post 로 받은 디바이스 토큰값을 데이터베이스에 저장해뒀다가, 나중에 사용자들에게 push 를 보낼때 이 토큰값을 이용해야 합니다.
이해하기 쉽죠?
그럼 저 fcm_register.php 파일의 경로는 어디서 설정해야 하느냐?
아까 위에서 안드로이드 스튜디오에서 작성한
- MyFirebaseInstanceIDService.java 파일의
//request
Request request = new Request.Builder()
.url("토큰값을 받을 나의 서버url")
.post(body)
.build();
위 url에 넣어두면 됩니다.
딸랑 post 배열에 "Token" 이라는 값만 오는데 이 값이 꽤 깁니다만, 그냥 varchar(200) 정도면 충분할 것으로 보입니다.
제가 넣은 토큰값입니다. 아직 서비스를 시작안해서 토큰값이 3개 뿐이네요~, 즉 앱을 다운 받아서 설치한 사람이 3명뿐이라는 뜻~(2명이 저임 -_-)
저 토큰값을 이용해서 이제 push를 보내봅시다.
저같은 경우 공지사항에 게시글이 달릴경우, push 를 보내도록 개발했습니다.
아래는 소스입니다.
- $sql = "SELECT token FROM ".$this->table['app_users'];
- $result = $this->lib['db']->query($sql);
- while($row = $this->lib['db']->result_assoc($result))
- {
- $tokens[] = $row['token'];
- }
- $message_string = "새로운 공지사항이 등록되었습니다.";
- "title" => "공지사항이 왔어용~",
- "message" => $message_string,
- "link" => URL . "/index.php?v=board_view&idx=" . $last_idx
- );
- //print_r($message);exit;
- $this->send_notification($tokens, $message);
- public function send_notification($tokens, $message)
- {
- $url = 'https://fcm.googleapis.com/fcm/send';
- 'registration_ids' => $tokens,
- 'data' => $message
- );
- 'Authorization:key =' . self::GOOGLE_FCM_API_KEY,
- 'Content-Type: application/json'
- );
- /*
- print_r($fields);
- print_r($headers);
- exit;
- */
- if ($result === FALSE) {
- }
- return $result;
- }
위 소스에서
GOOGLE_FCM_API_KEY
이 상수가 바로 아까 파이어베이스에서 만든 "서버키" 입니다.
소스는 보시기에 그리 어렵지 않을 것입니다.
사용하실거면 본인들 서버에 맞게끔 수정하셔서 사용하시면 됩니다.
눈여겨 볼부분은 $message 배열을 만드는 부분입니다.
저의 경우 title, message, link 3개의 값을 만들었는데, 이 데이터를 받는 부분은 안드로이드 소스안에 있습니다.
MyFirebaseMessagingService.java
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//추가한것
//sendNotification(remoteMessage.getData().get("message"));
sendNotification(
remoteMessage.getData().get("title"),
remoteMessage.getData().get("message"),
remoteMessage.getData().get("link")
);
}
위 파일의 소스에서 위같이 받아서, 본인 프로그램에 맞게끔 적절히 사용합니다.
저의 경우, link 를 보낸 이유가, 공지사항 푸쉬가 스마트폰에서 뜨면, 바로 그걸 누르면, 그 공지사항으로 이동되게 하기 위해서 넣었습니다.
힘들었습니다 ㅋ
여기까지가 제가 하루동안 삽질해가면서 얻은 "FCM을 이용한 안드로이드 메세지 발송하기" 입니다.
===============================================================================================================================
개별로 발송하는 푸쉬함수 - 2019-12-24 update
public function send_notification_one($token, $message)
{
$url = 'https://fcm.googleapis.com/fcm/send';
$fields = array(
'to' => $token,
'notification' => $message
);
$headers = array(
'Authorization:key ='.GOOGLE_FCM_API_KEY,
'Content-Type: application/json'
);
/*
print_r2($fields);
print_r2($headers);
exit;
*/
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
$result = curl_exec($ch);
//echo "result = ".$result;
if ($result === FALSE) {
die('Curl failed: ' . curl_error($ch));
}
curl_close($ch);
return $result;
}