Android Interface Definition Language (AIDL)

来源:互联网 发布:cssci数据库怎么查询 编辑:程序博客网 时间:2024/05/17 05:08

Android Interface Definition Language (AIDL)

Android 上跨进程通信(IPC)使用 AIDL。
Note:别的应用使用你的service时,并想多线程访问你的servcie时,就可以使用 AIDL 了。
 
AIDL接口的函数调用是直接的,你不能假设这调用的发生在那个线程上。这不同,取决于调用是否在本地进程或远程进程。
  • 如果执行接口函数来自本地的进程,那么它就和调用它的线程在同一个线程。例如,你的main UI Thread 调用了AIDL接口,
          那么main UI thread会 继续执行接口的调用。同理,如果时别的线程,那这个线程就是你执行service代码的线程。这样,本地线程
          访问你的servcie,你完全可以控制那个线程执行它。(那么直接实现Binder就可以了,不需要用到AIDL)
  •  调用来自远程进程,哪个线程调用是不确定的,有可能多个同时调用,所以确保AIDL接口是线程安全的。
  • oneway 关键字可以修改远程调用的行为,但使用时远程调用不会阻塞,只是简单的转移数据和立刻返回。
          接口的实现最终会从Binder线程池中的常规调用接受这数据,就像普通的远程调用一样。
          如果oneway用在个本地调用,这没什么影响,调用还是同步的。
 

Defining an AIDL Interface


Caution:对于任何的AIDL接口的改动,必须向后兼容。为了避免别的应用使用你的服务导致问题。这是因为,
.aidl 文件必须复制到别的应用那里,才能使用的你服务接口。
 
步骤:

1. Create the .aidl file

默认的,AIDL支持以下数据类型:
* java的所有基本类型 (如 int, long, char, boolean and so on)
* String
* CharSequence
* List
* Map
如果你要引用额外的类型,你必须要import语句导入,即使它和你的AIDL借口在同一个包下。
 
be aware that:
* 方法可带或不带参数,and return a value or void。
* 所有的不是基本类型的参数,都要一个方向标签来表示数据传递的方向,  in, out, or inout。
    但基本类型默认的是 in,不能是别的。
    Caution:你要限制方向的使用,除非 truly needed,因为 重新整理数据是很昂贵的。
* 在.aidl文件的所有的代码注释都会包括到生成的IBinder 接口中,(除了在import 和包名语句之前的注释)
* aidl只支持方法,不能有静态属性。
 
example:
// IRemoteService.aidl
package com.example.android;

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

/** Example service interface */
interfaceIRemoteService{
   
/** 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);
}
 
把它放在 src/ 目录下,eclipse的adt插件,在gen/目录下,会自动生成接口文件 IRemoteService.java。
 

2. Implement the interface

privatefinalIRemoteService.Stub mBinder= new IRemoteService.Stub(){
   
publicint getPid(){
       
returnProcess.myPid();
   
}
   
publicvoid basicTypes(int anInt,long aLong,boolean aBoolean,
       
float aFloat,double aDouble,String aString){
       
// Does nothing
   
}
};
mBinder是Stub class(a Binder)的一个实例,它为service定义了RPC接口
 
实现AIDL接口需要注意的一些地方:
* 调用的执行不一定来之main thread,所以你必须考虑你的service的线程安全。
* 默认的,RPC调用时同步的。如果你知道你的service要花费一些时间来完成请求,那你不应该在activity 的main thread去调用它。否则会ANR。
* 你调用时发生的exceptions不会发送给caller。
 

3. Expose the interface to clients

一旦你为service实现了接口,你需要把它暴露给clients去绑定它。
publicclassRemoteServiceextendsService{
   
@Override
   
publicvoid onCreate(){
       
super.onCreate();
   
}

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

   
privatefinalIRemoteService.Stub mBinder= new IRemoteService.Stub(){
       
publicint getPid(){
           
returnProcess.myPid();
       
}
       
publicvoid basicTypes(int anInt,long aLong,boolean aBoolean,
           
float aFloat,double aDouble,String aString){
           
// Does nothing
       
}
   
};
}
 
IRemoteService mIRemoteService;
privateServiceConnection mConnection= new ServiceConnection(){
   
// Called when the connection with the service is established
   
publicvoid 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
   
publicvoid onServiceDisconnected(ComponentName className){
       
Log.e(TAG,"Service has unexpectedly disconnected");
        mIRemoteService
=null;
   
}
};

Passing Objects over IPC

To create a class that supports the Parcelable protocol, you must do the following:
1. 使你的类实现 Parcelable 接口。
2. 实现 writeToParcel 方法,这能掌握对象的当前状态,并把它写到Parcle中。
3. 为你的class增加一个静态属性 CREATOR,他实现了 Parcelable.Creator 接口。
4. 最后,创建一个.aidl file 来定义你的 parcelable class。
 
for example:
Rect.aidl
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable
Rect;
 
Rect class
import android.os.Parcel;
import android.os.Parcelable;

publicfinalclassRectimplementsParcelable{
   
publicint left;
   
publicint top;
   
publicint right;
   
publicint bottom;

   
publicstaticfinalParcelable.Creator<Rect> CREATOR =new
Parcelable.Creator<Rect>(){
       
publicRect createFromParcel(Parcelin){
           
returnnew Rect(in);
       
}

       
publicRect[] newArray(int size){
           
returnnew Rect[size];
       
}
   
};

   
publicRect(){
   
}

   
privateRect(Parcelin){
        readFromParcel
(in);
   
}

   
publicvoid writeToParcel(Parcelout){
       
out.writeInt(left);
       
out.writeInt(top);
       
out.writeInt(right);
       
out.writeInt(bottom);
   
}

   
publicvoid readFromParcel(Parcelin){
        left
=in.readInt();
        top
=in.readInt();
        right
=in.readInt();
        bottom
=in.readInt();
   
}
}

Calling an IPC Method


步骤:
1. 在 src/ 目录下添加 .aidl file。
2.定义IBinder接口的实例(通过AIDL生成)
3.实现 ServcieConnection。
4.调用 Context.bindService(),传如 ServiceConnection 的实现。
5.在回调 onServiceConnected() 里,会接收个 IBinder 对象,Call YourInterfaceName.Stub.asInterface((IBinder)service)
来转换成你的接口类型。
6.调用你在接口定义的方法,trap DeadObjectException.
7.call Context.unbindServcie() 取消servcie的连接。
 
publicstaticclassBindingextendsActivity{
   
/** 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;

   
privateboolean mIsBound;

   
/**
     * Standard initialization of this activity.  Set up the UI, then wait
     * for the user to poke it before doing anything.
     */

   
@Override
   
protectedvoid 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.
     */

   
privateServiceConnection mConnection= new ServiceConnection(){
       
publicvoid 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();
       
}

       
publicvoid 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.
     */

   
privateServiceConnection mSecondaryConnection= new ServiceConnection(){
       
publicvoid 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);
       
}

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

   
privateOnClickListener mBindListener= new OnClickListener(){
       
publicvoid 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
(newIntent(IRemoteService.class.getName()),
                    mConnection
,Context.BIND_AUTO_CREATE);
            bindService
(newIntent(ISecondary.class.getName()),
                    mSecondaryConnection
,Context.BIND_AUTO_CREATE);
            mIsBound
=true;
            mCallbackText
.setText("Binding.");
       
}
   
};

   
privateOnClickListener mUnbindListener= new OnClickListener(){
       
publicvoid 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.");
           
}
       
}
   
};

   
privateOnClickListener mKillListener= new OnClickListener(){
       
publicvoid 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.
     */

   
privateIRemoteServiceCallback 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.
         */

       
publicvoid valueChanged(int value){
            mHandler
.sendMessage(mHandler.obtainMessage(BUMP_MSG, value,0));
       
}
   
};

   
privatestaticfinalint BUMP_MSG= 1;

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

   
};
}
 
原创粉丝点击