티스토리 뷰
액티비티에서 실행 중인 서비스의 데이터를 사용하고 싶을 때 사용합니다.
서비스에 접속하고 서비스의 값을 가져와 사용할 수 있습니다.
서비스 만들기
먼저 서비스를 하나 구현하도록 하겠습니다.
New > Service > Service를 누르고 서비스를 하나 생성해줍니다.
아래와 같이 코드를 작성하였습니다.
class IPCService : Service() {
var value = 0
var thread: ThreadClass? = null
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
thread = ThreadClass()
thread?.start()
return super.onStartCommand(intent, flags, startId)
}
inner class ThreadClass : Thread() {
override fun run() {
while (true) {
Thread.sleep(1000)
Log.d("test", "$value")
value++
}
}
}
}
ThreadClass라는 Thread를 상속받는 Class에 value 값을 계속해서 출력해주는 코드를 작성하였습니다.
그리고 onStartCommand에 thread를 시작하는 코드를 작성하였습니다.
서비스 체크하고 실행하기
서비스가 실행 중인지 체크하고 실행해주는 코드를 작성하겠습니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(!checkServiceRunning("com.example.ipcexample.IPCService")) {
val intent = Intent(this, IPCService::class.java)
startService(intent)
}
}
private fun checkServiceRunning(serviceName: String): Boolean {
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service : ActivityManager.RunningServiceInfo in manager.getRunningServices(Int.MAX_VALUE)) {
if (service.service.className == serviceName)
return true
}
return false
}
}
checkServiceRunning 함수는 서비스 이름을 파라미터로 받고 있고 Boolean 값을 반환하고 있습니다.
manager는 getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager를 받고 있습니다.
manager.getRunningServices(Int.MAX_VALUE)을 사용하면 사용 중인 모든 서비스를 가져옵니다.
ActivityManager.RunningServiceInfo를 받고 있는 service로 하나씩 비교를 합니다.
현재 실행 중인 서비스의 이름을 가져오는 service.service.className를 통해 파라미터로 받아온 서비스의 이름과 동일하면 true를 반환하고 아니면 false를 반환합니다.
if(!checkServiceRunning("com.example.ipcexample.IPCService")) {
val intent = Intent(this, IPCService::class.java)
startService(intent)
}
checkServiceRunning에 서비스의 Package 이름을 넣어줍니다.
그리고 실행 중이지 않을 때 서비스를 실행해줍니다.
실행하면 아래와 같이 동작합니다.
서비스의 값을 받아와서 액티비티에 출력하기
이제 IPC 개념을 사용해서 서비스의 값을 액티비티로 가져와 보겠습니다.
IPCService.kt
아래는 수정된 IPCService의 전체 코드입니다.
class IPCService : Service() {
var value = 0
var thread: ThreadClass? = null
var binder: IBinder = LocalBinder()
override fun onBind(intent: Intent): IBinder = binder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
thread = ThreadClass()
thread?.start()
return super.onStartCommand(intent, flags, startId)
}
inner class ThreadClass : Thread() {
override fun run() {
while (true) {
Thread.sleep(1000)
Log.d("test", "$value")
value++
}
}
}
inner class LocalBinder: Binder() {
fun getService(): IPCService = this@IPCService
}
fun getNumber(): Int = value
}
IPCService Class에서 value값을 가져오기 위해서 아래의 함수를 구현하였습니다.
fun getNumber(): Int = value
value 값을 출력하기 위해서는 서비스의 객체 주소 값을 가져와야 합니다.
서비스의 객체 주소 값을 직접 가져오는 방법은 없습니다.
따라서 바인더를 사용해야 합니다.
바인드를 상속받고 있는 IPCService 객체의 주소 값을 반환해주는 LocalBinder Class를 구현하였습니다.
inner class LocalBinder: Binder() {
fun getService(): IPCService = this@IPCService
}
LocalBinder를 담을 변수인 binder를 만들었습니다.
그리고 onBind에서 binder을 반환하였습니다.
var binder: IBinder = LocalBinder()
override fun onBind(intent: Intent): IBinder = binder
MainActivity.kt
아래는 전체 코드입니다.
class MainActivity : AppCompatActivity() {
var ipcService : IPCService? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = Intent(this, IPCService::class.java)
if(!checkServiceRunning("com.example.ipcexample.IPCService")) {
startService(intent)
}
bindService(intent, connection, Context.BIND_AUTO_CREATE)
button.setOnClickListener {
textView.text = "${ipcService?.getNumber()}"
}
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection)
}
private fun checkServiceRunning(serviceName: String): Boolean {
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service : ActivityManager.RunningServiceInfo in manager.getRunningServices(Int.MAX_VALUE)) {
if (service.service.className == serviceName)
return true
}
return false
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as IPCService.LocalBinder
ipcService = binder.getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
ipcService= null
}
}
}
IPCService를 참조하는 변수인 ipcService라는 변수를 만들었습니다.
var ipcService : IPCService? = null
서비스에서 값을 가져오기 위해서 ServiceConnection을 먼저 구현해야 합니다.
따라서 connection이라는 ServiceConnection을 받고 있는 변수를 만들어주었습니다.
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as IPCService.LocalBinder
ipcService = binder.getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
ipcService= null
}
}
onServiceConnected는 서비스에 접속되었을 때 실행되는 함수입니다.
onServiceDisconnected는 서비스와 연결이 끊어졌을 때 실행되는 함수입니다.
onServiceConnected에서 받고 있는 파라미터인 service는 IPCService안에 onBind에서 반환하는 binder을 받아온 것입니다.
그리고 IPCService 안에서 만든 LocalBinder 클래스를 가져와 ipcService에 getService를 넣어주었습니다.
onServiceDisconnected에서는 ipcService에 다시 null 값을 넣어주었습니다.
이제 서비스에 접속을 하도록 하겠습니다.
bindService(intent, connection, Context.BIND_AUTO_CREATE)
서비스가 실행이 되고 있으면 connection이 실행을 하고, 서비스가 죽어 있으면 connection이 실행을 하지 않도록 하는 코드입니다.
그리고 ipcService?.getNumber()를 통해 액티비티에서 서비스의 값을 받아왔습니다.
textView.text = "${ipcService?.getNumber()}"
마지막으로 서비스 접속을 해제하는 unbind 코드를 넣어줘야 합니다.
override fun onDestroy() {
super.onDestroy()
unbindService(connection)
}
실행하면 아래와 같이 동작합니다.
'알려주는 이야기 > 안드로이드' 카테고리의 다른 글
안드로이드 Broad Cast Receiver (0) | 2020.08.22 |
---|---|
안드로이드 소켓 통신 예제 (0) | 2020.08.22 |
안드로이드 Intent Action 사용하기 (0) | 2020.08.21 |
안드로이드 다른 앱 실행하기 (5) | 2020.08.21 |
안드로이드 Parcelable을 사용하여 데이터 전달하기 (0) | 2020.08.21 |