출처: http://i5on9i.blogspot.kr/2013/10/messenger.htmlhttp://rosaria1113.tistory.com/242

Messenger 개념

Handler 가 하나의 process 내에서 여러 Thread 들이 통신할 때 사용할 수 있다. 그런데 이 Handler 를 process 들끼리의 통신에 사용할 수는 없다. 이 때 사용하는 것이 Messenger 라는 녀석이다. 이 녀석을 이용해서 Handler 를 감싸면, 중간에 Marshaling 같은 부분들을 (Parcel 같은.) Messenger 가 해주고, 프로그래머 입장에서는 process 사이에서도 Handler 와 같은 interface 를 사용할 수 있게 돼서 프로그래밍이 좀 더 편하게 된다.

그러니까 정리해서 얘기하면 process 사이에서 Handler 를 사용할 수 있게 해 주는 녀석 정도로 이해하면 될 듯 하다.

아래 좀 다른 형태의 설명이 있으니 참고하세요.

Messenger 는 특정 Handler 를 감싸는 클래스입니다. 가장 큰 특징은 바로, 이 Messenger 가 Parcelable 인터페이스를 구현하고 있다는 점 입니다. Handler 자체는 다른 프로세스로 넘겨 줄 수 없지만, 이를 Messenger 로 감싸면, 해당 Handler 로 원격에서 메세지를 전할 수 있는 Messenger 인스턴스를 생성할 수 있고, 이 Messenger 인스턴스는 한 프로세스에서 다른 프로세스로 이동 할 수 있습니다. 그래서, 복잡한 AIDL 을 정의하지 않고도 간편하게 Message 에 기반한 IPC 작업을 수행할 수 있습니다. 예제 코드를 살펴 보도록 하지요.

출처 : 안드로이드 Handler 이야기 - Messenger Service - |작성자 휴우, 2011/08/18



Messenger 예제

간단한 예제는 아래에서 찾을 수 있습니다.
[Android/안드로이드] Messenger 를 이용한 IPC.( Service binding ), 2012/05/21


이 Messenger 를 이용해서 Service 와 Activity 가 통신하는 방법은 AIDL 에서 설명한 내용과 같다. 다시 정리하면 아래와 같다.

  • Service -> Activity
    Service 와 bind 되고 나서 Service 에 Activity 에서 생성된 Messenger() 를 Service에 넘겨줘서 Service 가 이것을 이용해서 Activity 에 원하는 Message 를 전달할 수 있게 해준다.
  • Activity -> Service
    Service 는 onBind() 에서 Message class type 을 넘겨준다(return). 이 Message 가 Bind 될 때 onServiceConnected 로 넘어오는 Binder 이다. 이녀석을 이용해서 Activity 는 Service 에 원하는 Message 를 전달하게 된다.
  • 코드
Messenger 는 특정 Handler 인스턴스의 리퍼런스를 갖고 있으며, 이를 이용하여 해당 Handler 로 메세지를 보낼 수 있습니다. 이를 이용하여, 프로세스간 메세지 기반 커뮤니케이션을 수행할 때 활용될 수 있습니다. 

[출처] 안드로이드 Handler 이야기 - Messenger Service - |작성자 휴우


A 프로세스 -> B 프로세스로 Handler 인스턴스를 그냥 넘길수는 없다. Parcelable 인터페이스를 구현하고 있는 Messenger로 감싸면 전송이 가능하다.  (Parcelable 인터페이스 : 객체 직렬화를 위한 인터페이스, 생성된 객체를 파일이 저장하거나 스트림을 통해 보내는 것이 가능


아래 예제는 2개의 Android Application을 생성하고 TestMessager2 어플에서 TestMessenger1에 있는 mHander로 메시지를 보내는 방법이다.

mHandler(service)에서 Client(호출하는곳)으로 응답 메시지를 보내야 하는 경우 

  1. Client 에 핸들러를 생성, Messenger로 만든다.

  2. replyTo 필드를 사용해서 1번으로 만들어진 인스턴스를 추가

  3. Server는 수신된 Message의 replyTo 필드에서 Client Messener를 꺼내서 응답 메시지를 전달


TestMessenger1

MessengerService.java

public class MessengerService extends Service {

private static final String TAG = "MessengerService";

private Handler mHandler = new Handler() {

public void handleMessage(Message msg) {

switch (msg.what) {

case 1:

Log.d(TAG, "mHandler.case 1");

break;

}

}

};

public void onCreate() {

super.onCreate();

Log.d(TAG, "MessengerService onCreate()");

}


@Override

public IBinder onBind(Intent arg0) {

Log.d(TAG, "MessengerService onBind()");

return new Messenger(mHandler).getBinder();

}

}


AndroidManifest.xml

<service 

android:name="com.example.testmessenger1.MessengerService"

android:process=":remote">

<intent-filter >

<action android:name="com.example.testmessenger1.MessengerService"/>

</intent-filter>

</service>

AndroidManifest.xml을 위와 같이 수정해주지 않으면 TestMessenger2에서 

"java.lang.SecurityException: Not allowed to bind to service Intent { cmp=com.example.testmessenger1/.MessengerService }" 이 뜬다.


TestMessenger2

MainActivity.java

public class MainActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button btn = (Button)findViewById(R.id.btn);

btn.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

ComponentName cn = new ComponentName("com.example.testmessenger1",

    "com.example.testmessenger1.MessengerService");

Intent intent = new Intent();

intent.setComponent(cn);

            

ServiceConnection conn = new ServiceConnection() {

@Override

public void onServiceDisconnected(ComponentName name) {}

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

Messenger messenger = new Messenger(service);

Message msg = Message.obtain(null, 1);

try {

messenger.send(msg);

} catch (RemoteException e) {

}

}

};

bindService(intent, conn, Context.BIND_AUTO_CREATE);

}

});

}

}

Context.BIND_AUTO_CREATE flag를 사용해서인지 TestMessenger1이 실행되지 않아도 호출 된다.




주의: AIDL 은 오직 Client 의 요청의 결과가 즉시 return 되어야 하기 때문에, Service 단에서 동시에 여러 스레드를 통해 개별 요청을 처리해야 하는 경우에만 필요합니다. 만일 그렇지 않은 경우에는 Messenger 클래스를 활용하세요.

[출처] 안드로이드 Handler 이야기 - Messenger Service - |작성자 휴우


Messenger 이외의 방법

Service 와 Activity 간 통신을 하는 좋은 방법
Android Example: Communication between Activity and Service using Messaging - Philipp's Tech Blog


Broadcast 를 이용해 Service 에서 Activity 로 data 전달
Android Coding: Pass data from Service to Activity


AIDL
위의 방법 외에 Service 에서 Activity 로 원하는 정보를 전달하는 방법은 aidl 을 사용할 수 있다. aidl 사용법 을 참고하자.



Messenger 로 Object 전달

Message.obj

[see also. 1] 에 보면, seperate processes 에서는 Messenger 의 obj 전달이 불가능하다고 하는 듯 하다. 하지만 하나의 process 에 Service 와 Acitivity 가 같이 있는 경우라면 Message.obj 에 원하는 object 를 넣고 전달하는 것이 가능하다.

Bundle

[See Also. 1] 에 따르면, seperate process 라면 Message.obj 를 사용해서 원하는 object 를 넘기는 것은 불가능하고, 대신에 Bundle 을 사용해야 한다고 한다.

Handler 에서 Bundle 은 마치 hashMap 처럼 사용할 수 있다. key 와 value 를 이용해서 값을 유연하게 넣을 수 있다. 그리고 이 녀석을 Message 에 실어서 Handler 로 보내면 된다.

Bundle 관련 예제 : http://mobileorchard.com/android-app-developmentthreading-part-1-handlers/


'Programing > Android' 카테고리의 다른 글

개발 협업을 위한 안드로이드 디자인 가이드 #01  (0) 2016.05.08
Resolution and DP (Density Independent Pixels)  (0) 2016.05.07
BroadcastReceiver  (0) 2016.05.06
ANR (Application Not Responding)  (0) 2016.05.06
Process and Thread  (0) 2016.05.04

출처: http://cafe.daum.net/_c21_/bbs_search_read?grpid=1MWA2&fldid=aAfL&datanum=102&docid=1MWA2aAfL10220110915161750

1. Broadcast Receiver에 대해서

 

Android의 4가지 Component 중 가장 쉬운 것중 하나가 바로

Broadcast Receiver이다.

Broadcast Receiver는 각각의 Component들 간에 메시지를

전달할 수 있는 방법을 제공한다.

 

 

우선 여러 패키지에서는 귀를 기울일만한 방송에 대해 Receiver라는 녀석을 등록해 둔다.

이 Receiver는 자신이 관심을 가지는 방송을 듣게되면 동작하게 되는 것이다.

 

그림으로 보자면

아래에 1번과 같이 특정 패키지 Component에서 sendBroadcast(메시지)를 통해서

전역 방송을 날린다. 이때 방송은 "Test1"이라고 가정하자.

그후 이미 등록된 Receiver 중 3번에 해당하는 receiver가 그 메시지에 관심을 가지고 있으므로 동작하게 될 것이다.

 

 

여기서 방송이라는 표현을 썼는데 이 것이 바로 Intent이다.

앞서 Activity를 설명하면서 Intent에 대해서 충분히 설명을 하였다.

Andriod를 하면서 Intent는 아주 중요하다. (조금이라도 이해가 안되었다면 앞의 강좌를 다시보고 또 보자.)

앞으로 계속 배우게 될 BroadcastReceiver, Service에서 계속 활용된다.

Activity에서 설명한 Intent에 대해 이해하면 모두 같은 방식으로 사용되니 한번만 잘 이해하면 된다.

(주변에 Intent에 대해 정확히 이해하는 사람이 많이 없어서 계속 말하는 것이다. ^^;)

 

어쨌든 참 글로 쓰면 왜 쉬운 내용도 어렵게 전달이 될까? 짜증이 난다. ^^;

카페에 누군가 동영상 강좌로 해 주면 안되겠냐는 글을 보았다.

어쩔때는 정말 그러고 싶다. ㅋㅋㅋ

 

자 계속해서 Receiver에는  두가지 종류가 있다.

바로 정적 Receiver와 동적 Receiver이다.

별로 어렵지 않으니 계속 강좌를 지켜보길 바란다.

 

 

1.1 정적 Receiver

 

정적 Receiver란 말 그대로 Receiver를 고정해서 등록해 놓고 원하는 방송에

반응하는 Receiver를 말한다.

 

역시 개발자는 소스를 보고 이해하는 것이 빠를 것이다.

아래의 패키지로 이해해 보자.

두 가지 패키지를 만들 것이다.

 

우선 첫번째 패키지는 방송을 하는 패키지이다.

 

아래에 1번과 같이 Intent를 하나 생성하고

Intent에 방송할 내용을 담는다.

방송할 내용 역시 Action이 사용된다. Activity와 똑같다. ㅎㅎㅎ

setData()를 통해 Intent Filter에 매칭될 Scheme을 하나 넣어 보았다.  왜?  그냥.... -  _-;

(BroadcastReceiver는 모든 패키지가 받을 수 있다. 그러므로 꼭 원하는 패키지 Receiver에게 전달하게 하려면

 적절한 Intent Filter 를 사용하는 것이 좋다.)

1번에서 방송하게 될 Action을 기억해 두자. "android.intent.action.SUPERSK"

 

 

자 이제 두번째 패키지 이다.

 

당연히 이 패키지는 방송을 듣고 반응하는 리시버 이다.

 

아래는 AndroidManifest.xml에 리시버를 하나 등록하고 있다.

아래는 AndroidManifest.xml에 리시버를 하나 등록하고 있다.

아래는 AndroidManifest.xml에 리시버를 하나 등록하고 있다.

3번 반복한 것은 ^^ 오타가 아니다.

왜냐하면 AndriodManifest.xml에 고정해서 리시버를 등록하기 때문에 정적 Receiver라고 말하는 것이다.

이해가 되는가 안되면 다음에 나올 동적 Receiver까지 보면 이해가 될 것이다.

일단 ^^/ 계속...

위의 Intent Filter는 이전 Activity에서 Intent를 이해했다면 쉽게 볼 수 있을 것이다.

2번을 보면 Action으로 첫번째 패키지가 보내는 방송에 귀를 기울이는 Action 일 것이다.

 

계속해서 그 방송을 듣게 되면 처리되는 코드는 아래와 같다.

해당 방송이 오면 Toast를 하나 띄울 것이다.

위의 onReceive( Context context, Intent intent) 의 인자인

intent로 방송을 보낼때 전달되었던 Intent가 그대로 전달받게 된다.

바로 이 Intent로 원하는 데이터를 Intent Extra에 담아서 보내면 된다.

 

자 이제 실행해 보자.

첫번째 패키지에 버튼을 누르면

2번과 같이 두번째 패키지가 방송을 듣고 Toast를 하나 보여 준다.

쉽지 않은가?

 

테스트 패키지 소스는 아래에 첨부한다.

 

첨부파일 Broadcast1.zip

첨부파일 Receiver1.zip

 

 

1.2 동적 Receiver

 

동적 Receiver는 AndroidManifest.xml에 Receiver를 등록하지 않는다.

위에서 만든 패키지 중 방송을 보내는 패키지를 그대로 활용하고,

아래의 패키지를 추가로 만들어 보자.

위에서 왜 Activity를 하나 만들었을까?

궁금증을 가지고 계속 아래를 보자.

 

Activity의 내용이다.

위에서 Activity의 생명 주기중 onCreate에서 Receiver를 등록하고 있다.

차근차근 보자.

1번에서 IntentFilter를 하나 생성하여 방송에 매칭될 Action과 Data의 Scheme을 등록하였다.

2번에서 BroadcastReceiver 객체를 하나 생성한다. 객체 생성과 동시에 onReceiver() 함수를 구현해 주면 된다.

            차후 방송이 오게 되면 바로 onReceiver()함수가 호출될 것이다.

3번에서 해당 BroadcastReceiver 객체를 등록한다. registerReceiver()함수는 바로 receiver 객체를 등록하게 해 준다.

           당연히 registerReceiver() 인자로 등록될 리시버 객체와 intentFilter를 넣어주면 된다.

 

자 특정 Component에서 BroadcastReceiver 객체를 동적으로 생성하여 리시버를 등록 하였다.

이 것이 바로 동적 Receiver라고 하는 이유다.

 

자 잘 동작되는지 보자.

1번과 같이 지금 만든 동적 리시버 패키지를 꼭 실행해 둬야 한다. (이유가 있다.)

2번과 같이 꼭 Home Key를 눌러서 다시 홈으로 가자.

3번에서 방송을 날릴 패키지를 실행하자.

4번의 버튼을 눌러보면 동적 Receiver가 동작하고 Toast를 하나 띄울 것이다.

 

자 다시 하나면 더 테스트를 해 보자.

아래와 같은 시나리오로 해야한다.

1번에서 동적 리시버 패키지를 실행하자

2번과 같이 Back Key를 눌러 해당 패키지를 종료해 보자.

3번과 같이 홈에서 방송을 보낼 패키지를 실행하고

4번과 같이 방송 버튼을 눌러 Broadcast를 날려 보자.

!!! Toast가 안뜨지 않는가?

이것이 바로 동적 리시버의 특징이다.

동적 리시버는 자신을 등록한 Component의 생명 주기가 끝나면 사라진다.

위에서는 Activity에서 리시버를 등록 했으므로 Activity를 Back Key를 이용해서 종료하게 되면

리시버도 사라지는 것이다.

 

Activity의 생명 주기는 아래와 같다.

즉 onDestroy()까지 타면 Receiver는 없어 진다.

꼭 이해하도록 하자.

 

동적 리시버에서 꼭 하나더 알아야 할 것이 있다.

리시버를 등록할때 registerReceiver()라는 함수를 썼다.

그렇다면 분면 unregisterReceiver()라는 함수도 있지 않겠는가 그렇다.

registerReceiver()로 등록된 Receiver는 unregisterReceiver() 함수를 이용해서 등록해제 할 수 있다.

즉 리시버를 더 이상 사용할 필요가 없을 때는 unregisterReceiver() 함수로 해제하면 된다.

또한 한번 등록된 리시버를 중복해서 다시 등록하는 일이 없도록 하자.

만일 두번 등록이 되면 중복된 리시버 모두 동작하기 때문이다.

뿐만아니라 unregisterReceiver()를 통해 해제치 않으면

메모리에 계속 잡고 있는 메모리 누수가 발생한다.

꼭 해제해 주도록하자.

 

적절한 위치에 등록과 해제 소스를 넣어 두자.

예를 들어 onCreate()에 등록하고 onDestroy()에 해제 한다던가...

 

테스트 패키지 소스는 아래에 첨부한다.

 

첨부파일 Receiver2.zip

 

 

1.3 동적 Receiver와 정적 Receiver는 어떤 차이가 있을까?

 

android를 처음 접할때 이 점이 참 궁금했다.

그 이유는 참 간단했다. 하지만 글로 설명해야 하는데 잠시 두렵기도 하다...  =_  =;;

일단 정적 리시버는 한번 등록하고 계속 유지할 수 있다는 장점이 있지만

해제가 어렵다는 단점이 있다.

다른 단점을 이해하기 위해서는 동적 리시버의 장점을 설명해야 한다.

 

동적 리시버는 등록한 component 생명 주기가 끝나면

더이상 동작하지 않는다는 단점이 있다고 했다. 물론 계속 유지 하고 싶다면 정적 리시버를 쓰면 된다.

동적 리시버는 내가 생각하는 아주 편리한 장점 두가지가 있다.

 

첫번째는 바로 시스템에 큰 부하를 주지 않는 점이다.

 

매분마다 발송되는 시스템 Action "android.intent.action.TIME_TICK" 이 있다

이 것은 AlarmManagerService란 서비스가 매 분마다 발송한다.

만일 정적 리시버로 해당 Action을 등록했다면 매분마다 계속 이벤트를 받게 되고,

매 분마다 해당 이벤트에 대한 처리를 할 것이다.

이는 시스템 측면에서 전체적이 성능을 저하시킬 것이다.

하지만 동적리시버는 한 Component의 생명주기 내에서만 해당

이벤트를 받으므로 큰 부하를 주지 않는 것이다.

 

참고로  "android.intent.action.TIME_TICK" 이벤트는 정적 리시버로는 받을 수 없다.

위에서 말한 이유 때문이다. 즉 Android 시스템에서 시스템에 부하를 주는

경우를 당연히 두지 않는다.

이렇게 동적 리시버에서만 Action을 받을 수 있도록 하는 Intent Flag는

"FLAG_RECEIVER_REGISTERED_ONLY" 이다.

( FLAG_RECEIVER_REGISTERED_ONLY는

   "17. Broadcast Receiver에 대해서 - IntentFlag 정리"

  강좌에서 설명하니 넘어가도록 하자.)

 

두번째는 다른 Component 내에 소스가 존재한다는 것이다.

리시버를 등록할때를 생각해 보자.

 

뭐 아래와 같다고 하자. 

 

    public class testActivity extends Activity

    {

        private   int        mCount = 0;

 

        public void onCreate(Bundle b)

        ....

 

        mReceiver = new BroadcastReceiver()

        {

              pubilc void onReceiver(Context context, Intent intent)

              {

            

              }

         };

         registerReceiver( mReceiver , intentFilter);

 

        .....

    }

위에서 Activity 내에 Receiver가 달려 있다.

여기서 mCount 라는 멤버 변수로 접근해 보자.

 

 

    public class testActivity extends Activity

    {

        private   int        mCount = 0;

 

        public void onCreate(Bundle b)

        ....

 

        mReceiver = new BroadcastReceiver()

        {

              pubilc void onReceiver(Context context, Intent intent)

              {

                    mCount  = 20;

              }

         };

         registerReceiver( mReceiver , intentFilter);

 

        .....

    }

위와 같이 쉽게 mCount로 접근할 수 있다.

너무 당연한 얘기 아닌가?

즉 Activity 내에 모든 멤버 객체에 접근하고

그 객체를 참조할 수 있는 것이다.

 

정적 Receiver를 생각해 보자.

정적 Receiver는 특정 Component에 포함되어 있지 않으므로

Receiver에서 사용하고 싶은 특정 Component의 멤버변수를 전혀 접근 할 수 없다.

(물론 Application 객체를 이용하여 접근할 수는 있다. -   _-; 혹시 궁금한 사람들은

 옆에 링크로 이동하여 Activity의 name 속성 부분을 보라.  여기로 링크  )

 

이해 되었는가?

이해 되지 않는 부분은 늘 질문하라...ㅎㅎㅎ

 

1.4 Receiver 꼭 알아야 할 것

 

Activity와 마찬가지로 Receiver 또한 ActivityManagerService가 처리한다.

아래의 그림을 보자.

1번과 같이 sendBroadcast() 호출하면

2번과 같이 ActivityManagerService가 활성화 되어야 할 등록된 Receiver를 호출해 준다.

만일 여러개의 리시버가 등록되어 있다면 순서대로 활성화 시킨다.

3,4,5번을 보면 등록된 리시버가 3개이다.

만일 Receiver1이 처리되는 시간이 3초라면 다음 처리될 Receiver2는 3초후에 활성화 될 것이다.

또한 Receiver2이 6초라는 시간이 소모되면 Receiver3번은 총 9초후에 활성화 될 것이다.

 

여기서 말하고자 하는 것은 리시버에 대한 처리는 최대한 빨리 끝내는 것이 바람직하다.

^^ 만일 처리시간이 오래 걸리게 되면 Activity와 마찬가지로 ANR이 발생된다.

 

아래의 테스트 코드로 이해해 보자.

리시버를 하나 등록했다.

해당 리시버는 12초의 지연을 주 예이다.

어떻게 될까?

 

아래와 같이 실행해 보자.

눈에 에러는 보이지 않으나 에러가 발생될 것이다.

 

아래를 보고 이해해 보자.

로그를 보자.

5번을 보면 리시버가 timeout이 발생되고

6번에서 ANR을 발생시켜 해당 리시버를 죽이기 위해 리시버가 포함된 Process를 죽여 버린다.

 

즉 시스템에서도 리시버에 처리 지연을 두고 보지 않는 다는 말이고

ActivityManagerService는 다른 작업을 하기 위해 절대 기다려 주지 않는 다는 것이다.

 

아래에 Framework 소스를 잠시 보자.

리시버의 Timeout은 정확히 10초임을 알 수 있다.

그렇다고 10초 내에 끝내라는 것이라고 이해하면 안된다.

최대한 간단한 작업만을 하라는 것이다.

 

리시버는 Activirty 처럼 UI가 존재하지 않는다.

그러므로 Background에서 돌아가는 component이다.

다음에 배우겠지만 service component 역시 background에서 돌아간다.

하지만 둘을 같이 보면 안된다.

Receiver는 제약 사항이 많기 때문이다. 지금 그 제약 사항을 설명하는 것은 어렵다.

Service를 배우지 않았기 때문이다. 차후에 다시 설명 하겠다.

(참고로 몰라도 되지만 ... Receiver에서는 bindService가 허용되지 않는다.)

 

어쨌든 기억하라. ~!!!!!

 

리시버는 간단한 작업만 하도록 하자.

예를 들어 다른 component를 활성화 한다던지,

(startActivity or startService 등)

DB에 값을 변경한다던지... 꼭 구구절절히 말하기는 힘들지만 어쨌든 onReceiver 함수 내에서만 처리될 수 있는

간단한 작업만을 하자.

 

간혹 리시버에 네트워크 작업등을 처리하려는 사람을 보았다.

그러지 말자. ^^;;;

'Programing > Android' 카테고리의 다른 글

Resolution and DP (Density Independent Pixels)  (0) 2016.05.07
Messenger  (0) 2016.05.06
ANR (Application Not Responding)  (0) 2016.05.06
Process and Thread  (0) 2016.05.04
Processes and Application Life Cycle  (0) 2016.05.04

출처: http://sunphiz.me/wp/archives/447

ANR(Application Not Responding)

ANR이란 무엇인가?

Application Not Responding의 약자이다. 단어 그대로, 어플리케이션이 응답하지 않는 경우 안드로이드 시스템에서 보여주는 에러이다. 메인 스레드(일명, UI 스레드)가 일정시간 동안 잡혀 있으면 발생한다.

언제 ANR이 발생하는가?

안드로이드 운영체제는 홈페이지 정리가 매우 잘 되어 있다. ANR의 경우는 여기에 정리되어 있는데,

  • 터치를 통한 사용자 입력이 5초 내에 처리되지 않았을 때
  • 브로드캐스트가 10초 내에 처리되지 않았을 때

가 나와있다. 하지만 여기에 나오지 않은 경우가 하나 더 있다.

  • 서비스가 20초 내로 처리되지 않을 때

안드로이드는 UI가 없는 컴포넌트인 브로드캐스트 리시버와 서비스도 메인 스레드에서 돌아가기 때문에 당연히 서비스에서도 시간 소모성 작업을 하는 경우 ANR이 발생할 수 있다.

왜 ANR이 발생하는가?

위의 ANR이 발생하는 조건을 보면 알겠지만, 앱이 무한 루프에 빠지거나 OOM(Out Of Memory)가 나야 ANR이 발생하는 것이 아니다. 시간이 좀 걸리는 처리를 해도 ANR이 충분히 발생할 수 있다. 그래서, 나처럼 자바프로그래밍에서 안드로이드로 온 사람에게는 한참동안 이해가 안될 수도 있다.

ANR이 필요한가?

그럼 이해가 안되는 것이 굳이 있어야하는 이유가 무엇인가? 내 생각에 이 부분을 이해하기 위해서는 사용자가 어떤 경우 문제가 있다고 느끼는가에 대한 것을 먼저 생각해야한다. 지금 대부분의 어플리케이션에서 사용하고 있는 프로그레스바를 생각하면 쉽다. 사용자에게 ‘움직이고 있다.’라는 사인을 주면 사용자는 기다린다. 하지만, 일명 먹통이 되면 사용자는 문제가 있다고 판단하여 전원을 끈다던가 다른 자신이 할 수 있는 해결책을 시도하는 것이다. 모바일 OS는 화면이 작아, 대부분(테블릿은 화면이 크고, 안드로이드에서 지원하는 프레그먼트는 한 화면에 여러개의 액티비티를 동작하게 할 수도 있다.)은 전체 화면을 차지한다. 이 때 화면이 정지한다면 정상적으로 동작하고 있음에도 문제가 있다고 느낄 수 있는 것이다.

그럼 어떻게 ANR을 관리해야 할까?

안드로이드 개발자 사이트를 참조해야할 질문이다. 여기에 잘 정리되어 있다. 간단히 요약하면,

  • 시간이 오래걸리는 작업은 스레드를 통해 처리하도록 권장한다.
  • 사용자에게는 프로그레스바 등을 이용해 진행 과정을 안내해 기다리도록 한다.
  • 이를 위해, 안드로이드에서 상속받아 사용할 수 있는 다양한 방법을 제공하고 있다.
  • Main thread에서 실행되는 임의의 method는 최소한의 일을 해야 합니다.
    특히 onCreate(), onResume() 과 같은 핵심 생명주기 method에서 가능한 적은 일 수행하는 것이 좋습니다.
    네트워크나 데이터베이스 operation 같은 잠재적으로 길게 실행될 가능성이 있는 작업들이나, 비트맵 크기를 조정하는 것 같은 계산상 값비싼 연산 쓰레드를 새로 생성해서 처리하는 것이 좋습니다. 물론 쓰레드에서 처리가 완료되었을 때 결과값을 받도록 handler를 설치 및 전달하는 것도 중요하지요.

    BR이 오랜 작업을 수행해야 하는 경우라면, service를 만드는 것이 좋습니다.
    BR이 activity를 띄워야 한다면, notification manager를 사용해서 사용자에게 보여주는 것으로 변경해야 합니다.

dumpstate에서 ANR의 원인을 분석하고 싶다면?

SYSTEM LOG에서 ActivityManager가 ANR 메시지를 표시하지 않는지 살펴보자. “ANR in”으로 검색하면 된다.

문제가 발생한 곳의 위치가 표시되고, 이유와 함께 CPU 사용량도 표시된다.

SYSTEM LOG에서 “SIG: 9″를 검색해 보자.  이는 시스템에서 ANR을 발생시키는 프로세스를 종료하기 위해 SIGNAL_KILL을 호출되면서 남기는 로그일 수 있다. 그 부분의 전후 로그를 살펴보자. ANR이라고 판단한 이유가 로그로 남아있을 수 있다.

EVENT LOG에서 “am_anr” 태그로 검색해 보자. anr이 발생한 어플리케이션 정보를 얻을 수 있다.

문제가 발생한 패키지 명과 PID, 이유가 표시된다.

DUMP OF SERVICE… 의 VM TRACES AT LAST ANR 부분에도 살펴보자. 문제가 없는 경우라면 다음 처럼 아무 내용이 없다.

하지만, ANR이 발생한 경우에는 어플리케이션의 콜스택이 남아있다. 글의 초반에 설명했듯이 ANR의 원인이 메인 스레드(일명 UI 스레드)이 오랫동안 바쁜 경우이므로 “main” 스레드 정보를 먼저 확인해야 한다. 특히, TIMED_WAIT나 MONITOR 에 있는지 확인하자.

그 밖에

ANR이 Activity Manager나 Window Manager 같은 곳에서 발생한 경우는 “어플리케이션이 응답이 없습니다. 기다리시겠습니까?”와 같은 메시지가 표시되지 않을 수 있다.

참조


'Programing > Android' 카테고리의 다른 글

Messenger  (0) 2016.05.06
BroadcastReceiver  (0) 2016.05.06
Process and Thread  (0) 2016.05.04
Processes and Application Life Cycle  (0) 2016.05.04
Google I/O - Memory Management For Android  (0) 2016.05.03

+ Recent posts