[Android] Service

leeehaΒ·2022λ…„ 11μ›” 13일
0
post-thumbnail

μ„œλΉ„μŠ€

  • λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ˜€λž«λ™μ•ˆ μˆ˜ν–‰λ˜λŠ” 업무λ₯Ό μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ μ»΄ν¬λ„ŒνŠΈ
  • ν™”λ©΄ 좜λ ₯ λŠ₯λ ₯은 가지지 μ•Šμ€ μ»΄ν¬λ„ŒνŠΈ
  • Serviceλ₯Ό 상속받아 μž‘μ„±
class MyService: Service() {
	override fun onBind(intent: Intent): IBinder? {
    	return null 
    }
}
  • μ»΄ν¬λ„ŒνŠΈμ΄λ―€λ‘œ AndroidManifest.xml에 등둝 (name 속성 ν•„μˆ˜)
<service
  android:name=".MyService"
  android:enabled="true"
  android:exported="true">
</service> 
  • startService()에 μ˜ν•œ μ‹€ν–‰
  • μ™ΈλΆ€ μ•±μ˜ μ„œλΉ„μŠ€λΌλ©΄ setPackage() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ μ‹€ν–‰ν•˜κ³ μž ν•˜λŠ” μ•±μ˜ νŒ¨ν‚€μ§€λͺ…을 λͺ…μ‹œ
val intent = Intent(this, MyService::class.java)
startService(intent) 
  • μ„œλΉ„μŠ€λ₯Ό μ’…λ£Œμ‹œν‚€κ³  μ‹Άλ‹€λ©΄ stopService() ν•¨μˆ˜λ‘œ μΈν…νŠΈ λ°œμƒ
val intent = Intent(this, MyService::class.java)
stopService(intent) 

μ„œλΉ„μŠ€μ˜ 생λͺ…μ£ΌκΈ°

  • μ„œλΉ„μŠ€λ₯Ό μ‹€ν–‰μ‹œν‚€λŠ” λ°©λ²•μ—λŠ” startService() ν•¨μˆ˜μ™€ bindService() ν•¨μˆ˜κ°€ μžˆλ‹€.
  • μ–΄λŠ ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ μ„œλΉ„μŠ€κ°€ μ‹€ν–‰λ˜λŠ”μ§€μ— 따라 μ„œλΉ„μŠ€μ˜ 생λͺ…μ£ΌκΈ°κ°€ 달라진닀.
  • μ„œλΉ„μŠ€ ν΄λž˜μŠ€λŠ” μ‹±κΈ€ν†€μœΌλ‘œ λ™μž‘ν•œλ‹€. (싱글톀: 클래슀의 μΈμŠ€ν„΄μŠ€κ°€ ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•˜λŠ” 것)
    • ν™”λ©΄ 좜λ ₯을 λͺ©μ μœΌλ‘œ ν•˜λŠ” μ•‘ν‹°λΉ„ν‹°λŠ” μΈν…νŠΈμ— μ˜ν•΄ μ—¬λŸ¬ 개의 객체λ₯Ό 생성할 수 μžˆλ‹€.
    • λ°˜λ©΄μ—, ν™”λ©΄ 좜λ ₯ κΈ°λŠ₯이 μ—†λŠ” μ„œλΉ„μŠ€λŠ” μΈν…νŠΈκ°€ μ—¬λŸ¬ 번 λ°œμƒν•˜λ”λΌλ„ μƒˆλ‘œμš΄ 객체가 μƒμ„±λ˜μ§€ μ•Šκ³  기쑴의 것을 κ·ΈλŒ€λ‘œ μ΄μš©ν•œλ‹€.

startService

  • startService() β†’ onCreate(), onStartCommand()
  • stopService() β†’ onDestroy()
  • onCreate() ν•¨μˆ˜λŠ” μ„œλΉ„μŠ€ 객체 생성 μ‹œ μ΅œμ΄ˆμ— ν•œλ²ˆλ§Œ μ‹€ν–‰λœλ‹€.
  • μ„œλΉ„μŠ€κ°€ μ€‘λ‹¨λ˜μ—ˆλ‹€κ°€ λ‹€μ‹œ 싀행될 λ•ŒλŠ” onStartCommand() ν•¨μˆ˜κ°€ ν˜ΈμΆœλœλ‹€.
  • onStartCommand() ν•¨μˆ˜λŠ” μžμ‹ μ„ μ‹€ν–‰μ‹œν‚¨ μΈν…νŠΈμ˜ μš”μ²­ 사항을 μ²˜λ¦¬ν•œλ‹€.

bindService

  • bindService() β†’ onCreate(), onBind()
  • unbindService() β†’ onUnbind(), onDestroy()
  • onCreate()λŠ” μ„œλΉ„μŠ€ 객체 생성 μ‹œ μ΅œμ΄ˆμ— ν•œλ²ˆλ§Œ μ‹€ν–‰λ˜μ§€λ§Œ, onBind()λŠ” μ—¬λŸ¬ 번 호좜될 수 μžˆλ‹€.

startService

  • startService() ν•¨μˆ˜μ— μ˜ν•΄ μ‹€ν–‰λœ μ„œλΉ„μŠ€λŠ” νŠΉμ • 객체λ₯Ό λ¦¬ν„΄ν•˜μ§€ μ•ŠλŠ”λ‹€. 즉, μ»΄ν¬λ„ŒνŠΈ κ°„μ˜ μƒν˜Έ 데이터λ₯Ό μ„œλΉ„μŠ€μ— 직접 전달할 수 μžˆλŠ” 방법이 μ—†λ‹€.
  • 그런데 λΈŒλ‘œλ“œμΊμŠ€νŠΈ λ¦¬μ‹œλ²„λŠ” 동적 등둝이 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ—, μ„œλΉ„μŠ€μ— λΈŒλ‘œλ“œμΊμŠ€νŠΈ λ¦¬μ‹œλ²„λ₯Ό λ“±λ‘ν•˜λ©΄ 이λ₯Ό μ‹€ν–‰μ‹œν‚¨ μΈν…νŠΈμ˜ λΆ€κ°€ 데이터λ₯Ό μ‚¬μš©ν•˜λŠ” λ°©μ‹μœΌλ‘œ μƒν˜Έ 데이터 전달이 κ°€λŠ₯해진닀.

  • μ•‘ν‹°λΉ„ν‹°λŠ” μΈν…νŠΈλ₯Ό 톡해 μ„œλΉ„μŠ€μ— λ“±λ‘λœ λΈŒλ‘œλ“œμΊμŠ€νŠΈ λ¦¬μ‹œλ²„λ₯Ό μ‹€ν–‰μ‹œν‚¨λ‹€.
  • μ„œλΉ„μŠ€μ˜ λΈŒλ‘œλ“œμΊμŠ€νŠΈ λ¦¬μ‹œλ²„λŠ” μžμ‹ μ„ μ‹€ν–‰μ‹œν‚¨ μΈν…νŠΈμ˜ λΆ€κ°€ 데이터λ₯Ό μ‚¬μš©ν•œλ‹€.
  • μ„œλΉ„μŠ€λŠ” μΈν…νŠΈλ₯Ό 톡해 앑티비티에 λ“±λ‘λœ 브둜트캐슀트 λ¦¬μ‹œλ²„λ₯Ό μ‹€ν–‰μ‹œν‚¨λ‹€.
  • μ•‘ν‹°λΉ„ν‹°μ˜ λΈŒλ‘œλ“œμΊμŠ€νŠΈ λ¦¬μ‹œλ²„λŠ” μžμ‹ μ„ μ‹€ν–‰μ‹œν‚¨ μΈν…νŠΈμ˜ λΆ€κ°€ 데이터λ₯Ό μ‚¬μš©ν•œλ‹€.

μ‹€μŠ΅ 예제

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutorial.c71">

    <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/Theme.AndroidLab">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

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

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#303897">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/background"
        android:layout_alignParentTop="true" />

    <ImageView
        android:id="@+id/startButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_play"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="36dp"
        android:layout_marginLeft="24dp"
        android:clickable="true"/>

    <ImageView
        android:id="@+id/stopButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_stop"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="36dp"
        android:layout_marginRight="24dp"
        android:clickable="true"/>
</RelativeLayout>

MyService.kt

package com.tutorial.c71

import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.MediaPlayer
import android.os.IBinder

class MyService : Service() {
    lateinit var player: MediaPlayer

    var receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val mode = intent?.getStringExtra("mode")
            if(mode != null){
                if(mode == "start"){
                    try{
                        if(player.isPlaying){
                            player.stop()
                            player.release()
                        }
                        player = MediaPlayer.create(context, R.raw.music)
                        player.start()
                    }catch (e: Exception){
                        e.printStackTrace()
                    }
                }else if(mode == "stop"){
                    if(player.isPlaying){
                        player.stop()
                    }
                }
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        player = MediaPlayer()
        registerReceiver(receiver, IntentFilter("PLAY_TO_SERVICE"))
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(receiver)
    }

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }
}

MainActivity.kt

package com.tutorial.c71

import android.content.Intent
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val startButton = findViewById<ImageView>(R.id.startButton)
        val stopButton = findViewById<ImageView>(R.id.stopButton)

        startButton.setOnClickListener {
            val intent = Intent("PLAY_TO_SERVICE")
            intent.putExtra("mode", "start")
            sendBroadcast(intent)

            startButton.isEnabled = false
            stopButton.isEnabled = true
        }

        stopButton.setOnClickListener {
            val intent = Intent("PLAY_TO_SERVICE")
            intent.putExtra("mode", "stop")
            sendBroadcast(intent)

            startButton.isEnabled = true
            stopButton.isEnabled = false
        }

        val intent = Intent(this, MyService::class.java)
        startService(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        val intent = Intent(this, MyService::class.java)
        stopService(intent)
    }
}


bindService

  • bindService()λŠ” μ„œλΉ„μŠ€κ°€ μ‹€ν–‰λ˜λ©΄μ„œ μžμ‹ μ„ μ‹€ν–‰μ‹œν‚¨ 곳에 객체λ₯Ό λ°”μΈλ”©ν•œλ‹€λŠ” 의미
  • μ•„λž˜ 그림처럼 μ„œλΉ„μŠ€μ˜ 객체λ₯Ό 앑티비티에 λ°”μΈλ”©μ‹œν‚€λ©΄, μ•‘ν‹°λΉ„ν‹°λŠ” ν•΄λ‹Ή 객체의 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 있게 λœλ‹€. (λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜λ‚˜ λ¦¬ν„΄κ°’μœΌλ‘œ μ»΄ν¬λ„ŒνŠΈ κ°„μ˜ μƒν˜Έ 데이터 전달이 κ°€λŠ₯해짐.)

  • μ„œλΉ„μŠ€μ˜ 생λͺ…μ£ΌκΈ° ν•¨μˆ˜ 쀑에 onBind() ν•¨μˆ˜κ°€ μ‹€ν–‰λœλ‹€.
class MyBinder: Binder() {
  fun funA(arg: Int) {
 	// ... 
  }
  fun funB(arg: Int): Int {
      return arg * arg
  }
}
override fun onBind(intent: Intent): IBinder? { 
     return MyBinder() // Binderλ₯Ό 상속받아 μƒμ„±ν•œ 객체 리턴 
}
  • ServiceConnection μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 객체 μ€€λΉ„
  • onServiceConnected() ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜λ‘œ Bind 객체 전달
val connection: ServiceConnection = object: ServiceConnection {
	override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    	serviceBinder = service as MyService.MyBinder
    }
    
    override fun onServiceDisconnected(name: ComponentName?) {
    	// ... 
    }
}

μ‹€μŠ΅ 예제

MyService.kt

package com.tutorial.c71

import android.app.Service
import android.content.Intent
import android.media.MediaPlayer
import android.os.Binder
import android.os.IBinder

class MyService : Service() {

    inner class MyBinder: Binder() {
        var player = MediaPlayer()
        fun startMusic() {
            try{
                if(player.isPlaying){
                    player.stop()
                    player.release()
                }
                player = MediaPlayer.create(applicationContext, R.raw.music)
                player.start()
            }catch (e: Exception){
                e.printStackTrace()
            }
        }

        fun stopMusic(){
            if(player.isPlaying){
                player.stop()
            }
        }
    }

    override fun onBind(intent: Intent): IBinder {
        return MyBinder()
    }
}

MainActivity.kt

package com.tutorial.c71

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    lateinit var binder: MyService.MyBinder

    private val connection: ServiceConnection = object: ServiceConnection {
        override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
            binder = p1 as MyService.MyBinder
        }

        override fun onServiceDisconnected(p0: ComponentName?) {
            TODO("Not yet implemented")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val startButton = findViewById<ImageView>(R.id.startButton)
        val stopButton = findViewById<ImageView>(R.id.stopButton)

        startButton.setOnClickListener {
            binder.startMusic()
            startButton.isEnabled = false
            stopButton.isEnabled = true
        }

        stopButton.setOnClickListener {
            binder.stopMusic()
            startButton.isEnabled = true
            stopButton.isEnabled = false
        }

        val intent = Intent(this, MyService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    override fun onDestroy() {
        super.onDestroy()
        unbindService(connection)
    }
}
profile
μŠ΅κ΄€μ΄ 될 λ•ŒκΉŒμ§€ πŸ“

0개의 λŒ“κΈ€