15-2 바인딩 서비스

StrayCat·2022년 12월 10일
0

IBinder 객체 바인딩

  • 서비스를 2가지로 호출하는 이유는 서비스를 이용하는 상황을 2가지로 구분하기 위함이다.
    • startService() : 백그라운드 작업은 필요하나 액티비티와 데이터를 주고받을 일이 없는 경우 호출
    • bindService() : 서비스와 액티비티가 상호작용을 해야할 경우 호출

서비스 코드

class MyService : Service(){
    class MyBinder : Binder(){
        fun funA(arg: Int){
            
        }
    }
    
    override fun onBind(p0: Intent?): IBinder? {
        return MyBinder()
    }
}
  • onBind() 함수의 반환 타입이 IBinder 인터페이스이다.
  • onBind() 가 호출되면 서비스를 실행한 곳에 IBinder 인터페이스를 구현한 객체를 전달한다.

액비티비 코드

  • 서비스를 bindService() 로 호출한 곳에서는 해당 서비스의 onBind() 함수에서 반환한 객체를 onServiceConnected() 함수로 받을 수 있다.(2번째 매개변수)
        val connection: ServiceConnection = object : ServiceConnection{
            override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
                serviceBinder = p1 as MyService.MyBinder
            }

            override fun onServiceDisconnected(p0: ComponentName?) {

            }

        }

        serviceBinder.funA()

인텐트 엑스트라 데이터와 bindService()의 데이터 송수신 차이

  • 인텐트 엑스트라 데이터 : 컴포넌트가 실행/종료될 때 전달
  • bindService() : 이미 서비스가 실행된 상태에서 서로 데이터를 송수신

메신저 바인딩

  • IBinder 구현 객체 대신 API에서 제공하는 Messenger 객체를 바인딩 하는 방법
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(p0: Intent?): IBinder? {
       messenger = Messenger(IncomingHandler(this))
        return messenger.binder
    }
}
  • handleMessage()는 외부에서 서비스에 데이터를 전달할 때 자동 호출된다.
  • 전달받은 메세지는 what으로 속성을, obj 속성으로 전달된 데이터를 가져온다.
class MainActivity : AppCompatActivity() {
    lateinit var messenger: Messenger

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

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

    val connection : ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
            messenger = Messenger(p1)
        }

        override fun onServiceDisconnected(p0: ComponentName?) {

        }

    }
}
  • Messenger 객체를 사용하는 액티비티 쪽 코드는 위와 같이 구성할 수 있다.
  • Messenger를 전달할 때는 what에 속성, obj에 데이터를 넣고 send() 를 호출한다.
  • send() 를 호출하는 순간 서비스쪽 handleMessage() 가 자동으로 호출 된다.
        val msg = Message()
        msg.what = 10
        msg.obj = "hello"
        messenger.send(msg)

외부 앱 연동

  • 외부앱과 연동하기 위해서는 매니페스트 파일에 <service>에 intent-filter가 적용되어 있어야 한다.
  • 또한 bindService()를 실행하는 곳에서의 매니페스트 파일에는 <queries> 를 통해 패키지 공개 상태를 적용해야 한다.
    <queries>
        <package android:name="com.example.test_outter"/>
    </queries>
  • 실행할 패키지명 명시
        val intent = Intent("ACTION_OUTER_SERVICE")
        intent.setPackage("com.example.test_outter")
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
  • 추가로 프로세스 간 통신에서 주고받는 데이터는 Parcelable이나 Bundle 타입이어야 하므로 데이터를 Bundle로 감싸서 Message객체에 담아야 한다.
        val bundle = Bundle()
        bundle.putString("data1", "hello")
        
        val msg = Message()
        msg.what = 10
        msg.obj = bundle
        messenger.send(msg)

AIDL 통신 기법

  • AIDL(Android Interface Definition Language)

  • 두 프로세스 간 데이터를 주고받는 프로세스 간 통신을 구현할 때 사용하는 기법으로 bindService() 함수를 사용한다.

  • 메신저 대비 AIDL 의 장점

    • 시스템을 통해 데이터를 전달할 때 원시타입으로 바꿔주는 마샬링 과정을 자동으로 해준다.
    • API를 사용하는 메신저와 다르게 주고받는 데이터의 종류가 많은 때 효율적이다.
    • 메신저는 모든 요청을 싱글스레드로 처리하지만 AIDL은 멀티 스레드 환경에서 동시에 실행된다.

업로드중..

interface IMyAidlInterface {

    void funA(String data);
    int funB();
}
  • aidl 파일을 생성하면 위와같이 자바 인터페이스를 정의 할 수 있다.
  • 해당 함수의 구체적인 로직은 서비스 컴포넌트에서 구현한다.
class MyService : Service() {

    override fun onBind(intent: Intent): IBinder {
        return object : IMyAidlInterface.Stub(){
            override fun funA(data: String?) {
            }

            override fun funB(): Int {
                return 10
            }

        }
    }
}
  • 뒤에 Stub()를 붙여주는 이유는 IMyAidlInterface를 구현한 객체를 전달하는 게 아니라, 프로세스 간 통신을 대행해 주는 객체를 전달하기 위함이다.
  • 매니페스트 파일에 인텐트 필터를 적용해준다.
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="ACTION_AIDL_SERVICE"/>
            </intent-filter>
        </service>
  • 액티비티에서의 구현은 아래와 같이 해준다.
class MainActivity : AppCompatActivity() {

    lateinit var aidlService: IMyAidlInterface

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

        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(p0: ComponentName?, p1: IBinder?) {
            aidlService = IMyAidlInterface.Stub.asInterface(p1)
        }

        override fun onServiceDisconnected(p0: ComponentName?) {
        }

    }
}
  • 호출은 Service의 함수를 그대로 호출하면 된다.
aidlService.funA("hello")

0개의 댓글