외로운 Nova의 작업실

안드로이드 앱 프로그래밍 - 29(백그라운드 제약) 본문

Programming/Kotlin - Android

안드로이드 앱 프로그래밍 - 29(백그라운드 제약)

Nova_ 2023. 2. 22. 13:50

- 백그라운드 제약

액티비티를 제외한 나머지 컴포넌트는 화면을 구현하는 용도가 아니라 백그라운드에서 작업을 처리할 목적으로 사용합니다. 그런데 예전에는 앱을 실행해 화면이 출력된적이 없는 상황에서도 백그라운드에서 작업을 처리할 수있었지만 안드로이드 8버전부터는 제약을 받습니다. 따라서 브로드캐스트 리시버나 서비스를 이용할때는 백그라운드 제약에 관해 잘 정리해둬야합니다.

 

- 리시버의 백그라운드 제약

브로드캐스트 리시버는 암시적 인텐트로 실행할 수 없습니다.

<recevier
    android:name=".MyRecevier"
    android:enable="true"
    android:exported="ture">
    <intent-filter>
    	<action andorid:name="ACTION_RECEIVER" />
    </intent-filter>
</receiver>

위처럼 매니페스트 파일에 등록하고 아래처럼 다른 클라이언트 앱에서 코드를 실행하면 리시버는 받지못합니다.

val intent = Intent("ACTION_RECEIVER")
sendBroadcast(intent)

암시적 인텐트는 registerReceiver()함수로 동적으로 등록해줘야하합니다.

val filter = IntetnFilter("ACTION_RECEVIER")
registerReceiver(receiver, fileter)

 

- 서비스의 백그라운드 제약

서비스는 앱이 백그라운드 상태일때 인텐트를 전달하면 오류가 발생합니다. 포그라운드 상황에서는 잘 실행되던 인텐트도 백그라운드 상황에서는 오류가 발생합니다. 따라서 앱을 포그라운드로 만들어줘야 서비스가 백그라운드에서도 잘 실행됩니다. 포그라운드를 만드는 방법은 알림을 띄우는 것입니다. 먼저 메인 액티비티.kt 파일에 아래와 같이 안드로이드 버전에따라 service를 실행시켜주는 방법을 달리합니다.

	   val intent = Intent(this, MyService::class.java)
        
	   if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            startForegroundService(intent)
        } else{
            startService(intent)
        }

이후 알림을 만들어서 서비스쪽 클래스에서 포그라운드로 실행시킵니다.

class MyService: Service(){
    lateinit var messenger: Messenger  //액티비티에 전달할 메시지 객체
    internal class IncomingHandler(     //Message 객체를 만들때 필요한 핸들러
        context: Context,
        private val applicationContext: Context = context.applicationContext
    ) : Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message){
            when(msg.what){
                10 ->
                    Toast.makeText(applicationContext, "${msg.obj}", Toast.LENGTH_SHORT).show()

                20 ->
                    Toast.makeText(applicationContext, "${msg.obj}", Toast.LENGTH_SHORT).show()

                else -> super.handleMessage(msg)
            }
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        messenger = Messenger(IncomingHandler(this))

        val builder: NotificationCompat.Builder //알림 빌더 선언

        //알림 빌더 작성
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channelId = "1"
            val channelName = "service-channel"
            val channel =
                NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)

            //채널에 다양한 정보 설정
            channel.description = "service"
            channel.setShowBadge(true)
            val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
            val audioAttributes =
                AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ALARM)
                    .build()
            channel.setSound(uri, audioAttributes)
            channel.enableVibration(true)

            //채널을 NotificationManger에 등록
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)

            //채널을이용해 알림 빌더 생성
            builder = NotificationCompat.Builder(this, channelId)
        } else {
            builder = NotificationCompat.Builder(this)
        }

        val pendingIntent: PendingIntent =
            Intent(this, MainActivity::class.java).let { notificationIntent ->
                PendingIntent.getActivity(this, 0, notificationIntent, 0)
            }

        //알림 빌더 설정
        builder.setWhen(System.currentTimeMillis())
        builder.setContentTitle("intent")
        builder.setContentIntent(pendingIntent)
        builder.setContentText("service running")
        builder.setAutoCancel(false) //알람을 터치해도 알람이 사라지지않습니다.
		builder.setOngoing(true)  //알림을 밀어내도 사라지지않습니다.


        val notification: Notification = builder.build()


        startForeground(10, notification)
            return messenger.binder
    }
}

이때 서비스를 포그라운드로 돌려야되기 때문에 매니페스트파일에 권한을 얻어야합니다. 매니페스트 파일에 아래처럼 작성해줍니다.

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

이렇게 알림으로 서비스를 포그라운드 상태로 만들면 사용자에게 앱이 실행되고 있음을 알리면서 서비스가 제약없이 작동될 수 있습니다.

Comments