자마린(Xamarin)

(자마린학원/추천자마린교육)Xamarin.Android Services, 자마린.안드로이드 바운드 서비스 실습_Xamarin강…

FSP 0 46 2018.12.12 16:18

(자마린학원/추천자마린교육)Xamarin.Android Services, 자마린.안드로이드 바운드 서비스 실습_Xamarin강좌


작성자 : 탑크리에듀교육센터, 이종철(mylife68@nate.com)


첨부 파일 참조하세요~

 

n  안드로이드 Application을 구성하는 4가지 컴포넌트 중에 하나인 ServiceActivity처럼 사용자와 상호작용 하는 컴포넌트가 아니고, 사용자 인터페이스(UI)도 제공하지 않는 백그라운드에서 주로 시간이 오래 걸리는 작업들을 처리하는 앱 컴포넌트이다.

 

n  시간이 걸리는 계산작업, 파일 다운로드, 네트워크를 통한 데이터 전송 및 수신, 음악 재생, 주기적으로 특정 웹사이트에서 데이터를 읽어 온다든지, 파일 I/O, 컨텐트 프로바이더와의 상호작용 등을 백그라운드에서 처리할 수 있다.  Activity 화면에서 동작뿐만 아니라 Activity가 종료되어 있는 상태에서도 동작하기 위해서 만들어진 컴포넌트 이다.


 

n  모바일 앱은 데스크톱 앱과 다르다. 데스크탑은 화면 공간, 메모리, 저장 공간 및 연결된 전원 공급 장치와 같은 많은 자원을 가지고 있지만 모바일 장치는 좀 다르다. 이러한 제약으로 인해 모바일 앱이 다르게 작동하는데, 휴대 기기의 작은 화면은 일반적으로 한 번에 하나의 앱 (, Activity) 만 표시된다.

n  기타 Activity는 백그라운드로 이동하고 작업을 수행 할 수 없는 일시 중지 상태로 푸시된다. 그러나 Android 애플리케이션이 백그라운드에 있다고 해서 앱이 계속 작동하는 것이 불가능 하지는 않다.

 

n  안드로이드에서 백그라운드 작업의 기본 메커니즘은 Android Service로 사용자 인터페이스 없이 일부 작업을 수행하도록 설계된 구성 요소이다. 안드로이드 어플리케이션 간의 프로세스 간 통신 (IPC)에도 사용될 수 있다. 예를 들어 하나의 Android 앱이 다른 앱의 뮤직 플레이어 서비스를 사용하거나 앱이 데이터 ( : 사람의 연락처 정보)를 서비스를 통해 다른 앱에 노출 할 수도 있다.

 n  서비스와 백그라운드 작업의 처리능력은 유연한 사용자 인터페이스를 제공하는 데 중요하다. 모든 Android 애플리케이션에는 Activity가 실행되는 기본 스레드 (UI 스레드라고도 함)가 있는데 기기가 응답 하도록 하려면 Android는 초당 60 프레임의 속도로 사용자 인터페이스를 업데이트 할 수 있어야 한다. Android 앱이 주 스레드에서 많은 작업을 수행하면 Android가 프레임을 삭제하고 이로 인해 UI가 깜박이는 것처럼 보인다 (때로는 janky라고도 함). , UI 스레드에서 수행 된 작업은 두 프레임 사이의 시간 간격 ( 16 밀리 초 (60 프레임마다 1 )) 동안 완료되어야 한다.

 

n  이런 문제점 때문에 개발자는 Activity의 스레드를 사용하여 UI를 차단하는 작업을 수행함으로 인해해 문제가 발생할 수도 있는데 Android가 여러 Activity 인스턴스를 종료시키고 다시 만들 가능성이 매우 높다. 그러나 Android는 스레드를 자동으로 삭제하지 않으므로 메모리 누수가 발생할 수도 있는데 가장 대표적인 예는 휴대폰을 회전하는 경우로 AndroidActivity의 인스턴스를 종료시킨 다음 새 인스턴스를 다시 만들려고 하는데 이 경우 AndroidActivity의 인스턴스를 Destroy 시킨 다음 새로운 인스턴스를 만들려고 한다.

 

n  Activity의 첫 번째 인스턴스에 의해 생성 된 스레드는 계속 실행되고 스레드가 Activity의 첫 번째 인스턴스에 대한 참조를 가지고 있다면 Android가 객체를 가비지 컬렉션하지 못하게 된다. 그러나 Activity의 두 번째 인스턴스는 여전히 생성되며 (이는 다시 새 스레드를 생성 할 수 있음) 빠른 속도로 연속해서 여러 번 기기를 돌리면 모든 RAM이 소모되어 안드로이드가 메모리를 회수하기 위해 전체 애플리케이션을 강제 종료 할 수 있다는 것이다.

 

n  처리해야 할 작업이 Activity보다 오래 지속되어야 한다면 서비스가 만들어 져야 한다. 만약 작업이 Activity 컨텍스트 내에서만 적용 가능하면 스레드를 만드는 것이 더 적절할 수 있다. 예를 들어 사진 갤러리 앱에 방금 추가 된 사진의 미리보기를 만들려면 서비스로 구현을 하는 것이 좋고, 액티비티가 Foreground에 있을 때만 음악을 재생하려면 스레드가 적합하다.

 

n  백그라운드 작업은 크게 두 가지로 분류 할 수 있다.

ü  장기 실행 태스크(Long Running Task) - 명시적으로 중지 될 때까지 진행중인 작업으로 음악을 스트리밍하거나 센서에서 수집 한 데이터를 모니터링 해야 하는 앱등으로 응용 프로그램에 표시되는 사용자 인터페이스가 없는 경우에도 작업이 실행 되어야 하는 경우이다.

 

ü  주기적 작업(Periodic Tasks) - 주기적 작업이란 비교적 짧은 기간 (수 초)으로 일정에 따라 실행된다 (, 하루에 한 번 또는 일주일에 한 번 또는 다음 60 초 내에 한 번만 실행 됨). 예를 들면 인터넷에서 파일을 다운로드하거나 이미지의 축소판을 생성하는 것등이 있다.

 

n  안드로이드 서비스는 실행되는 방식에 따라 4가지 유형으로 구분할 수 있다.

 

1.     바운드 서비스(Bound Service) - 바인딩 된 구성 요소(Activity)와 서비스가 서로 상호 작용할 수 있도록 인터페이스를 제공하며 서비스에 바인딩 된 클라이언트가 더 이상 없으면 Android가 서비스를 종료한다.

 

2.     인텐트 서비스(IntentService) - 서비스 생성 및 사용을 단순화 하는 Service 클래스의 특수 하위 클래스로 개별적인 호출을 처리하기 위한 것으로, 여러 개의 호출을 동시에 처리 할 수 있는 서비스와 달리 IntentService는 작업 대기열 프로세서와 비슷하다. 작업은 대기열에 있고 IntentService는 스레드에서 한 번에 하나씩 작업을 처리한다.

 

3.     Started Service(시작 서비스) – Activity와 같은 다른 안드로이드 서비스에 의해 시작된 서비스로 서비스가 중지 되도록 명시 적으로 알릴 때까지 백그라운드에서 계속 실행된다. 바운드 서비스와 달리 시작된 서비스에는 직접 바인딩 된 클라이언트가 없고 이러한 이유로 시작된 서비스가 필요에 따라 정상적으로 다시 시작되도록 설계하는 것이 중요하다.

 

4.     Hybrid Service(하이브리드 서비스) - 하이브리드 서비스는 Started ServiceBound Service의 특성을 가진 서비스입니다. 구성 요소가 구성 요소에 바인딩되거나 특정 이벤트에 의해 시작될 때 시작될 수 있고 클라이언트 구성 요소는 하이브리드 서비스에 바인딩되거나 바인딩되지 않을 수 있다. 하이브리드 서비스는 명시 적으로 중지 할 때까지 또는 더 이상 클라이언트가 바인딩되지 않을 때까지 계속 실행된다.

 

n  사용할 서비스 유형은 응용 프로그램의 요구 사항에 따라 다른데 일반적으로 Android 애플리케이션이 수행해야 하는 대부분의 작업에는 IntentService 또는 Bound Service로 충분하기 때문에 두 가지 유형의 서비스 중 하나를 선호한다. IntentService는 파일 다운로드와 같은 "원 샷" 작업에 적절하지만 액티비티와의 상호 작용이 빈번 할 때 바운드 서비스가 적합하다.

 

2.2.8 안드로이드 8.0이상에서의 Service 실행 제한

 

n  안드로이드 8.0 (API 레벨 26)부터 애플리케이션은 더 이상 백그라운드에서 자유롭게 실행할 수 없다. Foreground에서 앱은 제한없이 서비스를 시작하고 실행할 수 있지만 Background로 이동하면 Android 시스템은 서비스를 시작하고 사용할 수 있는 일정 시간을 앱에 부여하고 그 시간이 지나면 앱은 더 이상 서비스를 시작할 수 없고 시작된 모든 서비스는 종료된다.

n  이 시점에 앱이 어떤 작업을 수행 할 수는 없다. Android는 다음 조건 중 하나가 충족되면 응용 프로그램을 Foreground로 간주한다.

ü  가시적 인 활동 (시작됨 또는 일시 중지됨)이 있는 경우.

ü  앱에서 Foreground Service 시작.

ü  다른 앱이 포 그라운드에 있으며 배경에 있는 앱의 구성 요소를 사용하고 있는 경우. 예를 들어 포 그라운드에있는 응용 프로그램 A가 응용 프로그램 B에서 제공하는 서비스에 바인딩 되어있는 경우로 응용 프로그램 B Foreground에서 고려되며 Background에서 사용하기 위해 Android에 의해 종료되지 않는다.

n  앱이 백그라운드에 있어도 Android가 앱을 깨우고 몇 분 동안 이러한 제한 사항을 완화하여 앱에서 몇 가지 작업을 수행 할 수있는 경우가 있다. 우선 순위가 높은 Firebase 클라우드 메시지가 앱에 수신, 브로드 캐스트 메시지를 수신되는 경우 Notification에 대한 응답으로 PendingIntent를 실행한다.

n  기존 Xamarin.Android 응용 프로그램은 Android 8.0에서 발생할 수있는 문제를 피하기 위해 백그라운드 작업 수행 방법을 변경해야 할 수도 있다. 다음은 Android 서비스에 대한 실용적인 대안이다.

ü  Android Job Scheduler 또는 Firebase Job Dispatcher를 사용하여 백그라운드에서 작업 실행 예약 - 이 두 라이브러리는 백그라운드 작업을 개별 작업 단위로 분리하는 애플리케이션을위한 프레임 워크를 제공한다.

ü  Foreground에서 서비스 시작 - Foreground 서비스는 앱이 Background에서 일부 작업을 수행해야 하고 사용자가 주기적으로 해당 작업과 상호 작용해야하는 경우에 유용하다. Foreground 서비스는 앱이 백그라운드 작업을 실행하고 있으며 작업을 모니터링하거나 상호 작용할 수있는 방법을 제공한다는 것을 사용자가 알 수 있도록 지속적인 알림을 표시 한다. 예를 들어 사용자에게 팟 캐스트를 재생중인 팟 캐스팅 앱 또는 나중에 즐길 수 있도록 팟 캐스트 에피소드를 다운로드하는 경우가 있다.

ü  우선 순위가 높은 Firebase Cloud Message (FCM) 사용 - Android가 앱에 우선 순위가 높은 FCM을 받으면 해당 앱이 짧은 시간 동안 백그라운드에서 서비스를 실행할 수 있다.

ü  응용 프로그램이 Foreground로 들어올 때 작업 지연 - 이전 솔루션이 실행 가능하지 않은 경우 응용 프로그램이 포 그라운드로 오면 일시 중지하고 작업을 다시 시작할 수 있는 고유 한 방법을 개발해야 한다.

 

 

2.2.9 Xamarin.Android Bound Service

 

n  바인딩된 서비스는 Android Activity와 같은 클라이언트와 상호 작용할 수 있는 클라이언트-서버 인터페이스를 제공 하는 안드로이드 서비스 이다.

n  여러 클라이언트(액티비티등)가 동시에 서비스의 단일 인스턴스에 연결 되어 있을 수 있으며 바인딩된 서비스와 클라이언트는 서로 격리되며 대신 Android는 둘의 연결의 상태를 관리 하는 중간 개체를 제공한다.

 

n  클라이언트가 BindService 메서드를 사용하여 서비스에 바인딩 할 수 있는 세 가지 방법

ü  서비스 바인더(Service Binder) : 서비스 바인더는 Android.OS.IBinder 인터페이스를 구현하는 클래스로 대부분의 응용 프로그램은 인터페이스를 직접 구현하지 않고 대신 Android.OS.Binder 클래스를 상속받는다. 가장 일반적인 접근 방식이며 서비스와 클라이언트가 동일한 프로세스에 존재할 때 적합하다.

ü  메신저 사용(Using a Messanger) : 서비스가 별도의 프로세스에 있을 때 적합하다. 대신 서비스 요청은 Android.OS.Messenger를 통해 클라이언트와 서비스간에 정렬된다. Messenger 요청을 처리 할 Android.OS.Handler가 서비스에 생성된다.

ü  Android 인터페이스 정의 언어 (AIDL, Using Android Interface Definition Language) 사용 :  AIDL은이 가이드에서 다루지 않는 고급 기술이다.

n  클라이언트가 서비스에 바인딩되면 Android.OS.IBinder 객체를 통해 두 클라이언트 간의 통신이 이루어지는데 이 개체는 클라이언트가 서비스와 상호 작용할 수 있도록 하는 인터페이스를 담당한다. Xamarin.Android 응용 프로그램이 처음부터 이 인터페이스를 구현할 필요는 없지만 Android SDK는 클라이언트와 서비스간에 객체를 마샬링하는 데 필요한 대부분의 코드를 처리하는 Android.OS.Binder 클래스를 제공한다.

n  클라이언트가 서비스를 끝내면 클라이언트에서 UnbindService 메서드를 호출하여 바인딩을 해제해야 한다. 마지막 클라이언트가 서비스에서 언 바운드되면 Android는 바인딩 된 서비스를 중지한다.

n  바인딩 된 서비스를 구현하기 위해 Service 클래스를 확장하며, 클라이언트(Activity)에서 IServiceConnection을 구현하고 Binder를 확장하여 클라이언트가 서비스와 통신 할 수 있도록 한다. 

n  바운드 서비스를 사용하려면 다음 3가지 구성요소가 구현 되어야 한다.

n  Service 클래스 확장 및 생명주기 콜백 메소드 구현 : 서비스 요청 작업을 수행 할 코드가 포함된다.

n  IServiceConnection을 구현하는 클래스 : 이 인터페이스는 서비스에 대한 연결이 변경된 경우 (클라이언트가 서비스에 연결되거나 연결이 끊어진 경우) 클라이언트에게 알리기 위해 Android에서 호출하는 콜백 메서드를 제공한다. ServiceConnection은 클라이언트가 서비스와 직접 상호 작용하는 데 사용할 수있는 객체에 대한 참조를 제공하고 이 참조를 바인더(Binder)라고 한다.

n  IBinder 구현 클래스 : 바인더 구현은 클라이언트가 서비스와 통신하는 데 사용하는 API를 제공한다. 바인더는 바인딩 된 서비스에 대한 참조를 제공하거나 메서드를 직접 호출 할 수 있도록하거나 바인더가 응용 프로그램에서 바인딩 된 서비스를 캡슐화하고 숨기는 클라이언트 API를 제공 할 수 있다. IBinder는 원격 프로 시저 호출에 필요한 코드를 제공해야 하며 IBinder 인터페이스를 직접 구현할 필요는 없고 대신 IBinder에서 요구하는 대부분의 기본 기능을 제공하는 바인더 유형을 상속받아야 한다.

n  서비스 시작 및 바인딩 : 일단 서비스 연결, 바인더 및 서비스가 작성되면 Android 애플리케이션은 서비스 시작 및 바인딩을 담당한다.

n  Service 클래스 확장

ü  Xamarin.Android를 사용하여 서비스를 만들려면 Service를 서브 클래스 화하고 ServiceAttribute를 사용하여 클래스를 장식한다.

ü  Xamarin.Android 빌드 도구는 속성을 사용하여 앱의 AndroidManifest.xml 파일에 서비스를 올바르게 등록한다. 액티비티와 마찬가지로 바인딩 된 서비스는 라이프 사이클의 중요한 이벤트와 관련된 자체 라이프 사이클 및 콜백 메소드를 가지고 있는데 아래는 서비스가 구현할 일반적인 콜백 메소드의 예이다.

ü  OnCreate : Android가 서비스를 인스턴스화 할 때 호출되며 라이프 사이클 기간동안 서비스에 필요한 모든 변수 또는 객체를 초기화하는 데 사용되며 선택 사항이다.

ü  OnBind : 모든 바인딩 된 서비스에서 구현해야 한다. 첫 번째 클라이언트가 서비스에 연결을 시도 할 때 호출되며 클라이언트가 서비스와 상호 작용할 수 있도록 IBinder 인스턴스를 반환한다. 서비스가 실행되는 동안 IBinder 개체는 향후 클라이언트에서 서비스 바인딩 요청을 수행하는 데 사용된다.

ü  OnUnbind : 바인딩 된 모든 클라이언트가 언 바운드 될 때 호출된다. 이 메서드에서 true를 반환하면 새 클라이언트가 바인딩 할 때 OnUnbind에 전달된 인텐트가 나중에 OnRebind에서 호출된다. 언 바운드 후 서비스가 계속 실행될 때 이 작업을 수행합니다. 기본값은 false를 반환하며 아무 것도 수행하지 않는다. (선택 사항)

ü  OnDestroy : Android가 서비스를 파괴 할 때 호출되며 자원 해제와 같이 필요한 정리 작업을 수행해야 하는데 선택 사항이다.

 

 

 

public interface IHello

{

    string hello(string name);

}

 

class Hello : IHello

 {

    public string hello(string name)

    {

        return $"안녕~ {name}";

    }

}

// 안드로이드 서비스 클래스 : 서비스할 작업을 기술

    [Service(Name = "example.xamarin.HelloService")]

    public class HelloService : Service, IHello

    {

        static readonly string TAG = typeof(HelloService).FullName;

        IHello h;

 

        public IBinder Binder { get; private set; }

 

        public override void OnCreate()

        {

            base.OnCreate();

            Log.Debug(TAG, "OnCreate...");

            h = new Hello();     //서비스할 메소드를 가진 클래스를 인스턴스화

        }

 

        //반드시 구현해야 되는 메소드

        public override IBinder OnBind(Intent intent)

        {

            Log.Debug(TAG, " OnBind...");

            this.Binder = new HelloBinder(this);

            return this.Binder;

        }

 

        public override bool OnUnbind(Intent intent)

        {

            Log.Debug(TAG, " OnUnBind...");

            return base.OnUnbind(intent);

        }

 

        public override void OnDestroy()

        {

            Log.Debug(TAG, " OnDestroy...");

            Binder = null;

            h = null;

            base.OnDestroy();

        }

 

        // 서비스 메소드

        public string hello(string name)

        {

            return h?.hello(name);

        }

    }

 

n  IBinder 구현하기

ü  IBinder 객체는 클라이언트와 서비스 사이의 통신 채널을 제공한다. 안드로이드 응용 프로그램은 IBinder 인터페이스를 구현해서는 안되며, Android.OS.Binder를 확장해야 한다. Binder 클래스는 바인더 객체를 서비스 (별도의 프로세스에서 실행될 수 있음)에서 클라이언트로 마샬링하는 데 필요한 많은 인프라를 제공한다. 대부분의 경우 바인더 하위 클래스는 코드 몇 줄에 불과하며 서비스에 대한 참조를 래핑한다. 예문에서 TimestampBinder는 클라이언트에 TimestampService를 노출하는 속성을 가진다.

public class HelloBinder : Binder, IHello

    {

        public HelloService Service { get; private set; }

 

        public HelloBinder(HelloService service)

        {

            this.Service = service;

        }

 

        public string hello(string name)

        {

            return Service?.hello(name);  //바인더에서 서비스의 메소드를 호출

        }

    }

 

n  Service Connection 생성

ü  IServiceConnection Binder 객체를 클라이언트(액티비티)에 연결한다. IServiceConnection 인터페이스를 구현하는 것 외에도 클래스는 Java.Lang.Object를 확장해야 한며 서비스 연결은 클라이언트가 바인더에 액세스 할 수있는 방법을 제공해야 하므로 바인딩 된 서비스와 통신해야 한다.

ü  바인딩 프로세스의 일부로 Android는 바인딩되는 서비스의 이름과 서비스 자체에 대한 참조를 보유하는 바인더를 제공하여 OnServiceConnected 메소드를 호출한다. 아래 예에서 서비스 연결에는 Binder에 대한 참조를 보유하는 속성과 클라이언트가 서비스에 연결되어 있는지 여부에 대한 부울 플래그라는 두 가지 속성이 있다.

ü  OnServiceDisconnected 메서드는 클라이언트와 서비스 간의 연결이 예기치 않게 손실되거나 손상된 경우에만 호출되며 이 방법을 사용하면 클라이언트가 서비스 중단에 응답 할 수 있다.

[HelloServiceConnection.cs]

using Android.Util;

using Android.OS;

using Android.Content;

 

namespace BoundServiceDemo

{

    // 서비스에 대한 연결이 변경된 경우(클라이언트가 서비스에 연결되거나 연결이 끊어진 경우)

    // 클라이언트에게 알리기 위해 Android에서 호출하는 콜백 메서드를 제공한다.

    // ServiceConnection은 클라이언트가 서비스와 직접 상호 작용하는 데 사용할 수있는

    // 객체에 대한 참조를 제공하고 이 참조를 바인더(Binder)라고 한다.

    public class HelloServiceConnection : Java.Lang.Object, IServiceConnection, IHello

    {

        static readonly string TAG = typeof(HelloServiceConnection).FullName;

        MainActivity activity;

 

        public bool IsConnected { get; private set; }

        public HelloBinder Binder { get; private set; }

 

        public HelloServiceConnection(MainActivity activity)

        {

            IsConnected = false;

            Binder = null;

            this.activity = activity;

        }

 

        public void OnServiceConnected(ComponentName name, IBinder service)

        {

            Binder = service as HelloBinder;

            IsConnected = this.Binder != null;

 

            string message = "OnServiceConnected :: ";

            Log.Debug(TAG, $"OnServiceConnected {name.ClassName}");

 

            if(IsConnected)

            {

                message = message + " bound to service " + name.ClassName;

                this.activity.UpdateUiForBoundService();

            }

            else

            {

                message = message + " not bound to service " + name.ClassName;

                this.activity.UpdateUiForUnboundService();

            }

 

            Log.Info(TAG, message);

            activity.messageTextView.Text = message;

        }

 

        public void OnServiceDisconnected(ComponentName name)

        {

            Log.Debug(TAG, $"OnServiceDisConnected {name.ClassName}");

            IsConnected = false;

            Binder = null;

            activity.UpdateUiForUnboundService();

        }

 

        public string hello(string name)

        {

            if (!IsConnected) return null;

            return Binder?.hello(name);

        }      

    }

}

n  MainActivity안의 GetHello 번튼 클릭시 serviceConnection의 바인더를 사용하여 Service의 메서드를 호출 할 수 있다.

//GetHello 버튼 클릭

     void btnGetHello_Click(object sender, EventArgs e)

     {

         if (serviceConnection.IsConnected)

             messageTextView.Text = serviceConnection.Binder.Service.hello(nameEditText.Text);

         else

messageTextView.SetText(Resource.String.service_not_connected);          

}

 

 

n  명시적 인텐트(Explicit Intent)가 있는 서비스 시작 및 바인딩

n  바운드 서비스를 사용하려면 클라이언트 ( : Activity)Android.Content.IServiceConnection을 구현하고 BindService 메서드를 호출하는 객체를 인스턴스화 해야한다. BindService는 서비스가 바인딩 된 경우 true를 반환하고 그렇지 않으면 false를 반환하며 BindService 메서드는 세 가지 매개 변수를 사용한다.

ü  Intent - Intent는 연결할 서비스를 명시.

ü  IServiceConnection 개체 : 바인딩 된 서비스가 시작되고 중지 될 때 클라이언트에 알리는 콜백 메서드를 제공하는 중개자.

ü  Android.Content.Bind enum : 객체를 바인딩 할 때 시스템에서 사용하는 플래그 집합으로 가장 일반적으로 사용되는 값은 Bind.AutoCreate이며, 서비스가 아직 실행되지 않은 경우 자동으로 시작된다.

n  Explicit Intent 를 사용하여 Activity에서 바운드 서비스를 시작하는 방법의 예.

protected override void OnStart()

        {

            base.OnStart();

            if (serviceConnection == null) serviceConnection = new HelloServiceConnection(this);

 

            Intent intent = new Intent(this, typeof(HelloService));

            BindService(intent, serviceConnection, Bind.AutoCreate);

      }

 

 

2.2.10 Xamarin.Android Service Example

2.2.10.1 Xamarin.Android Bound Service 실습

n  환영 메시지를 Xamarin.Android의 바운드 서비스를 이용하여 출력하는 Xamarin.Android App을 만들어 보자. 이름을 입력하고  “HELLO 호출버튼을 클릭하면 서비스의 hello() 메소드를 호출하여 결과를 리턴받는 예제 이다.

n  Xamarin.Android, Blank App으로 BoundServiceDemo라는 프로젝트 생성

n  [activity_main.axml] : 컨트롤 이름 주의!

n  Resources\values\strings.xml

<resources>

    <string name="app_name">BoundServiceDemo</string>

    <string name="action_settings">Settings</string>

 

    <string name="name_message">이름을 입력하세요.</string>

    <string name="btnGetHello">hello 호출</string>

    <string name="btnStop">서비스로부터 언바운드</string>

    <string name="btnReStart">서비스에 다시 바운드</string>

    <string name="service_not_connected">서비스 연결 실패!</string>

</resources>

n  [IHello.cs] 

namespace BoundServiceDemo

{

    public interface IHello

    {

        string hello(string name);

    }

}

n  [Hello.cs] 

namespace BoundServiceDemo

{

    class Hello : IHello

    {

        public string hello(string name)

        {

            return $"안녕~ {name}";

        }

    }

}

n  [HelloBinder.cs] 

using Android.OS;

 

namespace BoundServiceDemo

{

    // 바인더는 클라이언트가 서비스와 통신하는 데 사용하는 API를 제공한다.

    // 바인더를 통해 안드로이드 서비스에 바인딩 하고 메소드를 호출할 수 있다.

    // Android.OS.IBinder를 구현한 Binder를 상속받으면 된다.

    // 클라이언트가 서비스에 바인딩되면 Android.OS.IBinder 객체를 통해 통신이 이루어지는데

    // HelloBinder가 클라이언트와 서비스와 상호 작용할 수 있는 인터페이스를 담당한다.

    public class HelloBinder : Binder, IHello

    {

        public HelloService Service { get; private set; }

 

        public HelloBinder(HelloService service)

        {

            this.Service = service;

        }

 

        public string hello(string name)

        {

            return Service?.hello(name);

        }

    }

}

n  [HelloServiceConnection.cs] 

using Android.Util;

using Android.OS;

using Android.Content;

 

namespace BoundServiceDemo

{

    // 서비스에 대한 연결이 변경된 경우(클라이언트가 서비스에 연결되거나 연결이 끊어진 경우)

    // 클라이언트에게 알리기 위해 Android에서 호출하는 콜백 메서드를 제공한다.

    // ServiceConnection은 클라이언트가 서비스와 직접 상호 작용하는 데 사용할 수있는

    // 객체에 대한 참조를 제공하고 이 참조를 바인더(Binder)라고 한다.

    public class HelloServiceConnection : Java.Lang.Object, IServiceConnection, IHello

    {

        static readonly string TAG = typeof(HelloServiceConnection).FullName;

        MainActivity activity;

 

        public bool IsConnected { get; private set; }

        public HelloBinder Binder { get; private set; }

 

        public HelloServiceConnection(MainActivity activity)

        {

            IsConnected = false;

            Binder = null;

            this.activity = activity;

        }

 

        public void OnServiceConnected(ComponentName name, IBinder service)

        {

            Binder = service as HelloBinder;

            IsConnected = this.Binder != null;

 

            string message = "OnServiceConnected :: ";

            Log.Debug(TAG, $"OnServiceConnected {name.ClassName}");

 

            if(IsConnected)

            {

                message = message + " bound to service " + name.ClassName;

                this.activity.UpdateUiForBoundService();

            }

            else

            {

                message = message + " not bound to service " + name.ClassName;

                this.activity.UpdateUiForUnboundService();

            }

 

            Log.Info(TAG, message);

            activity.messageTextView.Text = message;

        }

 

        public void OnServiceDisconnected(ComponentName name)

        {

            Log.Debug(TAG, $"OnServiceDisConnected {name.ClassName}");

            IsConnected = false;

            Binder = null;

            activity.UpdateUiForUnboundService();

        }

 

        public string hello(string name)

        {

            if (!IsConnected) return null;

            return Binder?.hello(name);

        }      

    }

}

n  [HelloService.cs] 

using Android.App;

using Android.Content;

using Android.OS;

using Android.Util;

 

namespace BoundServiceDemo

{

    // 안드로이드 서비스 클래스 : 서비스할 작업을 기술

    [Service(Name = "example.xamarin.HelloService")]

    public class HelloService : Service, IHello

    {

        static readonly string TAG = typeof(HelloService).FullName;

        IHello h;

 

        public IBinder Binder { get; private set; }

 

        public override void OnCreate()

        {

            base.OnCreate();

            Log.Debug(TAG, "OnCreate...");

            //서비스 메소드를 가진 객체 생성

            h = new Hello();

        }

 

        //반드시 구현해야 되는 메소드

        public override IBinder OnBind(Intent intent)

        {

            Log.Debug(TAG, " OnBind...");

            this.Binder = new HelloBinder(this);

            return this.Binder;

        }

 

        public override bool OnUnbind(Intent intent)

        {

            Log.Debug(TAG, " OnUnBind...");

            return base.OnUnbind(intent);

        }

 

        public override void OnDestroy()

        {

            Log.Debug(TAG, " OnDestroy...");

            Binder = null;

            h = null;

            base.OnDestroy();

        }

 

        //서비스 메소드

        public string hello(string name)

        {

            return h?.hello(name);

        }

    }

}

n  [MainActivity.cs] 

using Android.App;

using Android.OS;

using Android.Support.V7.App;

using Android.Widget;

using Android.Content;

using System;

 

namespace BoundServiceDemo

{

    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]

    public class MainActivity : AppCompatActivity

    {

        Button btnGetHello;

        Button btnStopService;

        Button btnReStartService;

        EditText nameEditText;

        internal TextView messageTextView;

 

        HelloServiceConnection serviceConnection;

 

        protected override void OnCreate(Bundle savedInstanceState)

        {

            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource

            SetContentView(Resource.Layout.activity_main);

 

            btnGetHello = FindViewById<Button>(Resource.Id.btnGetHello);

            btnGetHello.Click += btnGetHello_Click;

 

            btnStopService = FindViewById<Button>(Resource.Id.btnStop);

            btnStopService.Click += btnStopService_Click;

 

            btnReStartService = FindViewById<Button>(Resource.Id.btnReStart);

            btnReStartService.Click += btnReStartService_Click;

 

            messageTextView = FindViewById<TextView>(Resource.Id.message);

            nameEditText = FindViewById<EditText>(Resource.Id.txtName);

        }

 

        protected override void OnStart()

        {

            base.OnStart();

            if (serviceConnection == null) serviceConnection = new HelloServiceConnection(this);

 

            DoBindService();

        }

 

        protected override void OnResume()

        {

            base.OnResume();

            if (serviceConnection.IsConnected) UpdateUiForBoundService();

            else UpdateUiForUnboundService();

        }

 

        protected override void OnPause()

        {

            btnGetHello.Click -= btnGetHello_Click;

            btnReStartService.Click -= btnReStartService_Click;

            btnStopService.Click -= btnStopService_Click;

 

            base.OnPause();

        }

 

        protected override void OnStop()

        {

            DoUnBindService();

            base.OnStop();

        }

 

        internal void UpdateUiForBoundService()

        {

            btnGetHello.Enabled = true;

            btnReStartService.Enabled = false;

            btnStopService.Enabled = true;

        }

 

        internal void UpdateUiForUnboundService()

        {

            btnGetHello.Enabled = false;

            btnReStartService.Enabled = true;

            btnStopService.Enabled = false;

        }

 

        //GetHello 버튼 클릭

        void btnGetHello_Click(object sender, EventArgs e)

        {

            if (serviceConnection.IsConnected)

                messageTextView.Text = serviceConnection.Binder.Service.hello(nameEditText.Text);

            else

                messageTextView.SetText(Resource.String.service_not_connected);

        }

 

        //StopService 버튼 클릭

        void btnStopService_Click(object sender, EventArgs e)

        {

            DoUnBindService();

            UpdateUiForUnboundService();

        }

 

        //ReStartService 버튼 클릭

        void btnReStartService_Click(object sender, EventArgs e)

        {

            DoBindService();

            UpdateUiForBoundService();

        }

 

        void DoUnBindService()

        {

            UnbindService(serviceConnection);

            btnReStartService.Enabled = true;

            messageTextView.Text = "";

        }

 

        void DoBindService()

        {

            Intent intent = new Intent(this, typeof(HelloService));

            BindService(intent, serviceConnection, Bind.AutoCreate);

            messageTextView.Text = "";

        }

 

    }

}

n  실행화면


 

, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Comments