Notice
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 안드로이드 gcm
- xe
- WebView
- curl
- android 효과음
- C# IO
- 우분투
- php 취약점
- html5
- FCM
- Android
- Mail Server
- mysql
- 폼메일
- 안드로이드 푸시
- 설치
- 자동 생성
- C#
- php 시큐어코딩
- UML
- PHP
- 자바스크립트
- not working
- dovecot
- 안드로이드 푸쉬
- roundcube
- 안드로이드
- soundpool
- chart.js
- javascript
Archives
- Today
- Total
그러냐
안드로이드 Service 에서 Activity 를 실행하는 방법 본문
반응형
<안드로이드 Task 는 저를 늘 곤혹스럽게 만들곤 합니다.>
안드로이드에서 어플리케이션을 개발하다보면 코드 상으로는 될 거같지만, 막상 실행 시켜보면 오류가 발생하는 경우가 있습니다. Service 를 하나 만들어, 인터넷에서 음악 파일을 다운 받고, 파일 다운로드가 종료되면 뮤직 플레이어를 실행시키는 일도 그런 일 중에 하나입니다. 안드로이드 플랫폼 상에서는 Activity 가 아닌 Service 혹은 BroadcastReceiver 에서 Activity 를 하나 새롭게 생성하려고 할 경우에는 다음과 같은 생소한 예외를 만나게 됩니다.
ERROR/AndroidRuntime(): Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
간단히 살펴보면, Activity 가 아닌 곳에서 startActivity()를 호출 하지 말고, 만일 호출하고자 할 때면 FLAG_ACTIVITY_NEW_TASK 를 사용하라는 말입니다. 그리고 마지막에는 왠지 협박조로, 그게 정말 너가 원하는거냐? 라고 끝을 맺네요.
그런데 어째서 이런 오류가 발생하는 것일까요?
안드로이드 플랫폼에서 어플리케이션의 UI 가 어떻게 구성되어지는지 조금 살펴볼 필요가 있습니다. 안드로이드 어플리케이션 UI의 핵심 요소는 두 가지 Activity 와 Task 입니다. Activity 는 개별 개별의 화면을 나타낸다고 생각할 수 있고, 어플리케이션의 진행에 따라 서로 상호작용하는 Activity 가 차곡 차곡 쌓여있는 것을 Task 라고 할 수 있습니다. 용어가 좀 헷갈리네요. 안드로이드 개발자 사이트에서 Task 에 관한 내용을 살펴보면, "Stack 형태(Last In First Out)로 배치된 서로 연관된 Activity 의 그룹" 이라고 되어 있습니다. 결국 기능적으로는 'Activity Stack' 이라고 생각하셔도 될 듯 합니다.
<카드 놀이를 상상하면, 안드로이드 Activity 와 Task 를 이해하기 쉽습니다.>
윈도우에 깔려있는 두번째로 훌륭한 어플리케이션 (첫번째는 지뢰찾기) 카드놀이를 상상해보면 좀 더 이해하기 쉽습니다. 한장의 카드는 하나의 Activity 입니다. 여러장의 카드가 뒤섞여져 있는 카드 뭉치는 실행 전의 Activity 들이라고 생각할 수 있습니다. 그리고 화면 하단, 여러장의 카드가 일정한 규칙을 가지고 쌓여있는 덱은 Task 라고 상상할 수 있습니다. 카드 놀이에서 새로운 카드를 한장 뽑아 덱에 추가하는 일은 다음의 과정을 거칩니다.
- 우선 카드 뭉치에서 카드를 한장 선택합니다.
- 기존에 카드가 놓인 덱에는 규칙에 따라 카드를 놓습니다.
비어있는 덱에는 아무 조건 없이 카드를 추가할 수 있습니다.
잘못된 비유는 안하는 것만 못할 때가 있는 거 같습니다 ㅠㅠ;;; 카드놀이에서 빈 덱에 카드를 추가하기 위해서는 K 카드만이 가능하네요. 지적해주신 나비님 감사합니다~~! 카드게임에 대한 마지막 비유는 잊어 주시고;;; '안드로이드에서 새로운 TASK 를 생성하는데는 별다른 조건이 필요하지 않고, 그저 카드 한장(즉 Activity 하나) 만이 필요할 뿐이다.' 점만 기억해 주세요~~
새로운 Activity 가 Task 에 추가되는 것도 거의 동일합니다.
- 우선 실행하고자 하는 Activity 를 선택합니다. (Intent 를 통해)
- 특정 규칙에 부합한다면, 이미 존재하는 Task 에 Activity 를 추가합니다.
- 새로운 Task를 생성한다면 아무 조건없이 Activity 를 추가 할 수 있습니다.
카드놀이에서 기존의 덱에 카드를 추가하기 위한 조건이 '무늬의 색깔이 달라야 하고, 숫자가 작아야 한다' 라고 한다면, 새로운 Activity 는 이미 Task 에 존재하는 다른 Activity 가 해당 Activity 를 실행 시키는 경우에, 기존 Task 에 추가 됩니다. (Activity 의 속성 중, allowTaskReparenting 값에 의해 달라질 수도 있습니다...)
결국, 오류가 발생하는 원인은 굉장히 단순 합니다. Activity 를 Launch 시키기 위해서는 어떤 Task 위에 해당 Activity 를 추가할지 알아야 합니다. 그런데 Service 는 어떠한 Task 에도 속하지 않습니다. (오직 Activity 만이 Task 로 관리됩니다.) 따라서 Service 에서 Intent 를 날려 안드로이드 ActivityManagerService 에 Activity 를 "Activity 실행시켜 주세요" 라고 말한다면, 안드로이드 시스템은 다음과 같이 대답하게 됩니다. "응? 어디다가 실행시켜달란 말이냐? 혹시 완전 새로운 Task 에다가 실행시켜 주랴? 그렇다면 확실하게 FLAG_ACTIVITY_NEW_TASK 플래그를 써서 알려달라. 오바"
그럼 이 문제는 어떻게 해결 할 수 있을까요? 두 가지 방법이 있습니다.
첫번째, 오류 발생 시 알려주는 내용대로 FLAG_ACTIVITY_NEW_TASK 플래그를 사용하면 됩니다. 만일 실행 시키려고 하는 Activity 가 이미 작동중인 Task 가 있다면 해당 Task 에, 그렇지 않다면 아예 새로운 Task 에 Activity 를 위치 시키라는 뜻입니다.
Intent i = new Intent(this, ServiceTest.class);i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(i);
두번째로 PendingIntent 를 사용 하는 방법이 있습니다. PendingIntent 는 '위임된 Intent' 로 만일 Service 를 시작시키는 Activity 가 해당 Service 에 타겟 Activity 를 Launch 시키기 위한 PendingIntent 를 생성해서 Service 에 넘겨주게 되면, Service 는 적절한 시점에 전달받은 PendingIntent 를 send 할 수 있습니다. 이 경우, 실재 안드로이드 플랫폼에 Intent 를 전달하는 Sender 는 권한을 위임받은 Service 가 아니라 Service 에 권한을 위임한 Activity 가 되는 셈임으로 별 문제없이 새로운 Activity 를 시작할 수 있습니다. NotificationManager 에서 주로 사용하고 있는 방식입니다.
그런데 개발자들이 조금 헷갈리게도, PendingIntent 를 사용해 Service 에서 Activity 를 실행 시킬 수 있는 또다른 방법이 있습니다. 바로 Service 에서 직접 PendingIntent 를 선언해서 사용하는 방법입니다.
Intent i = new Intent(this, ServiceTest.class);PendingIntent p = PendingIntent.getActivity(this, 0, i, 0);try {p.send();} catch (CanceledException e) {e.printStackTrace();}
어라? 이 코드는 어째서 작동하는 것일까요? PendingIntent 를 만든다고 안드로이드 플랫폼에서 어떻게 Service 에서 요청한 Activity 시작 명령을 처리해 줄 수 있는걸까요? 해답은 고양이(LogCat)가 알고 있습니다. 로그를 살펴보니, PendingIntent 의 경우에는 ActivityManager 가 강제로 FLAG_ACTIVITY_NEW_TASK 를 추가해 준다고 하네요.
잘못된 PendingIntent 때문에 발생하는 오류의 경우, 실재 죄를 지은 사람(Intent 생성자)과 죄에 인해 피해를 보는 사람(Intent 송신자)이 다릅니다. 때문에, 그저 Intent 를 전달하는 일만 부탁받은 죄없는 Component 가 괜히 잘못을 덤탱이 쓰지 않도록 안드로이드 플랫폼은 PendingIntent 를 사용할 때 발생하는 오류(예를 들어 적절한 Activity 를 찾을 수 없거나 권한이 없어서 발생하는 오류) 를 모두 조용하게(강제 종료 팝업 없이) 처리해 줍니다.
역시 안드로이드는 멋진 녀석입니다.
[출처] 안드로이드 Service 에서 Activity 를 실행하는 방법|작성자 휴우
반응형
'android' 카테고리의 다른 글
intent 브라우져 사이트 띄우기 새창 띄우기 (0) | 2016.12.29 |
---|---|
안드로이드 WebView 화면 전환 시 Reload 되는 현상 방지 가로모드 (0) | 2016.06.01 |
Android 내장 메모리의 사진 정보 가져오기 (0) | 2016.05.16 |
안드로이드(Android) 사진의 EXIF 정보 가져오기 (0) | 2016.05.16 |
문자 애플리케이션 안드로이드 킷캣(4.4) 대응하기 (0) | 2016.02.01 |