Android 개발을 하면서 사용하는 Uri, Scheme, SSP(Scheme Specific PART)에 대해서 정리하였습니다. Intent는 data라는 정보를 갖고 있습니다. data는 Uri로 표현되며, Uri의 구성 요소 중에 scheme, SSP가 있습니다. scheme과 host 등은 어렵게 느껴지지 않지만 SSP(Scheme Specific Part)는 낯설게 느껴질 수 있습니다.
먼저 Intent에 대해서 간단히 설명하고, Uri와 Scheme, SSP를 소개하겠습니다.
Intent에 대한 간단한 설명
Android에서 이벤트를 주고 받을 때 Intent를 사용합니다. Intent에는 Data라는 아이템이 있는데요. Intent가 어떤 데이터를 갖고 있는지 표현하고, Broadcast 등을 전달할 때 어떤 리시버로 전달이 되어야하는지 나타낸다고 생각할 수 있습니다. Activity를 실행할 때도 Data의 내용에 따라서 어떤 Activity를 실행할지 결정됩니다.
Intent는 다음과 같이 생성하고 Data를 설정할 수 있습니다.
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri: Uri = Uri.parse("http://google.com")
intent.data = uri
위의 코드에서 Intent의 data를 설정할 때 Uri를 입력합니다. 즉, 데이터는 Uri이고 Uri가 어떤 데이터인지 표현합니다.
Uri가 갖고 있는 데이터 정보
Uri는 Uniform Resource Identifier라는 의미로, 리소스를 나타내는 식별자입니다. 인터넷에서 사용하는 Url은 Uri하위 개념입니다.
Uri은 다음과 같은 구조로 이루어져있습니다.
<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
위의 http://google.com
를 예로 들었는데요. 여기서 http
는 scheme, google.com
은 host가 됩니다.
port 및 path 등은 없습니다.
다음과 같이 Uri.parse()
를 이용하여 문자열을 Uri 객체로 만들 수 있습니다.
아래 코드와 같이 Uri 객체의 scheme, host 등을 출력해보면 잘 설정된 것을 볼 수 있습니다.
val uri: Uri = Uri.parse("http://google.com")
Log.d(TAG, "scheme: ${uri.scheme}, host: ${uri.host}, " +
"port: ${uri.port}, path: ${uri.path}")
Output:
MainActivity: scheme: http, host: google.com, port: -1, path:
Uri를 사용하는 이유
Uri는 리소스를 나타내는 식별자입니다.
예를 들어, 사용자가 http://google.com
이라는 링크를 눌렀을 때 앱은 이것을 인텐트로 만들고 startActivity()
를 호출합니다.
그리고 시스템은 Intent의 데이터를 처리할 수 있는 앱을 찾습니다. 이 때 Uri의 scheme, host 등으로 관련 있는 앱들을 찾습니다.
다음 코드를 실행시키면, 시스템은 이 Intent와 관련있는 앱들을 찾고 실행합니다. 이런 인텐트는 Browser들이 처리할 수 있기 때문에, 코드를 실행하면 Browser가 실행됩니다.
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri: Uri = Uri.parse("http://google.com")
intent.data = uri
startActivity(intent)
앱이 어떤 종류의 인텐트를 처리할 수 있는지 표현하는 방법 중 하나는 AndroidManifest의 IntentFilter입니다. 시스템은 AndroidManifest.xml을 읽어서 이 앱이 어떤 인텐트를 처리할 수 있는지 분류하고, 인텐트를 보낼 때 이런 정보들을 참조하여 누구에게 보낼지 결정합니다.
Chrome 앱의 Manifest를 보면 아래와 같이 Activity의 IntentFilter를 설정하였습니다.
조금 더 자세히 보시면, 위의 Intent가 갖고 있는 action, category가 동일하다는 것을 볼 수 있습니다.
또한, data 항목에 scheme이 http
로 정의되어있습니다. 이 data는 Uri를 의미하고, scheme이 http, https인 Uri는 모두 처리할 수 있다는 의미입니다. 즉, host가 어떤 것이 오든 중요하지 않습니다.
<activity android:name="com.google.android.apps.chrome.IntentDispatcher">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
이런 식으로 App은 Manifest에 처리할 수 있는 Uri를 선언하기 때문에 시스템은 Uri 정보를 비교하여 누구에게 Intent를 전달할지 결정할 수 있습니다.
만약 어떤 Activity는 host가 google.com
인 것만 처리할 수 있다면, 다음과 같이 IntentFilter를 설정할 수 있습니다. data 항목을 보시면 host가 설정되었습니다.
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http" android:host="google.com"/>
<data android:scheme="https" android:host="google.com"/>
</intent-filter>
SSP(Scheme Specific Part)
위에서 Uri는 아래와 같은 형식이라고 했습니다.
<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
하지만, 아래와 같은 형식으로도 사용합니다.
<scheme>:<scheme-specific-part>#fragment
첫번째 패턴과 비교했을 때 //
는 없습니다. :
의 오른쪽 부분을 SSP(Scheme Specific Part)라고 합니다. 그리고 #
의 오른쪽은 fragment라고 합니다.
Android에서 SSP 형식의 Uri를 사용할 때도 있습니다.
예를 들어, App이 설치가 완료되면 ACTION_PACKAGE_ADDED
인텐트가 전달됩니다.
Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.codechacha.myapplication flg=0x4000010 (has extras) }
이 인텐트의 Uri는 package:com.codechacha.myapplication
입니다. scheme은 package
이고 SSP는 com.codechacha.myapplication
입니다. fragment는 없습니다.
다른 예로, 전화를 걸 때 전달되는 Intent의 Uri는 tel:010-1111-1111
과 같은 형식입니다. tel
이라는 scheme이 있고, 그 scheme에 대한 자세한 정보를 SSP에 저장하고 있습니다.
sms
나 mms
나 모두 동일하게 SSP로 표현하고 있습니다.
IntentFilter에 SSP 정의
앱이 디바이스에서 삭제되면 PACKAGE_FULLY_REMOVED
라는 인텐트를 브로드캐스트로 보냅니다. 이 인텐트는 SSP를 사용하여 Uri를 표현하며 package:com.example.myapplication
처럼 SSP에 앱의 패키지 이름을 넣고 전달합니다.
만약 PACKAGE_FULLY_REMOVED
를 리시버에서 받고 싶다면 다음과 같이 scheme을 package
로 설정해야 합니다.
이렇게 설정하면 어떤 앱이 삭제될 때 그 이벤트를 받게 됩니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package"/>
</intent-filter>
</receiver>
하지만 만약 com.example.myapplication
라는 앱이 삭제되었을 때만 인텐트를 받고 싶다면 어떻게 해야 할까요?
IntentFilter에 ssp에 com.example.myapplication
로 설정하면 됩니다. 이렇게 설정하면 인텐트의 ssp와 인텐트필터의 ssp와 일치할 때만 전달이 됩니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" android:ssp="com.example.myapplication"/>
</intent-filter>
</receiver>
Manifest가 아닌 Context.registerReceiver()
로 리시버를 등록한다면 다음과 같이 IntentFilter 객체를 생성할 수 있습니다. SSP를 설정할 때 addDataSchemeSpecificPart()
를 사용하면 됩니다.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
intentFilter.addDataScheme("package")
intentFilter.addDataSchemeSpecificPart("com.example.myapplication",
PatternMatcher.PATTERN_LITERAL)
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("MyReceiver", "onReceive: $intent")
}
}
context.registerReceiver(receiver, intentFilter)
SSP로 Uri가 정의된 Intent를 전달
위의 예제들은 SSP로 설정된 Intent를 받기 위해 IntentFilter를 등록하는 방법들입니다. 반대로 내 앱에서 Intent를 보낼 수도 있습니다.
Uri에 SSP를 설정하고 싶다면, 다음과 같이 Uri.fromParts(scheme, ssp, fragment)
로 ssp가 설정된 Uri 객체를 만들 수 있습니다.
val intent = Intent("com.custom.intent.action")
intent.data = Uri.fromParts("package" /* scheme */,
"com.example.myapplication" /* ssp */,
null /* fragment */)
이렇게 인텐트를 보내면 다음과 같이 인텐트 필터를 설정한 앱으로 전달될 수 있습니다.
<intent-filter>
<action android:name="com.custom.intent.action" />
<data android:scheme="package" android:ssp="com.example.myapplication"/>
</intent-filter>
물론 scheme만 설정한 앱으로도 전달이 됩니다.
<intent-filter>
<action android:name="com.custom.intent.action" />
<data android:scheme="package"/>
</intent-filter>
Fragment가 사용되는 예제
Android 개발을 하면서 fragment가 사용되는 것은 못봤습니다. 혹시 보신분이 있다면 알려주세요.
정리
Intent의 Data를 표현하는 방식은 Uri와 Mimetype이 있습니다.
Mimetype은 여기서 소개하지 않았지만 데이터의 타입을 표현합니다. Uri는 <scheme>://<host>
형식으로 사용될 때도 있고 <scheme>:<scheme-specific-part>
형식으로 사용될 때가 있습니다.
각각 IntentFilter와 Intent 객체를 만드는 방법이 조금씩 다릅니다.
이 글을 읽으시면서 Intent를 조금이나마 이해하는데 도움이 되었으면 좋겠습니다.
Intent의 기본적인 내용에 대해서 좀 더 알고 싶으시다면 다음 글도 읽어보시는 것을 추천드립니다.
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력