안드로이드 4대 컴포넌트 :
- 액티비티
- 서비스
- 브로드캐스트 리시버
- 콘텐츠 프로바이더
액티비티 컴포넌트
- Intent : 컴포넌트를 실행하려고 시스템에 전달하는 메시지
- 엑스트라 데이터 : 인턴트에 담는 부가 정보
인텐트 필터
- 명시적 인텐트 : 클래스 타입 레퍼런스 정보를 활용한 인텐트
- 암시적 인텐트 : 인텐트 필터 정보를 활용한 인텐트
- 인텐트 필터 하위에 설정 가능한 태그
- <action> 컴포넌트의 기능을 나타내는 문자열
- <category> 컴포넌트가 포함되는 범주를 나타내는 문자열
- <data> 컴포넌트에 필요한 데이터 정보
액티비티 생명주기
- 생명주기 : 액티비티가 생성되어 소멸되기까지의 과정
- 액티비티 상태
- 활성 : 액티비티 화면이 출력되고 있고 사용자가 이벤트를 발생시킬 수 있는 상태
액티비티가 포커스를 가지는 상태 - 일시 정지 : 액티비티의 화면이 출력되고 있지만 사용자가 이벤트를 발생시킬 수 없는 상태
onPause()함수까지 호출된 상태
일반적으로 액티비티가 화면에 보이지만 포커스를 잃어 사용자 이벤트를 처리할 수 없는 상태
ex) 화면 분할 모드 - 비활성 : 액티비티의 화면이 출력되고 있지 않는 상태
액티비티가 종료되지 않고 화면에만 보이지 않는 상태, onPause() -> onStop() 까지 호출됨.
다시 실행시 onRestart() -> onStart() -> onResume() 호출(활성상태)
ex) 인텐트로 다른 액티비티를 실행했거나 홈 버튼을 눌러 런처 화면으로 이동하는 경우
- Bundle 객체 : 액티비티를 종료할 때 저장했다가 복원해야 할 데이터가 있을때 사용할 수 있는 객체
액티비티 제어
- InputMethodManager 클래스 : 특정한 순간에 키보드를 들어올리거나 내려야 할 경우 사용가능
- showSoftInput() : 소프트 키보드 나타남
- hideSoftInputFromWindow() : 소프트 키보드 사라짐
=> 에디트 텍스트 같은 뷰에 포커스가 없는 상태에서 키보드가 나타나게 하려면 포커스를 강제로 지정{requestFocus()}한 후 showSoftInput() 함수를 호출해야함
태스크 관리
- 태스크(Task) 관리 : 액티비티를 어떻게 생성하고 관리하는지를 제어하는 일
- 액티비티 태스크 : 앱이 실행될 때 시스템에서 액티비티의 각종 정보를 저장하는 공간
- 태스크 제어
- 액티비티가 등록되는 매니페스트 파일의 <activity> 태그의 launchMode 속성으로 실행 모드를 설정
=> 항상 설정한 대로 생성되어 태스크에 등록 - 인텐트의 flags 정보를 설정하여 제어
=> 이번 인텐트가 발생할 때에 한 번만 적용되어 태스크에 등록
액티비티 ANR 문제와 코루틴
- ANR 문제 : 액티비티가 응답하지 않는 오류 상황
=> 액티비티가 사용자 이벤트에 5초 이내에 반응하지 않으면 발생
val channel = Channel<Int>()
val backgroundScope = CoroutineScope(Dispatchers.Default + Job())
backgroundScope.launch{
var sum = 0L
var time = measureTimeMillis{
for(i in 1..2_000_000_000){
sum += i;
}
}
Log.d("kkang","time : $time")
channel.send(sum.toInt())
}
val mainScope = GlobalScope.launch(Dispatchers.Main){
channel.consumeEach{
binding.resultView.text = "sum : $it"
}
}
- 코루틴 : 비동기 경량 스레드
=> 장점 : 경량, 메모리 누수 적음, 취소 등 다양한 기능 제고 , 많은 제트팩 라이브러리에 적용되어 있음.
- 스코프 : 성격이 같은 코루틴을 묶는 개념, 성격이 같은 코루틴이 동작하는 공간 정도
- 디스패처 : 스코프에서 구동한 코루틴이 어디에서 동작해야 하는지를 나타냄
- Dispatchers.Main : 액티비티의 메인 스레드에서 동작하는 코루틴을 만듬
- Dispatcher.IO : 파일에 쓰기 또는 네트워크 작업 등에 최적화됨
- Dispatcher.Default : CPU를 많이 사용하는 작업을 백그라운드에서 실행
브로드캐스트 리시버 컴포넌트
- 브로드캐스트 리시버 : 이벤트(부팅이 완료되는 것과 같은 시스템의 특정한상황) 모델로 실행되는 컴포넌트
- 액티비티나 서비스에서 생성하는 경우 : 항상 실행 / 암시적 인텐트로 실행 가능
- 매니페스트에 등록해서 생성하는 경우 : 특정 상황에서 실행 / 암시적 인텐트로 실행 불가능
- 액티비티 인텐트는 시스템에 해당 인텐트로 실행될 액티비티가 없으면 오류, 실행될 액티비티가 여러개 있으면 사용자의 선택으로 하나만 실행
- 크로드캐스트 리시버는 시스템에 해당 인텐트로 실행될 리시버가 없으면 아무일 x, 시행될 리시버가 여러 개 있으면 모두 실행
서비스 컴포넌트
- 서비스 : 백그라운드 작업을 목적으로 하는 컴포넌트
- startService() 함수로 실행
해당 서비스를 인텐트에 담아서 매개변수로 전달
=> 백그라운드 작업은 필요하지만 액티비티와 데이터를 주고받을 일이 없는 등 서로 관련이 없는 서비스
val intnet = Intent(this, MyService::class.java)
startService(intent)
stopService(intent)
- bindService() 함수로 실행
ServiceConnection 인터페이스를 구현한 객체 준비 필요
=> 서비스와 액티비티 사이에 상호작용이 필요한 서비스
val connection: ServiceConnection = object : ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {}
override fun onServiceDisconnected(name: ComponentName?) {}
}
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
unbindService(connection)
- 서비스 생명주기
- IBindier : onBind() 함수의 반환 타입
//서비스 코드
class MyBinder : Binder(){
fun funA(arg: Int){
}
fun funB(arg: Int): Int{
return arg*arg
}
}
override fun onBind(intent: Intent): IBinder?{
return MyBinder()
}
//액티비티 코드
val connection: ServiceConnection = object : ServiceConnection{
override fun onServiceConnected(name: ComponetName?, service: IBinder?){
servideBinder = service as MyService.MyBinder
}
override fun onServiceDisconnected(name: ComponentName?){
}
}
servideBinder.funA(10)
- 메신저 바인딩
//서비스 코드
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
}
}
// 액티비티 코드
class MainActivity : AppCompatActivity(){
lateinit var messenger: Messenger
override fun onCreate(savedInstanceState: Bundle?){
supter.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?){
}
}
}
//서비스에 데이터 전달하고 싶을 때
val msg = Messenge()
msg.what = 10
msg.obj = "hello"
messenger.send(msg)
- AIDL 통신 기법 : 두 프로세스 사이에 데이터를 주고받는 프로세스 간 통신을 구현할 때 사용하는 기법으로, 서비스 컴포넌트의 bindService() 함수를 사용
백그라운드 제약
- 브로드캐스트 리시버의 백그라운드 제약
매니페스트에 등록한 리시버를 암시적 인텐트로 실행할 때에는 같은 앱의 리시버든 외부 앱의 리시버든 실행되지 않음
- 서비스의 백그라운드 제약
서비스는 앱이 백그라운드 상태일 때 인텐드를 전달하면 오류 발생
서비스가 정상적으로 실행되는 포그라운드 상황
- 액티비티가 시작되든 일시 중지되든 상관없이 보이는 액티비티가 있을 때
- 포그라운드 서비스가 있을 때
- 앱의 서비스에 바인딩하거나 앱의 콘텐츠 프로바이더를 사용해 또 다른 포그라운드 앱이 연결되었을 때
이외에는 백그라운드 상황으로 간주함.
백그라운드 상황에도 정상 실행되는 경우
- 우선순위가 높은 파이어베이스 클라우드 메시징(FCM) 처리
- SMS/MMS 서비스와 같은 브로드캐스트 수신
- 알림에서 PendingIntent 실행
- VPN 앱이 포그라운드로 승격되기 전에 VpnService 시작
백그라운드 상황이더라도 startForground() 함수를 사용하여 앱을 포그라운드 상황으로 만들면 됨.
- 잡 스케줄러
- 잡 서비스 : 백그라운드에서 처리할 작업을 구현한 서비스
- 잡 인포 : 잡 서비스 정보와 실행될 조건을 지정
- 잡 스케줄러 : 잡 인포를 시스템에 등록
콘텐츠 프로바이더 컴포넌트
- 콘텐츠 프로바이더 : 앱의 데이터를 다른 앱과 공유할 때 사용
콘텐츠 프로바이더는 필요한 순간에 시스템에서 자동으로 생성해줌.
'전공공부 > 모바일 프로그래밍' 카테고리의 다른 글
책 "아키텍처를 알아야 앱 개발이 보인다" #2 (0) | 2022.04.06 |
---|---|
책 "아키텍처를 알아야 앱 개발이 보인다" #1 (0) | 2022.04.06 |
kotlin 공부 #2 (0) | 2022.03.22 |
looper (0) | 2021.08.31 |
23일차 (0) | 2021.07.28 |