Computer >> 컴퓨터 >  >> 프로그래밍 >> Android

안드로이드(Android) 앱이 종료된 후에도 알림을 보내는 방법

앱이 화면에서 사라지거나 완전히 종료된 이후에도 사용자에게 알림을 전달해야 하는 경우가 종종 있습니다. 이번 튜토리얼에서는 서비스(Service)타이머(Timer)를 활용해 안드로이드 앱이 닫힌 뒤에도 주기적으로 알림을 발송하는 방법을 단계별로 살펴보겠습니다.

1단계: 새 프로젝트 만들기

Android Studio에서 [File] → [New Project]로 이동한 뒤, 필요한 정보를 모두 입력하여 새 프로젝트를 생성합니다.

2단계: 레이아웃 작성 — res/layout/activity_main.xml

메인 화면에는 알림 테스트를 위해 앱을 종료할 수 있는 버튼 하나를 배치합니다. 아래 코드를 activity_main.xml 파일에 추가하세요.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:onClick="closeApp"
android:text="close App for notification" />

</RelativeLayout>

3단계: 메인 액티비티 작성 — MainActivity.java

onStop() 콜백은 액티비티가 더 이상 화면에 보이지 않을 때 호출됩니다. 이 지점에서 알림 서비스를 시작하도록 처리하면, 사용자가 앱을 벗어나는 순간부터 서비스가 동작합니다. 버튼의 onClick과 연결된 closeApp() 메서드는 액티비티를 종료합니다.

package app.tutorialspoint.com.notifyme;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
protected void onStop() {
super.onStop();
// 액티비티가 화면에서 사라질 때 알림 서비스를 시작
startService(new Intent(this, NotificationService.class));
}

public void closeApp(View view) {
finish();
}
}

참고로 최신 Android Studio 프로젝트는 AndroidX를 기본으로 사용하므로, 위의 android.support.* 임포트 대신 androidx.appcompat.app.AppCompatActivity, androidx.core.app.NotificationCompat을 사용하시면 됩니다.

4단계: 알림 서비스 작성 — NotificationService.java

서비스 내부에서는 TimerHandler를 사용해 일정 간격(예제에서는 5초)마다 알림을 생성합니다. 특히 Android 8.0(오레오) 이상에서는 반드시 알림 채널(NotificationChannel)을 생성해야 알림이 정상적으로 표시된다는 점에 유의하세요.

package app.tutorialspoint.com.notifyme;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

public class NotificationService extends Service {
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
Timer timer;
TimerTask timerTask;
String TAG = "Timers";
int Your_X_SECS = 5; // 알림 반복 주기(초)

@Override
public IBinder onBind(Intent arg0) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}

@Override
public void onCreate() {
Log.e(TAG, "onCreate");
}

@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
stopTimerTask();
super.onDestroy();
}

// TimerTask 내부에서 UI 스레드 작업을 처리하기 위한 핸들러
final Handler handler = new Handler();

public void startTimer() {
timer = new Timer();
initializeTimerTask();
// 5초 후 첫 실행, 이후 Your_X_SECS 간격으로 반복
timer.schedule(timerTask, 5000, Your_X_SECS * 1000);
}

public void stopTimerTask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}

public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
createNotification();
}
});
}
};
}

private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), default_notification_channel_id);
mBuilder.setContentTitle("My Notification");
mBuilder.setContentText("Notification Listener Service Example");
mBuilder.setTicker("Notification Listener Service Example");
mBuilder.setSmallIcon(R.drawable.ic_launcher_foreground);
mBuilder.setAutoCancel(true);

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", importance);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel);
}
assert mNotificationManager != null;
mNotificationManager.notify((int) System.currentTimeMillis(), mBuilder.build());
}
}

5단계: 매니페스트 설정 — AndroidManifest.xml

서비스를 사용하려면 매니페스트 파일에 반드시 등록해야 합니다. 아래처럼 <service> 요소를 추가하세요. 진동 알림을 사용할 수 있도록 VIBRATE 권한도 함께 선언되어 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.tutorialspoint.com.notifyme">

<uses-permission android:name="android.permission.VIBRATE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service
android:name=".NotificationService"
android:label="@string/app_name">
<intent-filter>
<action
android:name="app.tutorialspoint.com.notifyme.NotificationService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

</application>
</manifest>

앱 실행 및 결과 확인

이제 애플리케이션을 실행해 보겠습니다. 실제 안드로이드 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤 툴바의 실행(Run) 아이콘을 클릭하고, 옵션 목록에서 본인의 모바일 기기를 선택하세요. 그러면 기기에 앱의 기본 화면이 나타납니다.

화면의 버튼을 눌러 앱을 종료하거나 홈 버튼으로 앱에서 벗어나면, 약 5초 후부터 설정한 주기(예제에서는 5초)마다 알림이 계속 도착하는 것을 확인할 수 있습니다. 이렇게 서비스와 타이머를 조합하면 앱이 백그라운드에 있거나 종료된 상태에서도 원하는 시점에 알림을 사용자에게 전달할 수 있습니다.