[Android][study] 서비스 컴포넌트 - 2

Intelli·2022년 12월 1일
1

이 글은 『Do it! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린』 교재를 바탕으로 작성되었습니다.

15 - 2 바인딩 서비스


  • startService() : 액티비티와 서비스 사이에 관련이 없는 경우
  • bindService() : 액티비티와 서비스 사이에 관련이 있는 경우

1. IBinder 객체 바인딩

🌳 1. 서비스 코드

class MyBinder : Binder() {
	fun funA(arg: Int) {}
    fun funB(arg: Int) {
    	return arg * arg
    }
}
override fun onBind(intent: Intent): IBinder? {
	return MyBinder()
}
  • onBind()가 호출되면 서비스를 실행한 곳에 IBinder 인터페이스를 구현한 객체(예제에서는 MyBinder())를 전달함.

🌴 2. 액티비티 코드

val connection: ServiceConnection = object : ServiceConnection {
	override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    	serviceBinder = service as MyService.MyBinder
    }
    override fun onServiceDisconnected(name: ComponentName?) {
    }
}
  • onServiceConnected의 두번째 매개변수에서 onBind()의 리턴값을 전달받음.

🌵 3. 서비스에서 바인딩한 객체의 함수 호출

serviceBinder.funA(10)
  • 액티비티에서 서비스의 요소들을 이용할 수 있다.

🌲 4. 인텐트 엑스트라 데이터와 차이점

  • 인텐트의 엑스트라 데이터는 인텐트로 특정 컴포넌트가 실행 시작될 때 데이터를 전달
  • 서비스 바인딩은 액티비티와 서비스가 이미 실행되고 있는 상황에 서로 데이터를 주고 받아야 할 때 이용.

2. 메신저 바인딩

⛅ 1. 메신저 바인딩을 이용할 때 서비스 코드

class MyService : Service() {
	lateinit var messenger: Messenger
    internal class IncomingHandler(
    	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))
        return messenger.binder
    }
}
  • MyService클래스는 변수 messenger와 내부클래스 IncomingHandler, 함수 handleMessage()로 이루어져 있다.
  • 클래스 IncomingHandlerHandler(Looper.getMainLooper())를 상속받았으며, 함수 handleMessage()를 가진다.
  • handleMessage()는 외부에서 서비스에 데이터를 전달할 때 자동으로 호출된다.
  • handleMessage()는 Message를 받아서 message값에 따라 다른 toastMessage를 띄운다.
  • messenger는 handleMessage를 받고, 이를 바인딩 한 값을 액티비티의 onServiceConnected로 보낸다.

🛫 2. 메신저 바인딩을 이용할 때 액티비티 코드

  1. 메신저 객체 이용
class MainActivity : AppCompatActivity() {
	lateinit var messenger: Messenger
    override fun onCreate(savedInstanceState: Bundle?) {
    	super.onCreate(savedInstanceState)
        // 내용 생략
        val intent = Intent(this, MyService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }
    
    val connection: ServiceConnection = object: ServiceConnection {
    	override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        	messenger = Messenger(service)
        }
        override fun onServiceDisconnected(name: ComponentName?) {}
    }
}
  • messenger에 서비스 컴포넌트의 Handler(HandleMessage)를 받는다.
  1. 서비스에 데이터 전달
val msg = Message()
msg.what = 10
msg.obj = "hello"
messenger.send(msg)
  • Message()의 what으로 초기화 한 값으로 서비스가 메세지의 내용을 알아볼 수 있도록 한다.
  • Message()의 obj는 수신자에게 보내는 추상 오브젝트
  1. 외부 앱 연동

🌍 서비스 컴포넌트를 가진 앱의 매니페스트 파일

<service
        android:name=".MyService"
        android:exported="true">
  		<intent-filter>
          	<action android:name="ACTION_OUTER_SERVICE" />
        </intent-filter>
 </service>
  • 인텐트 필터가 선언되어 있어야 한다.

🌎 서비스 컴포넌트를 이용하는 앱의 매니페스트 파일

<manifest ...생략...>
	<queries>
    	<package android:name="com.example.test_outter" />
    </queries>
</manifest>
  • 연동하고자 하는 앱의 패키지명을 지정한다.

🌎 서비스를 이용하기 위해 인텐트 객체 생성

val intent = Intent("ACTION_OUTER_SERVICE")
intent.setPackage("com.example.test_outter")
bindService(intent, connection, Context.BIND_AUTO_CREATE)
  • intent.setPackage() 를 통해 패키지명을 명시한다.

🌎 번들 객체를 이용한 A앱의 서비스와 B앱의 액티비티간 데이터 통신

val bundle = Bundle()
bundle.putString("")

3. AIDL

🔥 1. AIDL 통신 기법

  • 프로세스 간 통신을 구현할 때 사용하는 기법. 하나의 프론세스에서 다른 프로세스의 메모리에 접근할 수 없기 때문에, 데이터를 시스템에 전달한 후에 시스템이 다른 프로세스에 전달해 줘야 한다.
  • AIDL에서는 마샬링 과정을 대신 처리해준다.
  • AIDL은 여러 요청이 들어오면 멀티 스레드 환경에서 요청들을 동시에 실행한다.

💧 2. 서비스를 제공하는 앱에서의 코드

  1. AIDL 파일 만들고 인터페이스 작성
package com.example.test_aidl;

interface MyAIDLInterface {
	void funA(String data)
    int funB();
}
  1. 서비스 컴포넌트에 AIDL 인터페이스 구현
class MyAIDLService : Service() {
	override fun onBind(intent: Intent): IBinder {
    	return object: MyAIDLInterface.Stub() {
        	override fun funA(data: String?) {
            }
            override fun funB(): Int {
            	return 10
            }
        }
    }
}
  • Stub은 프로세스 간 통신을 대행해 주는 객체이다.
  1. 메니페스트 파일에 인텐트 필터 선언
<service
     android:name=".MyAIDLService"
     android:enabled="true"
     android:exported="true">
  	 <intent-filter>
  	 	<action android:name="ACTION_AIDL_SERVICE" />
  	 </intent-filter>
</service>

⚡ 3. 서비스를 이용하는 외부 앱에서의 코드

  1. 매니페스트에 패키지 정보 등록
<queries>
	<package android:name="com.example.test_outter" />
</queries>
  1. AIDL파일 복사
  2. 외부 앱의 서비스 실행
class MainActivity : AppCompatActivity() {
	lateinit var aidlService: MyAIDLInterface
    
    override fun onCreate(savedInstanceState: Bundle?) {
    	super.onCreate(savedInstanceState)
        
        val intent = Intent("ACTION_AIDL_SERVICE")
        intent.setPackage("com.example.test_outter")
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }
    
    val connection: ServiceConnection = object : ServiceConnection {
    	override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        	aidlService=MyAIDLService.Stub.asInterface(service)
        }
        override fun onServiceDisconnected(name: ComponentName?) {
        	Log.d("kkang", "onServiceDisconnected")
        }
    }
}
  1. AIDL에 선언된 함수 호출
aidlService.funA("hello")
profile
I never dreamed about success. I worked for it.

0개의 댓글