Idealim
article thumbnail

/* 본 게시물은 '이것이 안드로이드다 with Kotlin | 고돈호 지음' 의 내용을 토대로 작성되었습니다. */


# 4대 컴포넌트

4개의 핵심 컴포넌트

컴포넌트는 안드로이드 앱을 구성하는 요소인데, 안드로이드는 4개의 핵심 컴포넌트를 제공한다. 컴포넌트는 독립적인 생명 주기에 의해서 실행된다.

  • 액티비티 : 화면 UI를 담당하는 컴포넌트
  • 브로드캐스트 리시버 : 시스템 또는 사용자가 발생하는 메시지를 수신하는 컴포넌트
  • 서비스 : 백그라운드 코드 처리를 담당하는 컴포넌트 (서브 스레드와 비슷한 개념으로 화면이 없는 Activity라 생각하면됨)
  • 콘텐트 프로바이더 : 앱 간의 데이터 공유를 위한 컴포넌트
  • 인텐트 : 액티비티, 브로드캐스트 리시버, 서비스 3개의 컴포넌트를 실행하기 위해 시스템에 전달되는 메시지 도구
  • 콘텐트 리졸버 : Content Provider가 제공하는 데이터를 사용하기 위한 도구

 

# 액티비티

액티비티는 사용자가 직접 보고 입력하는 화면을 담당하는 컴포넌트이다. 레이아웃을 화면에 그리기 위해서는 액티비티를 통해서만 가능하기 때문에 레이아웃에서 액티비티를 사용했다.

메이저 컴포넌트 중에 하나인 액티비티를 다루기 위해서는 먼저 컴포넌트를 구성하고 있는 핵심 요소인 컨텍스트에 대한 이해가 선행되어야 한다.

 

# 컨텍스트란?

컨텍스트는 시스템을 사용하기 위한 정보(프로퍼티)와 도구(메서드)가 담겨 있는 클래스이다. 대부분의 컨텍스트는 컴포넌트 실행 시 함께 생성되고, 생성된 컴포넌트가 가지고 있는 메서드를 호출해서 각각의 도구들을 사용할 수 있다.

안드로이드에서의 컨텍스트는 앱을 실행하기 위해 잘 짜여진 설계도의 개념으로 앱에서 사용하는 기본 기능이 담겨있는 기본 클래스이다. 액티비티는 컨텍스트를 상속받아 구현된다. ( Activity : Context() ) 액티비티처럼 컨텍스트를 상속받은 컴포넌트들은 코드상에서 baseContext를 호출하는 것만으로 안드로이드의 기본 기능을 사용할 수 있다.

 

예로 액티비티 안에서 startActivity() 메서드를 통해 다른 액티비티를 호출할 수 있는 것도 모든 액티비티가 startActivity()가 설계되어 있는 컨텍스트를 상속받아서 구현되어 있기 때문이다.


# 컨텍스트의 종류

컨텍스트는 애플리케이션 컨텍스트와 베이스 컨텍스트가 있다.

  • Application Context : 애플리케이션과 관련된 핵심 기능을 담고 있는 클래스이다. 앱을 통틀어서 하나의 인스턴스만 생성된다. 액티비티나 서비스 같은 컴포넌트에서 applicationContext를 직접 호출해서 사용할 수 있는데 호출하는 지점과 관계없이 모두 동일한 컨텍스트가 호출된다.
  • Base Context : 안드로이드의 4대 메이저 컴포넌트인 액티비티, 서비스, 컨텐트 프로바이더, 브로드캐스트 리시버의 기반 클래스이다. 각각의 컴포넌트에서 baseContext 또는 this로 컨텍스트를 사용할 수 있고 컴포넌트의 개수만큼 컨텍스트도 함께 생성되기 때문에 호출되는 지점에 따라 서로 다른 컨텍스트가 호출된다.

# 인텐트

액티비티를 실행하기 위해서는 단순히 컨텍스트가 제공하는 메서드를 호출하면 되는데, 이 때 실행할 액티비티가 명시된 인텐트를 해당 메서드에 전달해야한다. 인텐트는 그대로 직역하면 '의도' 라고 해석할 수 있는데, 개발자가 어떤 의도를 가지고 메서드를 실행할 것인지를 인텐트에 담아서 안드로이드에 전달하면 안드로이드는 해당 인텐트를 해석하고 실행한다. 인텐트는 안드로이드 프로그래밍을 하는데 있어서 컨텍스트와 함께 꼭 알고 있어야 하는 개념이다.

액티비티를 실행하려면 기본적으로 인텐트가 필요하지만, 프로젝트를 생성할 때 함께 만들어지는 MainAcitivty는 특별한 설정을 하지 않아도 안드로이드에 자동으로 등록되고 실행된다. 하지만 MainActivity 외에 다른 액티비티를 사용할 때는 인텐트에 새 액티비티의 이름을 담아서 시스템에 전달한다. 다음은 새로 생성된 액티비티를 실행하기 위해 인텐트가 전달되는 과정이다.

  1. 실행할 대상의 액티비티 이름과 전달할 데이터를 담아서 인텐트를 생성한다.
  2. 생성한 인텐트를 startActivity() 메서드에 담아서 호출하면 액티비티 매니저에 전달된다.
  3. 액티비티 매니저는 인텐트를 분석해서 지정한 액티비티를 실행시킨다.
  4. 전달된 인텐트는 최종 목적지인 타킷 액티비티까지 전달된다.
  5. 타깃 액티비티에서는 전달받은 인텐트에 데이터가 있다면 이를 꺼내서 사용할 수 있다.

# 액티비티 사이에 값 주고받기

액티비티와 같은 컴포넌트는 인텐트에 실행 메시지도 전달하지만 인텐트를 통해 데이터도 주고받을 수 있다. 인텐트 내부에는 번들이라는 데이터 저장 공간이 있는데, 이 번들에 데이터를 담아서 주고 받을 수 있다.

인텐트에 값을 입력할 때는 키와 값의 조합으로 번들에 직접 넣고, 꺼낼 때는 처음 입력했던 키로 꺼낸다. 마치 코틀린 문법에서 공부했던 맵처럼 동작한다. putExtra("키", "값")

예시)

ProductListViewModel.onClickProduct() 

override fun onClickProduct(productId: Long?) {
        startActivity<ProductDetailActivity>{
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
            putExtra(ProductDetailActivity.PRODUCT_ID, productId)
        }
  • intent.putExtra("product_id", productId)를 이용해 ProductDetailActivity에 productId 값을 전달한다.

ProductMainActivity

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val productId = intent.getLongExtra(PRODUCT_ID, -1)
        }
  • productId이 Long형이므로 getLongExtra(key, default value)로 인텐트에 있는 값을 꺼낸다.

# 메인 액티비티에서 값 돌려받기

이번에는 반대로 서브 액티비티가 종료되면 메인 액티비티로 값을 돌려주는 코드를 작성해보자.

 

 

예시)

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)
          btnStart.setOnClickListener { startActivityForResult(intent, 99) }
}
  • 99는 requestCode 값으로 onActivityResult() 콜백 함수를 호출할 때 필요하다. 

SubActivity.onCreate()

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

        btnClose.setOnClickListener {
            val returnIntent = Intent()
            returnIntent.putExtra("returnValue", editMessage.text.toString())
            setResult(Activity.RESULT_OK, returnIntent) // returnIntent와 상태값을 setResult() 메서드에 담아서 실행하면 호출한 측으로 전달된다. 상태 값은 RESULT_OK와 RESULT_CANCLED로 안드로이드에 이미 상수로 정의되어 있다.
            finish()
        }
  • setResult(상태값, 전달하려는 인텐트) : setResult()는 호출한 측으로 인텐트값을 전달하는 함수이다. 상태값으로는 RESULT_OK를, 실패이거나 취소되었으면 CANCELED를 사용하면 된다. 이어서 finish() 메서드를 호출해서 액티비티를 종료한다.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
                99 -> {
                    val message = data?.getStringExtra("returnValue")
                    Toast.makeText(this, message, Toast.LENGTH_LONG).show()
                }
            }
        }

onActivityResult(requestCode, resultCode, data) 메서드의 구조

  • requestCode : 호출 시에 메인 액티비티에서 입력하는 startActivityForResult 메서드에 인텐트와 함께 입력해서 호출한 코드(또는 버튼)를 구분한다. 
  • resultCode : 결과 처리 후 서브 액티비티에서 입력하는 코드. 앞에서 RESULT_OK를 담아서 보냈다.
  • data : 결과 처리 후 서브 액티비티가 넘겨주는 인텐트가 담겨있다.

 

반응형
profile

Idealim

@Idealim

읽어주셔서 감사합니다. 잘못된 내용이 있으면 언제든 댓글로 피드백 부탁드립니다.