AIDL 使用方法介绍

在Android中,一个进程通常无法访问另一个进程的内存,那么为了实现进程间通信,需要将信息转换为系统能够理解的原语,再通过系统来实现跨进程访问,在Android中这便是AIDL(Android Interface Definition Language)。

1. 定义一个AIDL接口

第一步,需要使用Java语言在src目录下创建一个.aidl文件,这个文件应该同时存在在服务器端和客户端。之后SDK工具会根据你的.aidl文件在gen目录下生成一个IBinder接口,接下来Service实现这些接口,并将这个接口返回给客户端,这是客户端程序就可以调用这个接口的方法,实现跨进程通信。

(1)创建.aidl文件

(2)实现接口

SDK工具根据你的.aidl文件生成了一个interface,这个接口有一个名为Stub的内部类,这个内部类继承了Binder类并实现了你定义在.aidl文件中的接口方法,你必须继承Stub类并实现这些方法

(3)将接口返回给客户端

实现一个Service接口并重写onBind()方法将你实现好的Stub类返回


1.1 创建.aidl文件

AIDL使用简单的语法让你可以定义一个或多个有参数和返回值的方法。参数和返回值可以为任意类型。
必须使用Java编程语言来构造.aidl文件。每一个.aidl文件必须声明一个接口,并且只需要方法名和参数,不需要方法体。
默认情况下,AIDL支持以下几种数据类型:
  • 所有基本数据类型(比如int long char boolean等等)
  • String
  • CharSequence
  • List
  • Map
所有以上为列出的类型都必须加上import,即使所使用的类在同一个包。
当定义service接口的时候,需要知道:
  • 方法可以有0个或多个参数,返回一个值或者void
  • 接口中所有的注释都会被生成到Java文件中
  • 在AIDL中只支持方法,无法输出静态字段
下面是一个.aidl文件的例子
// 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工具会自动在gen目录下生成一个IBinder接口的.java文件。IRemoteService.aidl文件对应生成的Java接口文件名为IRemoteService.java。

1.2 实现接口

根据.aidl文件生成的接口中有一个名为Stub的子类,这个类中包含.aidl文件中声明的所有方法。
下面是一个实现接口的例子:
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的一个实例(IBinder),下一步,我们要将mBinder返回给客户端,这样客户端就可以调用相应接口中国的方法。

1.3 将接口返回给客户端

当你实现好接口方法之后,你需要将它返回给客户端,这样客户端才能绑定你的Service。要返回这个接口,你要做的就是继承Service类并实现onBind()方法,在onBind()方法中放回Stub的实例。下面是一个Service的例子
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
        }
    };
}
现在,当客户端执行bindService()之后会从onBind()方法中得到mBinder实例。
如果客户端和服务端不在同一个应用中,那么客户端的src目录中也必须有与服务端一样的.aidl文件。
当客户端在onServiceConnected()方法中获得到IBinder之后,可以通过YourServiceInterface.Stub.asInterface(service)方法将它转换为YourServiceInterface 类型。比如:
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;
    }
};

2. 跨进程传递对象

你可以用AIDL跨进程传递你自己的对象。但是你要保证通信那边能够识别你的类并且你的类必须实现Parcelable接口。
要创建一个实现Parcelable接口的类,你需要这样做:
  1. 创建一个类实现Parcelable接口
  2. 实现writeToParcel方法,这个方法会将当前对象的状态信息写入到Parcel对象中
  3. 创建一个名为CREATOR的静态字段,这个字段实现Parcelable.Creator接口
  4. 最后,创建一个.aidl文件,在文件中定义你的parcelable类。
下面是一个Rect.aidl文件,这个文件创建了一个Parcelable的Rect类:
package android.graphics;

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

下面是实现了Parcelable的Rect类:
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();
    }
}

2.1 调用其他进程的方法

下面是调用其他AIDL中方法的步骤:
  1. 将.aidl文件放入到项目的src目录下
  2. 定义一个IBinder接口的实例(基于AIDL)
  3. 实现ServiceConnection
  4. 调用Context.bindService()方法,将刚刚实现的ServiceConnection当做参数传入
  5. 在ServiceConnection方法的onServiceConnected回调方法中,你会得到一个名为service的IBinder实例,调用YourInterfaceName.Stub.asInterface((IBinder)service)方法将返回的IBinder转换为你需要的接口
  6. 调用你定义在接口中的方法。你需要时刻小心DeadObjectException异常,这个异常会在连接损坏时抛出,这也是远程方法唯一会抛出的异常
  7. 如果想要断开连接,调用Context.unbindService()方法
下面是一些示例代码:

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

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

    };
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值