【Android官方文档】翻译Android官方文档-AIDL(三)

AIDL(Android Interface Definition Language 接口描述语言),它与其他IDL类似,你可以使用进程间通信的方法,以达到客户端到服务端交互的目的。在Android中,不同进程不能直接访问对方内存,因此,它们需要分解成操作系统能够理解的单位,使用AIDL可以处理这个问题。

注:只有在你允许来自不同应用的客户端跨进程通信访问你的service时,才能使用AIDL。如果你不使用IPC,那么你就需要使用Binder机制,如果你在不需要使用多线程的情况下使用IPC,那么你需要利用Message。(If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.)

在你设计AIDL接口前,要注意,调用AIDL接口要直接调用的函数。(You should not make assumptions about the thread in which the call occurs. What happens is different depending on whether the call is from a thread in the local process or a remote process. Specifically:)这取决是调用的是本地进程还是远程进程,具体如下:

  • 本地进程的调用是在同一线程中执行的. 如果是在主线程中, 该线程会在AIDL中继续执行,如果是在主线程之外, (that is the one that executes your code in the service). 因此, 如果只有本地线程访问服务,你完全可以控制线程执行它(如果是这样的话,那么你不应该如此,而是应该通过使用AIDL实现Binder创建接口)。
  • (Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process.) 你必须要为未知线程的调用做好准备,尤其是在同一时间内的多次调用. 总之,一句话,AIDL的实现必须要是线程安全的。
  • (The oneway keyword modifies the behavior of remote calls.)单向关键词修改远程调用的行为。 使用时,远程调用不阻塞; 发送一个简单的交互数据并且立即返回. (The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. )如果oneway 使用的本地call, 那么没有任何影响,呼叫仍然是同步的。

定义AIDL接口

我们必须要在 .aidl文件中定义AIDL接口, 然后保存这些代码(在 src/ 路径下) (of both the application hosting the service and any other application that binds to the service.)

当你创建的每个应用程序包含aidl文件,那么Android SDK工具将生成一个基于IBinder接口。在项目的根目录中保存它。服务必须实现IBinder接口。客户端应用程序可以绑定到服务和从IBinder调用执行IPC方法。

通过AIDL来实现创建绑定服务,要按照以下步骤:

  • 创建 .aidl文件:该文件定义了方法签名的编程接口。
  • 继承接口:Android SDK 会基于你的 .aidl文件生成 以java 程序语言的 接口。This interface has an inner abstract class named Stub that extends Binder and implements methods from your AIDL interface. 你必须继承 Stub class 并且实现它的方法 。
  • expose the interface to client:实现Service 并且重写onBind() to return your implementation of the Stub class.

注意: (Any changes that you make to your AIDL interface after your first release must remain backward compatible in order to avoid breaking other applications that use your service. ),因为为了提供服务接口,你的 .aidl 文件 必须复制到 other applications, (you must maintain support for the original interface.)


创建 .aidl 文件
AIDL 使用简单的语法规则,让你定义一个接口,其中可以包含一个或多个方法。这里的参数及方法的返回值可以定义为任何的类型 , 甚至可以是其他AIDL生成的接口.

你必须要用Java 来构建你的 .Aidl文件。 每一个aidl文件必须定义一个接口,仅仅只需要做的是:接口声明和方法签名。

在默认情况下, AIDL 以下数据类型:

  • 所有Java语言下的原始类型 (例如 int, long, char, boolean, 等等)
  • String
  • CharSequence
  • List
    任何元素在 List 中都必须有数据类型 ,甚至可以是AIDL生成的接口或者是声明的parcelables。List 中填充的数据是任选的(例如可以是, List). (The actual concrete class that the other side receives is always an ArrayList, although the method is generated to use the List interface.)
  • Map
    任何元素在Map中都必须有数据类型,甚至可以是AIDL生成的接口或者是声明的parcelables,Generic maps, (such as those of the form Map
// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

当你在你的应用中创建 aidl文件时,会保存在你的项目的 src/ 路径下, 并且 SDK tools 会生成 IBinder 接口文件在项目的 gen/ 路径下. 这个生成的文件名称是和 aidl 文件名称一样的, 但是却是 .java 文件(例如, IRemoteService.aidl 生成IRemoteService.java).

如果你使用的是Ecilipse, 几乎立即生成绑定类。如果你使用的不是Ecilipse ,Ant tool会在你编写项目的时候生成binder 类, (then the Ant tool generates the binder class next time you build your application)—在你完成对.aidl文件的编写后,就可以开始编译你的项目了,你的代码会再次与生成的类关联。(you should build your project with ant debug (or ant release) as soon as you’re finished writing the .aidl file, so that your code can link against the generated class.)


实现接口
在构建你的项目时, Android SDK tools 会在你创建了 .aidl文件后创建一个 .java的接口文件。在生成的接口中会有个名为 Stub的 stuclass类(The generated interface includes a subclass named Stub that is an abstract implementation of its parent interface (例如, YourInterface.Stub)在.aidl 中声明了所有的方法.)

注意: Stub also defines a few helper methods, most notably asInterface(), which takes an IBinder (usually the one passed to a client’s onServiceConnected() callback method) and returns an instance of the stub interface. See the section Calling an IPC Method for more details on how to make this cast.

需要实现的接口是来自 aidl文件的,实现生成的接口 (例如 YourInterface.Stub) .

这里是一个例子,实现了一个接口。 (defined by the IRemoteService.aidl example, above) using an anonymous instance:

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
    public int getPid(){
        return Process.myPid();
    }
    public void basicTypes(int anInt, long aLong, boolean aBoolean,
        float aFloat, double aDouble, String aString) {
        // Does nothing
    }
};

现在 mBinder 是 Stub 类的实例 (a Binder), (which defines the RPC interface for the service.)( In the next step, this instance is exposed to clients so they can interact with the service.)

以下是一些规则,你需要在实现你的AIDL文件时遵守。(There are a few rules you should be aware of when implementing your AIDL interface:)

  • (Incoming calls are not guaranteed to be executed on the main thread), 因此你需要考虑多线程,并且是线程安全的。(so you need to think about multithreading from the start and properly build your service to be thread-safe.)
  • 默认的情况下, RPC 调用是同步的, 如果你了解到Service完成需求应当就几毫秒,所以不应该将耗时的放在主线程中, 因为它可能会挂起应用程序 (Android 可能会出现”应用程序无响应” 对话框)—你应该做一个分离的线程在客户端中。
    -没有异常抛出给回调者。 No exceptions that you throw are sent back to the caller.

Expose the interface to clients
一旦你已经实现了你的服务的接口,你需要把它Expose给客户,让他们可以绑定到它。将接口Expose给你的服务,需要继承服务 ,并且实现 onBind() 方法 然后返回你的类的实例,实现的是 generated Stub .以下是个例子展示:.

public class RemoteService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return mBinder;
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public int getPid(){
            return Process.myPid();
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
            float aFloat, double aDouble, String aString) {
            // Does nothing
        }
    };
}

现在,当一个客户端 (例如 一个 Activity) 调用 bindService() 方法去连接 service,客户端的 onServiceConnected() 方法回调接收了mBinder 的实例,通过 service的onBind() 方法返回。

客户端必须连接接口类, 如果在程序中服务端和客户端是分离的, 那么 client 需要在 src/ 路径下拷贝 .aidl文件 (which generates the android.os.Binder interface—提供客户端连接AIDL的方法).

当客户端在onServiceConnected()方法回调中接收IBinder, 必须要调用 YourServiceInterface.Stub.asInterface(service) to cast the returned parameter to YourServiceInterface type. 例如:

IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Following the example above for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service
        mIRemoteService = IRemoteService.Stub.asInterface(service);
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "Service has unexpectedly disconnected");
        mIRemoteService = null;
    }
};

想看更多例子, 请看 RemoteService.java class


Passing Objects over IPC
如果你想从一个进程中发送数据到另一个进程中,那么就需要通过 IPC接口. 然而,你必须确保你的类的代码是可用的IPC通道和类必须支持Parcelable接口。 (Supporting the Parcelable interface is important because it allows the Android system to decompose objects into primitives that can be marshalled across processes.)

创建的类要用上 Parcelable 协议, 你必须遵守以下规则:

  • 创建的类需要实现Parcelable接口。
  • 实现 writeToParcel, (which takes the current state of the object and writes it to a Parcel.)
  • Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface.
  • 最后,创建一个 .aidl 文件并且声明你的parcelable 类(就像如下的Rect.aidl 文件所示).
    如果您使用的是自定义生成进程,那么添加AIDL文件不会加入到你的项目构建。类似于C语言的头文件,AIDL文件不会被编译。

AIDL使用这些方法在代码中生成 marshall 和 unmarshall 对象。(AIDL uses these methods and fields in the code it generates to marshall and unmarshall your objects.)

如下例子:

package android.graphics;

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

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

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();
    }
}

(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.)


Calling an IPC Method

这里是实现步骤:

  • .aidl 文件要在项目的 src/ 路径下。
  • 声明 IBinder接口的实例 。 (generated based on the AIDL).
  • 实现 ServiceConnection.
  • 调用 Context.bindService(),( passing in your ServiceConnection implementation.)
    -在你的 onServiceConnected() 实习方法中, 你将会得到 IBinder实例 (called service). 调用YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter to YourInterface type.
  • 调用你接口中的方法.( You should always trap DeadObjectException exceptions, which are thrown when the connection has broken; this will be the only exception thrown by remote methods.)
  • 断开连接, 调用Context.unbindService()断开连接

调用IPC服务的几点意见:

  • 对象是进程间的引用。(Objects are reference counted across processes.)
  • 可以发送匿名对象作为方法参数。

    想了解更多信息, 请阅读 Bound Services 文档.

这里是一些示例代码演示调用AIDL创建服务,从项目的apidemos远程服务的例子。

public static class Binding 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(Binding.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(Binding.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);
        }


1857
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(Binding.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);
            }
        }

    };
}

有的地方解读的不准确,会继续改进,勿喷~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章