일반적으로 부팅되면 대부분의 앱은 실행되지 않은 상태입니다.
안드로이드는 부팅이 완료되면 ACTION_BOOT_COMPLETED 인텐트를 전달하여 앱이 실행되도록 합니다.
앱은 이 인텐트를 받고, 어떤 작업을 처리할 수 있습니다.
이 글에서는 ACTION_BOOT_COMPLETED 인텐트를 수신하는 방법을 소개합니다.
이 글에서 소개된 Sample App은 GitHub - StartForegroundService에서 확인할 수 있습니다. 이 글의 예제는 Kotlin으로 작성되었습니다.
권한 설정
App이 ACTION_BOOT_COMPLETED 인텐트를 수신하려면 다음과 같이 RECEIVE_BOOT_COMPLETED 권한을 AndroidManifest에 선언해야 합니다.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application ...>
...
</application>BroadcastReceiver 구현
이벤트를 반으려면 Receiver 클래스를 구현하고 그 클래스를 AndroidManifest에 정의해야 합니다.
부팅이 되면 앱이 실행되지 않았을 때, Context.registerReceiver() API로 리시버를 등록할 수 없기 때문입니다.
다음과 같이 Receiver 클래스를 생성합니다.
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("Test", "Receive : ${intent.action}")
// Do something
}
}그리고 다음과 같이 AndroidManifest에 리시버를 정의하면 됩니다.
인텐트를 받기 위해서는 리시버의 인텐트 필터에 BOOT_COMPLETED Action을 추가해야 합니다.
<application ... >
...
<receiver android:name=".MyReceiver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>이제 앱을 설치하고 디바이스를 재시작하면 BOOT_COMPLETED가 앱으로 전달되는 것을 확인할 수 있습니다.
12-24 11:35:00.574 15143 15143 D Test : Receive : android.intent.action.BOOT_COMPLETED서비스 실행
BOOT_COMPLETED 인텐트를 받고 내 앱의 서비스를 실행시켜 어떤 작업을 처리하도록 만들 수 있습니다.
서비스 구현
먼저 다음과 같이 Service를 구현합니다.
class MyService : Service() {
companion object {
const val NOTIFICATION_ID = 10
const val CHANNEL_ID = "my_notification_channel"
}
override fun onCreate() {
super.onCreate()
Log.d("Test", "MyService is started")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("MyService is running")
.setContentText("MyService is running")
.build()
startForeground(NOTIFICATION_ID, notification)
}
}
private fun createNotificationChannel() {
val notificationChannel = NotificationChannel(
CHANNEL_ID,
"MyApp notification",
NotificationManager.IMPORTANCE_HIGH
)
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.enableVibration(true)
notificationChannel.description = "AppApp Tests"
val notificationManager = applicationContext.getSystemService(
Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(
notificationChannel)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}Android O(API 26)부터 Background service 실행이 제한되어 Foreground로 실행해야 합니다.
startForegroundService()으로 서비스를 Foreground로 실행시킬 수 있습니다. 하지만, 서비스는 5초 이내에 startForeground()를 호출하여 Notification으로 서비스가 Foreground에 실행 중이라는 것을 사용자에게 알려야 합니다. 그렇지 않으면 시스템은 서비스를 종료시킵니다.
그렇기 때문에 위의 코드에서 Notificaiton Channel과 Notificaiton을 만들었습니다.
주의할 점은 위의 코드에서 다음과 같이 startForeground()를 호출하면서 Channel ID를 인자로 넘기는데, Channel ID는 0이 아닌 숫자가 전달되어야 합니다. 0을 전달하면 동작이 되지 않습니다.
startForeground(NOTIFICATION_ID, notification)Manifest에 퍼미션 및 서비스 등록
다음과 같이 AndroidManifest에 서비스를 정의하고 FOREGROUND_SERVICE 퍼미션을 추가해야 합니다.
FOREGROUND_SERVICE는 Foreground로 서비스를 실행시키기 위해 필요한 권한입니다.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application>
...
<service android:name=".MyService" />
</application>리시버에서 서비스 실행
다음으로 Receiver에서 인텐트를 받았을 때 서비스를 실행하도록 구현합니다.
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("Test", "Receive : ${intent.action}")
val intent = Intent(context, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
}위에서 설명한 것처럼 Android O(API 26)부터 Background Service 실행을 제안하고 있습니다.
API 26 이상에서는 startForegroundService()으로 서비스를 Foreground로 실행시켜야 합니다.
테스트
위와 같이 구현했으면 앱을 설치하고 디바이스를 재시작해보세요.
부팅 후 조금 기다리면 Receiver로 BOOT_COMPLETED가 전달되고, Foreground로 서비스가 실행되는 것을 확인할 수 있습니다.
12-24 13:04:58.044 3793 3793 D Test : Receive : android.intent.action.BOOT_COMPLETED
12-24 13:04:58.054 3793 3793 D Test : MyService is startedTroubleshooting
FOREGROUND_SERVICE 권한을 앱에 추가하지 않으면 다음과 같은 에러로 앱이 종료됩니다.
12-24 12:58:32.783 3688 3688 E AndroidRuntime: FATAL EXCEPTION: main
12-24 12:58:32.783 3688 3688 E AndroidRuntime: Process: com.example.app, PID: 3688
12-24 12:58:32.783 3688 3688 E AndroidRuntime: java.lang.RuntimeException: Unable to create service com.example.example.MyService: java.lang.SecurityException: Permission Denial: startForeground from pid=3688, uid=10153 requires android.permission.FOREGROUND_SERVICEstartForegroundService()호출 후, 실행된 서비스에서 5초 내에 startForeground()를 호출하지 않으면 시스템에 의해 서비스가 종료됩니다.
12-24 11:53:19.793 504 530 W ActivityManager: Bringing down service while still waiting for start foreground: ServiceRecord{21733d5 u0 com.example.app/.MyService}
12-24 11:53:19.912 504 3971 I ActivityManager: Crashing app skipping ANR: ProcessRecord{1bdf9c0 3660:com.example.app/u0a153} Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{21733d5 u0 com.example.app/.MyService}참고
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력