인텐트란?
컴포넌트를 실행하려고 시스템에 전달하는 메시지
기능을 수행하는 함수를 제공하는 클래스가 아니라 데이터를 담는 클래스
이 때, 데이터는 컴포넌트를 실행하는 정보이며
이 정보가 담긴 인텐트 객체를 시스템에 전달하면 컴포넌트가 실행된다
* 액티비티는 매니페스트 파일에 등록해야 한다.
액티비티 클래스 하나당 <activity> 태그 하나로 등록해야 하며, name 속성은 생략 불가능하다.
* 액티비티뿐만 아니라 서비스, 브로드캐스트 리시버, 콘텐츠 프로바이더도 매니페이스 파일에 등록해야 한다.
그 이유는 시스템에 컴포넌트를 알려야 하기 때문이다.
시스템은 런타임 때 매니페스트 파일의 정보를 참조하여 앱을 실행한다.
val intent: Intent = Intent(this, DetailActivity::class.java)
startActivity(intent)
(인텐트를 시스템에 전달)
startActivity() 함수가 인텐트를 시스템에 전달한다.
이 함수의 매개변수에는 시스템에 실행을 요청할 컴포넌트의 정보가 담긴 Intent 객체를 전달한다.
매개변수로 등록된 DetailActivity::class.java는 클래스 타입 레퍼런스 정보이다.
결국, DetailActivity를 실행해 달라는 정보를 Intent 객체에 대입한 것이다.
인텐트 엑스트라 데이터
인텐트에 담는 부가 정보
한 액티비티에서 인텐트를 이용해 다른 액티비티를 실행할 때 데이터를 전달해야 한다면 어떻게 해야할까?
받는 액티비티에서 함수를 하나 선언하고 보내는 액티비티에서 함수를 호출하면서
매개변수로 데이터를 넘기는 방법을 생각할 순 있지만 이 방법은 불가능하다.
왜냐하면 컴포넌트 객체는 시스템이 생성하므로 개발자 코드로는 직접 접근할 수 없기 때문이다.
따라서 컴포넌트 객체로 어떤 함수를 호출할 수는 없다.
인텐트에 컴포넌트 실행을 요청할 때 데이터를 함께 전달하려면 엑스트라 데이터를 이용해야 한다.
MainActivity에서 인텐트에 엑스트라 데이터를 추가해서 전달하고
SubActivity에서 인텐트에 담긴 엑스트라 데이터를 가져오는 구조
인텐트에 엑스트라 데이터를 추가하는 함수는 putExtra()이다.
public Intent putExtra(String name, CharSequence value)
이 때, name은 데이터 식별자이며, value가 전달할 데이터이다.
val intent: Intent = Intent(this, SubActivity::class.java)
intent.putExtra("data1", "hello")
intent.putExtra("data2", 10)
startActivity(intent)
인텐트로 실행된 컴포넌트에서 엑스트라 데이터를 가져오려면 먼저 getIntent() 함수로
자신을 실행한 인텐트 객체를 얻어야 한다.
public Intent getIntent()
코틀린 프로그램에서는 getIntent() 함수를 인텐트 객체의 프로퍼티로 이용하면 된다.
그리고 그 인텐트 객체의 get타입Extra() 함수로 데이터를 가져오면 됩니다.
- public int getIntExtra(String name, int defaultValue)
- public String getStringExtra(String name)
- public double getDoubleExtra(String name, double defaultValue)
액티비티 화면 되돌리기
어떤 액티비티가 다른 액티비티를 실행해 화면이 전환되었을 때 의도에 따라
화면을 되돌리거나 되돌리지 않을 수 있다.
이런 상황을 고려해 액티비티를 시작하는함수는 2개이다.
# 화면을 되돌릴 필요가 없을 때
public void startActivity(Intent intent)
# 결과를 포함해 화면을 되돌릴 때
public void startActivityForResult(Intent intent, int requestCode)
두 번째 매개변수인 requestCode는 개발자가 정하는 요청 코드이며 인텐트를 식별하는 값이다.
이 값은 startAcitivityForResult()함수로 결과를 돌려받은 후 별도로 처리할 때 필요하다.
intent.putExtra("resultData", "world")
setResult(RESULT_OK, intent)
finish()
위 코드는 startActivityForResult() 함수로 시작한 액티비티에서 결과를 되돌리는 코드이다.
사용자가 뒤로가기 버튼을 누르지 않고 자동으로 화면을 되돌릴 때는 finish() 함수를 이용한다.
setResult() 함수로는 결과를 어떻게 되돌릴지 지정할 수 있다.
요청을 제대로 처리했으면 RESULT_OK, 아니면 RESULT_CANCELED 등 상수를 지정한다.
이 값을 결과 코드(resultCode)라고 한다.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == 10 && resultCode == Activity.RESULT_OK) {
val result = data?.getStringExtra("resultData")
}
}
(결과를 돌려받은 후 처리)
위 코드는 처음 인텐트를 시작한 액티비티에 작성되었다.
결과가 되돌아와서 다시 자신이 화면에 출력되면 onActivityResult() 함수가 자동으로 호출된다.
이 함수의 매개변수는 다음과 같다.
- requestCode : 인텐트를 시작한 곳에서 인텐트를 구분하려고 설정한 요청 코드이다
- resultCode : 인텐트로 실행된 곳에서 돌려받은 결과 코드이다
- data : 인텐트 객체이다. 이 객체에 결과 데이터가 있다
인텐트 필터
앱의 매니페스트 파일에 들어있는 표현. 해당 구성 요소가 수신하고자 하는 Intent의 유형을 나타냄.
인텐트는 실행할 컴포넌트 정보를 어떻게 설정하는지에 따라 2가지로 나뉜다.
1) 명시적 인텐트 : 클래스 타입 레퍼런스 정보를 활용한 인텐트
내부 앱의 컴포넌트를 요청하는 인텐트 객체를 만들 때 사용
2) 암시적 인텐트 : 인텐트 필터 정보를 이용한 인텐트
외부 앱의 컴포넌트는 클래스 타입 레퍼런스를 활용할 수 없으므로 암시적 인텐트 이용
<activity android:name=".OneActivity" />
<activity android:name=".TwoActivity">
<intent-filter>
<action android:name="ACTION_EDIT" />
</intent-filter>
</activity>
OneActivity는 매니페스트 파일에 등록될 때 name 속성만 지정되어 있어 명시적 인텐트로만 실행할 수 있다.
TwoActivity의 경우 <intent-filter>가 설정되어 있어 암시적 인텐트로도 실행할 수 있다.
인텐트 필터 하위 정보 설정
- <action> : 컴포넌트의 기능을 나타내는 문자열
- <category> : 컴포넌트가 포함되는 범주를 나타내는 문자열
- <data> : 컴포넌트에 필요한 데이터 정보
액티비티 인텐트 동작 방식
인텐트로 실행할 액티비티가 시스템에 없거나 여러 개라면 어떻게 될까?
명시적 인텐트는 클래스 타입 레퍼런스 정보를 이용하므로 액티비티가 없거나 여러 개일 수 없다.
그러나, 암시적 인텐트는 실행할 액티비티를 <action>, <category>, <data> 등의 문자열 정보로 나타내므로
없거나 여러 개일 수 있다.
액티비티가 없을 때
오류가 발생한다.
따라서, 다음과 같이 예외 처리를 해주어야 한다.
val intent = Intent("ACTION_HELLO")
try{
startActivity(intent)
} catch (e: Exception) {
Toast.makeText(this, "no app...", Toast.LENGTH_SHORT).show()
}
액티비티가 여러 개일 때
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.7749, 127.4194"))
startActivity(intent)
위 코드는 지도 앱의 액티비티를 실행하는 인텐트이다.
그런데, 사용자의 폰에 위와 같은 정보로 등록된 지도 앱이 여러개라면 다이얼로그를 띄워
사용자에게 어느 앱의 액티비티를 실행할 것인지를 묻고 선택한 액티비티를 실행해야 한다.
만약 사용자에게 묻지 않고 특정 앱의 액티비티를 실행하고 싶다면
setPackage() 함수로 실행할 앱의 패키지를 지정하면 된다.
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.7749, 127.4194"))
intent.setPackage("com.google.android.apps.maps")
startActivity(intent)
패키지 공개 상태
안드로이드 11(API 레벨 30) 버전부터는 앱의 패키지 공개 상태를 지정하지 않으면
외부 앱의 패키지 정보에 접근할 수 없게 되었다.
인텐트를 이용해 외부 앱과 연동하는 부분에는 영향이 없지만 아래의 함수들을 사용할 때는 영향을 받는다.
PackageManager.getPackageInfo()
PackageManager.queryIntentActivities()
Intent.resolveActivity()
PackageManager.getInstalledPackages()
PackageManager.getInstalledApplications()
bindService()
오류 없이 정상적으로 실행하려면 매니페스트 파일에
외부 앱의 정보를 접근하겠다고 선언해주어야 한다.
<manifest ... 생략 ... >
<queries>
<package android:name="com.example.test_outter" />
</queries>
</manifest>
<queries> 태그 하위에 <package> 태그를 이용해 접근하고자 하는 앱의 패키지명을 선언하고 실행하면
오류없이 앱의 정보에 접근할 수 있다.
만약 여러 앱에 접근해야 할 경우 <package> 태그를 여러 번 선언하면 되는데,
<queries> 태그를 사용하지 않고 모든 외부 앱의 정보에 접근할 수 있도록 허용해 달라는 의미로
<uses-permission>을 선언할 수도 있다.
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
다만, <queries> 태그를 활용하는 것이 조금 더 권장된다.
=> 주의
- 앱의 보안을 보장하기 위해 구성 요소 중 서비스에 대한 암시적 인텐트는 하지 않는 것이 좋음
-> Service는 사용자 인터페이스 없이 백그라운드에서 작업을 수행하는 구성 요소라서
사용자가 어느 서비스가 시작되는지 눈으로 확인할 수 없기 때문
-> 결론적으로, 서비스를 시작하려면 항상 명시적 인텐트만 사용하고
암시적 인텐트를 위한 서비스 인텐트 필터는 선언하지 말 것
'Android' 카테고리의 다른 글
[Android] 코루틴 Coroutine (0) | 2022.04.01 |
---|---|
[Android] 액티비티 컴포넌트 - 액티비티 ANR 문제와 코루틴 (0) | 2022.01.05 |
[Android] 액티비티 컴포넌트 - 액티비티 생명주기 (0) | 2022.01.05 |
[Android] 안드로이드 앱의 기본 구조 (0) | 2022.01.04 |
[AndroidStudio] Android Studio 업데이트 오류 해결 (missing essential plugin org.jetbrains.android please reinstall android studio from scratch) (0) | 2021.08.01 |