Designing a Remote Interface Using AIDL

Since each application runs in its own process, and you can write a service that runs in a different process from your Application's UI, sometimes you need to pass objects between processes. On the Android platform, one process can not normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and "marshall" the object across that boundary for you.

각각의 애플리케이션이 자신만의 프로세스에서 실행되고, 여러분이 애플리케이션의 사용자 인터페이스와는 분리되어 있는 프로세스상에서 실행되는 서비스를 만들 수 있기 때문에, 가끔 여러분은 프로세스들간에 오브젝트를 전달해야 할 필요가 있다. 안드로이드 플랫폼상에서 하나의 프로세스는 보통 다른 프로세스의 메모리를 접근할 수 없다. 그렇기 때문에 프로세스들 간의 대화를 위해서는, 운영체제가 이해할 수 있는 프리미티브primitive 형태로 오브젝트를 분해하고 여러분을 위해 프로세스의 경계를 넘어서 오브젝트를 마샬링marshalling 할 필요가 있다.

The code to do that marshalling is tedious to write, so we provide the AIDL tool to do it for you.

마샬링을 하기 위한 코드를 작성하는 것은 장황하다. 그래서 우리는 여러분을 위해 그것을 해주는 AIDL 툴tool을 제공한다.

AIDL (Android Interface Definition Language) is an IDL language used to generate code that enables two processes on an Android-powered device to talk using interprocess communication (IPC). If you have code in one process (for example, in an Activity) that needs to call methods on an object in another process (for example, a Service), you would use AIDL to generate code to marshall the parameters.

AIDL(Android Interface Definition Language , 안드로이드 인터페이스 정의 언어)은 안드로이드 디바이스에서 두 개의 프로세스가 IPC(Interprocess Communication, 프로세스 간 통신)를 사용해서 대화할 수 있는 코드를 작성하는데 사용되는 IDL 언어이다. 만약 여러분이 다른 프로세스(예를 들어 서비스)에 있는 오브젝트에 대한 메쏘드를 호출해야 하는 하나의 프로세스(예를 들어 액티비티) 내에 코드를 가지고 있다면, 파라미터를 먀샬링하는 코드를 생성하는 AIDL을 사용하면 된다.

The AIDL IPC mechanism is interface-based, similar to COM or Corba, but lighter weight. It uses a proxy class to pass values between the client and the implementation.

AIDL IPC 메커니즘은 COM 또는 Corba와 유사한 인터페이스 기반이지만, 보다 가볍다. 이 메커니즘은 클라이언트와 그것의 구현implementation간에 값을 전달하기 위해 프락시 클래스를 사용한다.

Implementing IPC Using AIDL

Follow these steps to implement an IPC service using AIDL.

AIDL을 사용해서 IPC 서비스를 구현하기 위해서는 아래의 단계를 따라하라.

  1. Create your .aidl file - This file defines an interface (YourInterface.aidl) that defines the methods and fields available to a client.
  2. Add the .aidl file to your makefile - (the ADT Plugin for Eclipse manages this for you). Android includes the compiler, called AIDL, in the tools/ directory.
  3. Implement your interface methods - The AIDL compiler creates an interface in the Java programming language from your AIDL interface. This interface has an inner abstract class named Stub that inherits the interface (and implements a few additional methods necessary for the IPC call). You must create a class that extends YourInterface.Stub and implements the methods you declared in your .aidl file.
  4. Expose your interface to clients - If you're writing a service, you should extend Service and override Service.onBind(Intent) to return an instance of your class that implements your interface.
  1. 여러분의 .aidl 파일을 생성하라 ? 이 파일은 클라이언트에서 사용가능한 메쏘드들과 필드들을 정의하는 인터페이스(YourInterface.aidl)를 정의한다.
  2. 여러분의 makefile에 .aidl 파일을 추가하라 ? (이클립스의 ADT 플러그인은 여러분을 위해 이것을 잘 관리한다). 안드로이드는 tools/ directory에서 ADIL이라 불리는 컴파일러를 포함하고 있다.
  3. 여러분의 인터페이스 메쏘드를 구현하라 ? AIDL 컴파일러는 여러분의 AIDL 인터페이스로부터 Java 프로그래밍 언어로 된 인터페이스를 생성한다. 이 인터페이스는 해당 인터페이스를 상속하는 (그리고 IPC 호출을 위해 필요한 몇가지 추가적인 메쏘드를 구현하는) Stub라는 내부의 추상 클래스inner abstract class를 가진다. 여러분은 YourInterface.Stub을 확장해서 여러분의 .aidl 파일에 선언한 메쏘드를 구현하는 클래스를 만들어야만 한다.
  4. 여러분의 인터페이스를 클라이언트에게 제시하라expose. 여러분이 서비스를 작성하고 있다면 여러분은 서비스를 확장해서 여러분의 인터페이스를 구현하는 여러분의 클래스에 대한 인스턴스를 리턴하는 Service.onBind(Intent)를 오버라이드 해야 한다.

Create an .aidl File

AIDL is a simple syntax that lets you declare an interface with one or more methods, that can take parameters and return values. These parameters and return values can be of any type, even other AIDL-generated interfaces. However, it is important to note that you must import all non-built-in types, even if they are defined in the same package as your interface. Here are the data types that AIDL can support:

AIDL은 여러분으로 하여금 파라미터와 리턴 값을 받을 수 있는 하나 또는 그 이상의 메쏘드로 인터페이스를 선언할 수 있게 하는 단순한 구문이다. 이 파라미터와 리턴 값은 어떠한 타입도 될 수 있고 심지어 다른 AIDL로부터 생성된 인터페이스일 수도 있다. 하지만 모든 내장되어built-in 있지 않은 타입들all non-built-in types이 여러분의 인터페이스로써 동일한 패키지에 정의되어 있다 할지라도 그 모든 타입들을 임포트import 해야한다는 것을 알고 있어야 한다. 여기에 AIDL이 지원하는 모든 데이터 타입들이 있다.

  • Primitive Java programming language types (int, boolean, etc) No import statement is needed.
  • One of the following classes (no import statements needed):
    • String
    • List - All elements in the List must be one of the types in this list, including other AIDL-generated interfaces and parcelables. List may optionally be used as a "generic" class (e.g. List<String>). The actual concrete class that the other side will receive will always be an ArrayList, although the method will be generated to use the List interface.
    • Map - All elements in the Map must be of one of the types in this list, including other AIDL-generated interfaces and parcelables. Generic maps, (e.g. of the form Map<String,Integer> are not supported. The actual concrete class that the other side will receive will always be a HashMap, although the method will be generated to use the Map interface.
    • CharSequence - This is useful for the CharSequence types used by TextView and other widget objects.
  • Other AIDL-generated interfaces, which are always passed by reference. An import statement is always needed for these.
  • Custom classes that implement the Parcelable protocol and are passed by value. An import statement is always needed for these.
  • 기본적인 Java 프로그래밍 언어의 타입들(정수(int), 불리언(boolean), 기타) - 임포트 import 절statement이 필요하지 않음.
  • 아래의 클래스 중의 하나(임포트(import) 절(statement)이 필요하지 않음)
    • String(문자열)
    • List(리스트) ? 리스트에 있는 모든 엘리먼트들은 다른 AIDL로부터 생성한 인터페이스와 파서러블parcelable을 포함한 그 리스트에 있는 타입 중 하나여야 한다. 리스트는 선택 사항으로 “일반적인generic” 클래스(예를 들어 List<String>)로써 사용되어질 수 있다. 다른 쪽에서 수신하게 될 실제의 구체적인 클래스concrete class는 해당 메쏘드가 리스트 인터페이스를 사용하기 위해 생성될지라도 항상 ArrayList가 될 것이다.
    • Map ? 맵에 있는 모든 엘리먼트들은 다른 AIDL로부터 생성한 인터페이스와 파서러블parcelable을 포함한 그 리스트에 있는 타입 중 하나여야 한다. (예를 들어 Map<String.Integer> 형태의) 일반적인 맵은 지원되지 않는다. 다른 쪽에서 수신하게 될 실제의 구체적인 클래스concrete class는 메쏘드가 맵 인터페이스를 사용하기 위해 생성될지라도 항상 HashMap이 될 것이다.
    • CharSequence ? 이것은 TextView와 다른 위젯 오브젝트에 의해 사용되는 CharSequence 타입들에 대해 유용하다.
  • 다른 AIDL로부터 생성된 인터페이스들, 이것들은 항상 레퍼런스에 의해 전달된다. 이것들을 위해서 임포트import 절이 항상 필요하다.
  • 파서러블Parcelable 프로토콜을 구현하고 값value에 의해 전달되는 커스텀 클래스. 이곳들을 위해서 항상 임포트import 절statement이 필요하다.

Here is the basic AIDL syntax:

여기에 기본적인 AIDL 구문이 있다.

// My AIDL file, named SomeClass.aidl
// Note that standard comment syntax is respected.
// Comments before the import or package statements are not bubbled up
// to the generated interface, but comments above interface/method/field
// declarations are added to the generated interface.

// Include your fully-qualified package statement.
package com.android.sample;

// See the list above for which classes need
// import statements (hint--most of them)
import com.android.sample.IAtmService;

// Declare the interface.
interface IBankAccountService {
    
    // Methods can take 0 or more parameters, and
    // return a value or void.
    int getAccountBalance();
    void setOwnerNames(in List<String> names);
    
    // Methods can even take other AIDL-defined parameters.
    BankAccount createAccount(in String name, int startingDeposit, in IAtmService atmService);

    // All non-Java primitive parameters (e.g., int, bool, etc) require
    // a directional tag indicating which way the data will go. Available
    // values are in, out, inout. (Primitives are in by default, and cannot be otherwise).
    // Limit the direction to what is truly needed, because marshalling parameters
    // is expensive.
    int getCustomerList(in String branch, out String[] customerList);
}

Implementing the Interface

AIDL generates an interface file for you with the same name as your .aidl file. If you are using the Eclipse plugin, AIDL will automatically be run as part of the build process (you don't need to run AIDL first and then build your project). If you are not using the plugin, you should run AIDL first.

AIDL은 여러분의 .aidl 파일과 같은 이름을 가진 인터페이스 파일을 생성한다. 여러분이 이클립스 플러그인을 사용하고 있다면, AIDL은 자동으로 빌드 프로세스의 일부로 실행될 것이다(여러분은 먼저 AIDL을 실행하고 나서 여러분의 프로젝트를 빌드할 필요가 없다). 여러분이 이 플러그인을 사용하고 있지 않다면 ,AIDL을 먼저 실행시켜야 한다.

The generated interface includes an abstract inner class named Stub that declares all the methods that you declared in your .aidl file. Stub also defines a few helper methods, most notably asInterface(), which takes an IBinder (passed to a client's onServiceConnected() implementation when applicationContext.bindService() succeeds), and returns an instance of the interface used to call the IPC methods. See the section Calling an IPC Method for more details on how to make this cast.

생성된 인터페이스는 여러분의 .aidl 파일에서 선언했던 모든 메쏘드를 선언하는 스터브Stub라고 불리는 추상 내부 클래스abstract inner class를 포함한다. 스터브Stub는 또한 몇가지 헬퍼 메쏘드들helper methods을 정의한다. 그 중에서도 가장 두드러지는 헬퍼 메쏘드인 asInterface()는 (애플리케이션의 contentext.bindService()가 성공할 때 클라이언트의 onServiceConnected()에 전달되는) IBinder를 가지며, IPC 메쏘드를 호출하기 위해 사용되는 인터페이스의 인스턴스를 리턴한다. 이것에 대한 더 자세한 사항에 대해서는 이후 나오는 “IPC 메쏘드 호출하기”를 보라.

To implement your interface, extend YourInterface.Stub, and implement the methods. (You can create the .aidl file and implement the stub methods without building between--the Android build process will process .aidl files before .java files.)

여러분의 인터페이스를 구현하기 위해서는, YourInterface.Stub를 확장하고 메쏘드를 구현하라(여러분은 .aidl 파일을 생성하고 빌드할 필요없이 스터브(Stub) 메쏘드를 구현할 수 있다 ? 안드로이드 빌드 프로세스는 .java 파일에 앞서서 .aidl 파일을 처리할 것이다).

Here is an example of implementing an interface called IRemoteService, which exposes a single method, getPid(), using an anonymous instance:

여기에 익명의 인스턴스를 사용하는 getPid()라는 하나의 메쏘드를 드러내는 IRemoteService라는 인터페이스를 구현하는 예제가 있다.

// No need to import IRemoteService if it's in the same project.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
    public int getPid(){
        return Process.myPid();
    }
}

A few rules about implementing your interface:

다음은 여러분의 인터페이스를 구현하는 것에 대한 몇 가지 규칙들이다.

  • No exceptions that you throw will be sent back to the caller.
  • IPC calls are synchronous. If you know that an IPC service takes more than a few milliseconds to complete, you should not call it in the Activity/View thread, because it might hang the application (Android might display an "Application is Not Responding" dialog). Try to call them in a separate thread.
  • Only methods are supported; you cannot declare static fields in an AIDL interface.
  • 여러분이 만들어내는 어떤 예외exception도 호출자에게 되돌려 보내지지 않는다.
  • IPC 호출은 동기적이다. 여러분이 IPC 서비스가 완료되는데 1/1000초 이상이 걸린다는 것을 알고 있다면, 여러분은 액티비티/뷰 쓰레드에서 그것을 호출해서는 안 된다. 왜냐하면, 그것이 애플리케이션을 멈출게 할 지 모르기 때문이다(안드로이드는 “애플리케이션이 응답하지 않는다(Application is Not Responding)” 다이얼로그를 보여줄 지도 모른다). 그것을 별도의 쓰레드에서 호출하도록 시도하라.
  • 오직 메쏘드만이 지원된다. 여러분은 AIDL 인터페이스에서 정적 필드static fields를 선언할 수 없다.

Exposing Your Interface to Clients

Now that you've got your interface implementation, you need to expose it to clients. This is known as "publishing your service." To publish a service, inherit Service and implement Service.onBind(Intent) to return an instance of the class that implements your interface. Here's a code snippet of a service that exposes the IRemoteService interface to clients.

이제 여러분의 인터페이스를 구현했다면, 여러분은 그 인터페이스를 클라이언트에게 드러낼 필요가 있다. 이것은 “서비스 배포하기publishing your service”로 알려져 있다. 서비스를 배포하려면 서비스Service를 상속받고, 그리고 여러분의 인터페이스를 구현하는 클래스의 인스턴스를 리턴하는 Service.onBind(Intent)를 구현하라. 여기에 IRemoteService 인터페이스를 클라이언트에 제시하는 하나의 서비스에 대한 코드 단편snippet이 있다.

public class RemoteService extends Service {
...
    @Override
    public IBinder onBind(Intent intent) {
        // Select the interface to return.  If your service only implements
        // a single interface, you can just return it here without checking
        // the Intent.
        if (IRemoteService.class.getName().equals(intent.getAction())) {
            return mBinder;
        }
        if (ISecondary.class.getName().equals(intent.getAction())) {
            return mSecondaryBinder;
        }
        return null;
    }

    /**
     * The IRemoteInterface is defined through IDL
     */
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public void registerCallback(IRemoteServiceCallback cb) {
            if (cb != null) mCallbacks.register(cb);
        }
        public void unregisterCallback(IRemoteServiceCallback cb) {
            if (cb != null) mCallbacks.unregister(cb);
        }
    };

    /**
     * A secondary interface to the service.
     */
    private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
        public int getPid() {
            return Process.myPid();
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
                float aFloat, double aDouble, String aString) {
        }
    };

}

Pass by value Parameters using Parcelables

If you have a class that you would like to send from one process to another through an AIDL interface, you can do that. You must ensure that the code for your class is available to the other side of the IPC. Generally, that means that you're talking to a service that you started.

여러분이 AIDL 인터페이스를 통해 하나의 프로세스에서 다른 프로세스로 보내고자 하는 클래스가 있다면 여러분은 그것을 할 수 있다. 여러분은 해당 클래스에 대한 코드가 IPC의 다른 쪽에서도 사용가능하도록 보장해야 한다. 일반적으로 이것은 여러분이 시작한 서비스와 대화하고 있다는 것을 의미한다.

There are five parts to making a class support the Parcelable protocol:

하나의 클래스가 파서러블Parcelable 프로토콜을 지원하도록 하는 다섯 가지 영역이 있다.

  1. Make your class implement the Parcelable interface.
  2. Implement the method public void writeToParcel(Parcel out) that takes the current state of the object and writes it to a parcel.
  3. value in a parcel into your object.
  4. Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface.
  5. Last but not least, create an aidl file that declares your parcelable class (as shown below). If you are using a custom build process, do not add the aidl file to your build. Similar to a header file in C, the aidl file isn't compiled.
  1. 여러분의 클래스에 파서러블Parcelable 인터페이스를 구현하라.
  2. 오브젝트의 현재 상태를 얻어서 그것을 파셀parcel에 쓰는write, public void writeToParcel(Parcel out) 메쏘드를 구현하라.
  3. 여러분의 오브젝트 안으로 파셀parcel 내의 값을 읽어오는 public void readFromParcel(Parcel in) 메쏘드를 구현하라.
  4. Parcelable.Creator 인터페이스를 구현하는 오브젝트인 여러분의 클래스에 CREATOR 라고 불리는 정적 필드static field를 추가하라.
  5. 지막이지만 아주 중요한 것으로, (아래에서 보여지는 처럼) 여러분의 파서러블parcelable 클래스를 선언하는 aidl 파일을 생성하라. 만약 여러분이 커스텀 빌드 프로세스를 사용한다면, 여러분의 빌드에 aidl 파일을 추가하지 마라. C에서 헤더파일과 유사하게 aidl 파일은 컴파일되지 않는다.

AIDL will use these methods and fields in the code it generates to marshall and unmarshall your objects.

AIDL은 여러분의 오브젝트를 마샬링하고marshall 언마샬링하기unmarshall 위해 그것이 생성하는 코드에서 위에서 언급한 메쏘드와 필드들을 사용할 것이다.

Here is an example of how the Rect class implements the Parcelable protocol.

여기에 Rect 클래스가 파서러블Parcelable 프로토콜을 구현하는 방법의 예제가 있다.

import android.os.Parcel;
import android.os.Parcelable;

public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;

    public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
        public Rect createFromParcel(Parcel in) {
            return new Rect(in);
        }

        public Rect[] newArray(int size) {
            return new Rect[size];
        }
    };

    public Rect() {
    }

    private Rect(Parcel in) {
        readFromParcel(in);
    }

    public void writeToParcel(Parcel out) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }

    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}

Here is Rect.aidl for this example

여기에 위의 예제에 대한 Rect.aidl 이 있다.

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

The marshalling in the Rect class is pretty simple. Take a look at the other methods on Parcel to see the other kinds of values you can write to a Parcel.

Rect 클래스에서 마샬링marshalling은 꽤 간단한다. 여러분이 파셀parcel에 쓸 수 있는 다른 종류의 값을 보려면 파셀Parcel에 있는 다른 메쏘드를 살펴보라.

Warning: Don't forget the security implications of receiving data from other processes. In this case, the rect will read four numbers from the parcel, but it is up to you to ensure that these are within the acceptable range of values for whatever the caller is trying to do. See Security and Permissions for more on how to keep your application secure from malware.

Warning: 다른 프로세스에서 데이터를 받는 것에 대한 보안 관련 사항을 잊지 마라. 이 경우 rect는 파셀parcel에서 네 가지 숫자들을 읽는다. 그러나 호출자가 시도하는 것이 무엇이든 간에 그 값들이 수용가능한 값의 범위 내에 있는지를 확실하게 하는 것은 여러분에게 달려 있다. 여러분의 애플리케이션을 악의적인 소프트웨어malware로부터 안전하게 지키는 방법에 대한 더 많은 정보는 8장. “보안 및 퍼미션”을 참고하라.

Calling an IPC Method

Here are the steps a calling class should make to call your remote interface:

여기에 호출하는 클래스가 여러분의 원격 인터페이스를 호출하기 위해 해야 하는 단계들이 있다.

  1. Declare a variable of the interface type that your .aidl file defined.
  2. Implement ServiceConnection.
  3. Call Context.bindService(), passing in your ServiceConnection implementation.
  4. In your implementation of ServiceConnection.onServiceConnected(), you will receive an IBinder instance (called service). Call YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter to YourInterface type.
  5. Call the methods that you defined on your interface. You should always trap DeadObjectException exceptions, which are thrown when the connection has broken; this will be the only exception thrown by remote methods.
  6. To disconnect, call Context.unbindService() with the instance of your interface.
  1. 여러분의 .aidl 파일이 정의했던 인터페이스 타입 변수를 선언하라.
  2. ServiceConnection을 구현하라.
  3. 여러분의 ServiceConnection 구현을 전달하는 Context.bindService()를 호출하라.
  4. 여러분의 ServiceConnection.onServiceConnected() 구현에서, 여러분은 (service라 불리우는) IBinder 인스턴스를 받을 것이다. 리턴된 파라미터를 YourInterface 타입에 캐스트하기 위한 YourInterfaceName.Stub.asInterface((IBinder)service)를 호출하라.
  5. 러분의 인터페이스에서 정의한 메쏘드를 호출하라. 여러분은 항상 DeadObjectException 예외 사항을 처리trap해야 한다. 이 예외 사항은 연결이 끊어질 때 발생한다. 이것은 원격 메쏘드에서 발생하는 유일한 예외 사항이다.
  6. 연결을 끊으려면, 여러분의 인터페이스의 인스턴스를 가지고 Context.unbindService()를 호출하라.

A few comments on calling an IPC service:

IPC 서비스를 호출하는 것에 대한 몇 가지 커멘트comments

  • Objects are reference counted across processes.
  • You can send anonymous objects as method arguments.
  • 오브젝트는 프로세스들 전역에 걸쳐 레퍼런스 갯수가 계산된다.
  • 여러분은 메쏘드 아규먼트로써 익명의 오브젝트를 보낼 수 있다.

Here is some sample code demonstrating calling an AIDL-created service, taken from the Remote Activity sample in the ApiDemos project.

여기에 ApiDemos 프로젝트의 Remote Activity 샘플에서 가져온 AIDL로 만들어진 서비스를 호출하는 것을 보여주는 예제 코드가 있다.

public class RemoteServiceBinding extends Activity {
    /** The primary interface we will be calling on the service. */
    IRemoteService mService = null;
    /** Another interface we use on the service. */
    ISecondary mSecondaryService = null;

    Button mKillButton;
    TextView mCallbackText;

    private boolean mIsBound;

    /**
     * Standard initialization of this activity.  Set up the UI, then wait
     * for the user to poke it before doing anything.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.remote_service_binding);

        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(mUnbindListener);
        mKillButton = (Button)findViewById(R.id.kill);
        mKillButton.setOnClickListener(mKillListener);
        mKillButton.setEnabled(false);

        mCallbackText = (TextView)findViewById(R.id.callback);
        mCallbackText.setText("Not attached.");
    }

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IRemoteService.Stub.asInterface(service);
            mKillButton.setEnabled(true);
            mCallbackText.setText("Attached.");

            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

            // As part of the sample, tell the user what happened.
            Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mKillButton.setEnabled(false);
            mCallbackText.setText("Disconnected.");

            // As part of the sample, tell the user what happened.
            Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * Class for interacting with the secondary interface of the service.
     */
    private ServiceConnection mSecondaryConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // Connecting to a secondary interface is the same as any
            // other interface.
            mSecondaryService = ISecondary.Stub.asInterface(service);
            mKillButton.setEnabled(true);
        }

        public void onServiceDisconnected(ComponentName className) {
            mSecondaryService = null;
            mKillButton.setEnabled(false);
        }
    };

    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
            // Establish a couple connections with the service, binding
            // by interface names.  This allows other applications to be
            // installed that replace the remote service by implementing
            // the same interface.
            bindService(new Intent(IRemoteService.class.getName()),
                    mConnection, Context.BIND_AUTO_CREATE);
            bindService(new Intent(ISecondary.class.getName()),
                    mSecondaryConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            mCallbackText.setText("Binding.");
        }
    };

    private OnClickListener mUnbindListener = new OnClickListener() {
        public void onClick(View v) {
            if (mIsBound) {
                // If we have received the service, and hence registered with
                // it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        mService.unregisterCallback(mCallback);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service
                        // has crashed.
                    }
                }

                // Detach our existing connection.
                unbindService(mConnection);
                unbindService(mSecondaryConnection);
                mKillButton.setEnabled(false);
                mIsBound = false;
                mCallbackText.setText("Unbinding.");
            }
        }
    };

    private OnClickListener mKillListener = new OnClickListener() {
        public void onClick(View v) {
            // To kill the process hosting our service, we need to know its
            // PID.  Conveniently our service has a call that will return
            // to us that information.
            if (mSecondaryService != null) {
                try {
                    int pid = mSecondaryService.getPid();
                    // Note that, though this API allows us to request to
                    // kill any process based on its PID, the kernel will
                    // still impose standard restrictions on which PIDs you
                    // are actually able to kill.  Typically this means only
                    // the process running your application and any additional
                    // processes created by that app as shown here; packages
                    // sharing a common UID will also be able to kill each
                    // other's processes.
                    Process.killProcess(pid);
                    mCallbackText.setText("Killed service process.");
                } catch (RemoteException ex) {
                    // Recover gracefully from the process hosting the
                    // server dying.
                    // Just for purposes of the sample, put up a notification.
                    Toast.makeText(RemoteServiceBinding.this,
                            R.string.remote_call_failed,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }
    };

    // ----------------------------------------------------------------------
    // Code showing how to deal with callbacks.
    // ----------------------------------------------------------------------

    /**
     * This implementation is used to receive callbacks from the remote
     * service.
     */
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.  Note that IPC calls are dispatched through a thread
         * pool running in each process, so the code executing here will
         * NOT be running in our main thread like most other things -- so,
         * to update the UI, we need to use a Handler to hop over there.
         */
        public void valueChanged(int value) {
            mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
        }
    };

    private static final int BUMP_MSG = 1;

    private Handler mHandler = new Handler() {
        @Override public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    mCallbackText.setText("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

    };
}
↑ Go to top