Service与AIDL

来源:互联网 发布:php pos系统 编辑:程序博客网 时间:2024/05/17 21:24

Service介绍

Service是一个没有界面的系统组件,可以在后台进行一些长期的操作。这里的后台并不是指子线程,而是指在离开应用后仍然可以继续运行。Service一般由其他系统组件启动,可以用来实现IPC(Inter-Process Communication)。

Service可以分为三种:

  1. scheduled Service:即用来定时进行一些操作,可以通过JobScheduler(API 21)启动来实现。

  2. started Service:由系统组件比如Activity调用startService()来实现,启动后的Service可以脱离原组件的生命周期独立存在,无限运行。通常进行一些操作,操作完毕要记得进行停止操作。

  3. bound Service:由系统组件调用bindService()来实现,可以形成客户端-服务端的结构进行交互,甚至实现IPC。绑定的Service的生命周期长度就跟在原组件一样了。一个Service可以绑定多个系统组件,当所有组件都解绑的时候,Service也就destroyed了。

一个Service可以既是被启动的,同时也是被绑定的。不管Activity是启动还是绑定的,都是跟Activity一样通过intent来开启。

注意:Service默认运行在应用所在进程的主线程当中,既不是运行在单独进程中,也没有创建自己的线程。如果Service中要进行耗时操作,那就需要开启一个子线程避免ANR。

Service和线程的区别

Service是一个没有运行在前台界面的组件,即使用户离开本应用也可以继续运行。在这种需求下适合使用Service。

如果只需要在用户与本应用交互的时候独立于主线程进行操作,则是开启一个Thread。

Service和线程并不是同类概念,Service是一个系统组件,线程是一个程序执行的路径。Service默认运行在主线程当中。

Service的几个重要回调

  • onStartCommand():系统组件调用startService()后会回调该方法,然后Service就启动了,可以在后台长期运行。如果实现了这个方法,那就需要在操作完成的时候调用stopSelf()或者stopService()来停止掉。如果只想要对Service进行绑定,那就不需要实现该方法。
  • onBind():系统组件调用bindService()后会回调方法,因为是用来绑定跟客户端进行交互的,所以需要返回一个实现了IBinder接口的对象。如果不需要绑定就直接返回null即可。
  • onCreate():Service创建的时候回调该方法,在onStartCommand()和onBind()之前调用。
  • onDestroy():Service销毁之前调用的最后一个回调,可以用来释放资源销毁对象。

如果Service是通过startService()启动的,那就会一直运行到自己调用stopSelf()或者其他系统组件调用了stopService()为止。

如果Service是通过bindService()创建的,而且没有回调过onStartCommand(),Service就运行到解除绑定为止。

在内存不足的情况下系统可能会把一个Service强制停止掉,如果Service绑定到用户交互中的Activity上的话,被强制停止的概率就比较小了。如果这个Service是声明了在前台运行的话,就几乎不会被强制停止。如果Service是started的而且长期运行,那就很有可能会被强制停止,所以实现started的Service时,要对系统在内存不足时强制终止、在内存充足时重启Service的情况进行处理。可以在onStartCommand()中指定返回值来决定系统重启Service的方式。

Service的创建

1. 在Manifest中声明

<manifest ... >  ...  <application ... >      <service android:name=".ExampleService" />      ...  </application></manifest>

android:name属性用来表示这个Service的类名。android:exported=false用来设置其他应用无法启动这个Service。可以设置android:description属性来给Service添加描述,用户在查看运行中进程的时候可以看到这个描述。

为了保证app的安全性,最好不要使用隐式intent来启动Service(start或者bind),Service也不要设置IntentFilter。从API 21开始,如果使用隐式intent来调用bindService()会抛出异常。

2-1. 创建started Service

有两个类可以选择用来创建started Service:

  • Service:所有service的基类,需要自己创建子线程进行耗时操作。
  • IntentService:Service的子类,使用一个工作线程来处理所有的请求。如果不需要同时处理多个请求的话,使用这个类是最佳选择。在onHandleIntent()中处理请求进行耗时操作。

2-1-1. 继承IntentService类来创建

IntentService类帮我们做了这些事:

  • 创建一个工作线程来处理传递到onStartCommand()中的所有请求。
  • 创建一个工作队列,负责一次传递一个intent到onHandleIntent()当中。
  • IntentService会把所有接收到的intent处理完毕,然后自动停止,这样我们就不用调用stopSelf()了。
  • 实现onBind()方法返回空。
  • 实现onStartCommand()方法,把传给onStartCommand()的intent发送到工作队列中,然后再转发给onHandleIntent()。

具体实现:

public class HelloIntentService extends IntentService {  /**   * 需要一个无参构造函数,传入工作线程的名称   */  public HelloIntentService() {      super("HelloIntentService");  }  /**   * 该方法在子线程中调用   */  @Override  protected void onHandleIntent(Intent intent) {      // 睡眠5s模拟耗时操作      try {          Thread.sleep(5000);      } catch (InterruptedException e) {          // Restore interrupt status.          Thread.currentThread().interrupt();      }  }}

只要提供构造函数和重写onHandleIntent()方法就可以了,如果需要重写其他方法,记得要调用super方法保证IntentService正常工作,除了onBind()方法不用。


2-1-2. 继承Service类来创建

IntentService适用于单个子线程操作,如果需要进行多线程操作,可以继承Service来实现。

以下为继承Service类模拟IntentService的实现:

public class HelloService extends Service {  private Looper mServiceLooper;  private ServiceHandler mServiceHandler;  // 声明一个Handler来接收消息,传入指定Looper来指定handleMessage方法所在的线程  private final class ServiceHandler extends Handler {      public ServiceHandler(Looper looper) {          super(looper);      }      @Override      public void handleMessage(Message msg) {          // 模拟耗时操作          try {              Thread.sleep(5000);          } catch (InterruptedException e) {              // Restore interrupt status.              Thread.currentThread().interrupt();          }          // 耗时操作完成,使用startId停止Service,如果该请求startId与最新startId不符,就不会停止该服务。          stopSelf(msg.arg1);      }  }  @Override  public void onCreate() {    // 使用HandlerThread开启一个子线程,优先级设为后台级避免影响UI    HandlerThread thread = new HandlerThread("ServiceStartArguments",            Process.THREAD_PRIORITY_BACKGROUND);    thread.start();    // 使用子线程的Looper作为自定义Handler的Looper,这样我们的Handler就绑定到这个子线程上了    mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);  }  @Override  public int onStartCommand(Intent intent, int flags, int startId) {      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();      // 从消息池中获取一个消息来重用,把startId封装到消息当中方便停止服务            Message msg = mServiceHandler.obtainMessage();      msg.arg1 = startId;      mServiceHandler.sendMessage(msg);      // START_STICKY是Service被系统杀死后可以自动重启      return START_STICKY;  }  @Override  public IBinder onBind(Intent intent) {      // We don't provide binding, so return null      return null;  }  @Override  public void onDestroy() {    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();  }}

以上模拟了IntentService的单线程处理机制,如果要对于请求进行多线程处理,可以在onStartCommand()中开启多个子线程。

下面分析一下onStartCommand()的返回值,该返回值决定了Service被系统强制终止之后对Service的后续处理。可以有以下几种返回:

  • START_NOT_STICKY:如果系统在onStartCommand()返回之后杀死了这个Service,并不会重新创建这个Service,除非有一些未待处理的intent需要传递。所以Service只有在有未完成任务的时候才会重新创建。
  • START_STICKY:如果系统在onStartCommand()返回之后杀死了这个Service,会重新创建这个Service并调用onStartCommand(),但是并不会传递最后一个intent。除了有未处理intent存在的情况下会传递这些未处理intent,否则会使用null intent调用onStartCommand()。该标志适用于媒体播放器之类的场景,没有在执行任务但是长期运行等待任务。
  • START_REDELIVER_INTENT:如果系统在onStartCommand()返回之后杀死了这个Service,会重新创建这个Service并调用onStartCommand(),并且传入最后一个传给Service的intent。然后轮流传入待处理intent。该标志适用于正在进行一些需要马上恢复的任务,比如说下载软件。

2-1-3. 开启服务

Intent intent = new Intent(this, HelloService.class);startService(intent);

传入的intent会传递到onStartCommand()当中。如果Service还未创建,那就会先调用onCreate(),再调用onStartCommand()。

如果Service没有提供binding,那么startService()传递的intent就是调用方和Service唯一的沟通桥梁。如果想要让Service有办法回传消息,可以通过getBroadcast()获取一个广播,然后给这个广播设置PendingIntent,然后包裹在intent里调用startService(),这样Service就可以通过这个广播来传递结果。

每次调用startService()都会回调onStartCommand(),即使多次startService()也只要一次stopService()或者一次stopSelf()就可以停止Service。


2-1-4. 停止服务

一个started Service正常情况下需要调用stopSelfResult()或者其他组件调用stopService()才能停止。stopSelfResult(int startId)方法是判断传入的startId与Service最近收到的startId是否一致,如果一致才停止Service,不一致就不会停止,stopSelfResult()比stopSelf()多返回了是否停止成功的结果。

为了节省系统资源,必须要停止Service,即使Service提供绑定,如果有接收到onStartCommand()也需要停止该Service。

2-2. 创建一个bound服务

通过bindService()与Service建立一个连接。当一个Service提供绑定功能时,必须要提供一个实现IBinder接口的对象来让客户端与Service进行交互。有三种方法可以定义这个对象:

  • 继承Binder类:如果Service仅仅提供给本应用使用,也就是用来执行本应用 的后台任务时,就应该优先使用继承Binder类的方式。

  • 使用Messenger:如果需要跨进程,那可以使用Messenger。这样Service就会创建一个对应多种Message对象的Handler,这个Handler是Messenger的基础,可以用来跟客户端分享IBinder对象,这样客户端就可以通过发送Message对象给Service进行交互。客户端也可以自己定义一个Messenger,这样Service也可以通过这个新的Messenger来发送消息给客户端。

  • 使用AIDL。Android Interface Definition Language把对象分解成操作系统可以明白的基本数据类型,然后跨进程传送实现IPC。Messenger也是基于AIDL的。Messenger的实现是多个客户端请求排到同一队列当中,Service一次性处理一条请求。如果想要一次处理多条的话,那就需要直接通过AIDL来实现,注意此时Service就要处理多线程的问题保证线程安全。

注意只有Activity、Service、ContentProvider可以绑定服务,BroadcastReceiver无法绑定服务。


2-2-1. 继承Binder类

Binder类适用于同应用同进程的情况,比如音乐播放器中Activity绑定Service。同应用的话就可以让客户端对接收到的IBinder类进行转型调用其方法。要求同进程的原因是Binder没有进行跨进程处理。

实现步骤:

  • 创建Binder实例来提供客户端可以调用的public方法,public方法可以声明在Binder中,或者声明在当前Service中,又或者声明在当前Service包含的其他对象中。如果是后两者,则Binder实例要有办法获取到当前Service实例才行。
  • onBind()中返回定义的Binder实例。
  • 客户端在onServiceConnected()中接收Binder对象,调用提供的方法。

示例如下:
Service:

public class LocalService extends Service {    // Binder given to clients    private final IBinder mBinder = new LocalBinder();    // Random number generator    private final Random mGenerator = new Random();    /** 自定义Binder */    public class LocalBinder extends Binder {        LocalService getService() {            // 返回Service对象供客户端调用方法            return LocalService.this;        }    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    /** 提供给客户端的方法 */    public int getRandomNumber() {      return mGenerator.nextInt(100);    }}

客户端调用:

public class BindingActivity extends Activity {    LocalService mService;    boolean mBound = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);    }    @Override    protected void onStart() {        super.onStart();        // 绑定服务        Intent intent = new Intent(this, LocalService.class);        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        // 解绑服务        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }    public void onButtonClick(View v) {        if (mBound) {            // 调用绑定服务中的方法            int num = mService.getRandomNumber();            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();        }    }    /** 定义绑定回调 */    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,                IBinder service) {            // 绑定成功,通过Binder获取内容            LocalBinder binder = (LocalBinder) service;            mService = binder.getService();            mBound = true;        }        @Override        public void onServiceDisconnected(ComponentName arg0) {            mBound = false;        }    };}

注意以上绑定回调的onServiceDisconnected(),只有当连接意外断开的时候才会回调这个方法,比如说Service出错了。如果客户端主动解除绑定,是不会回调这个方法的。

bindService()返回是否成功绑定,即使绑定未成功,也需要调用unbindService(),否则Service空闲时会无法正常关闭。

bindService()的第三个参数一般为BIND_AUTO_CREATE,即Service未启动的话会先启动这个Service。如果为0就是不操作。

绑定服务的几个注意点:

  • 连接断开的时候会抛出DeadObjectException,应该要捕获这个异常。
  • 对象是通过方法参数发送的(Objects are reference counted across processes。
  • 应该根据客户端的生命周期来绑定和解绑服务。如果只需要在Activity可见时与Service进行交互,应该在onStart()中绑定,onStop()中解绑。
  • 如果要Activity不可见的时候还可以与Service进行交互,可以在onCreate()中绑定,onDestroy()中解绑。Beware that this implies that your activity needs to use the service the entire time it’s running (even in the background), so if the service is in another process, then you increase the weight of the process and it becomes more likely that the system will kill it.
  • 通常不要在onResume()和onPause()中绑定解绑,因为操作太频繁了。
  • 如果多个Activity绑定了同一个Service,可能会这个Activity解绑了,下个Activity还没绑定,这时候Service会销毁再重建。

2-2-2. 使用Messenger

使用Messenger实现跨进程单线程交互。
实现步骤:

  • Service实现一个Handler用来接收客户端的请求。
  • Service使用Handler来创建一个Messenger对象,Messenger对象实际上就是对Handler对象的引用。
  • 在onBinde()中返回Messenger对象封装的IBinder对象给客户端。
  • 客户端使用IBinder对象来实例化Messenger对象,实例化出来的Messenger对象指向了Service的Handler,这样客户端就可以发送消息给Service了。
  • Service在Handler的handleMessage()中接收到客户端发送的消息。

Messenger这种方式下,Service和客户端以Messenger为桥梁,发送Message对象进行交互。

Messenger使用示例如下:
Service端:

public class MessengerService extends Service {    /** Command to the service to display a message */    static final int MSG_SAY_HELLO = 1;    /**     * 创建一个接收客户端消息的Handler     */    class IncomingHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_SAY_HELLO:                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();                    break;                default:                    super.handleMessage(msg);            }        }    }    /**     * 使用Handler构建Messenger     */    final Messenger mMessenger = new Messenger(new IncomingHandler());    /**     * 给绑定的客户端发送Messenger内部封装的IBinder对象     */    @Override    public IBinder onBind(Intent intent) {        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();        return mMessenger.getBinder();    }}

客户端:
客户端的工作就是把接收到的IBinder还原为Messenger,然后就可以通过Messenger来发送信息了。

public class ActivityMessenger extends Activity {    /** 声明与Service交互的Messenger */    Messenger mService = null;    /** 绑定标志 */    boolean mBound;    /**     * 绑定回调类     */    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            // 从IBinder对象获取Messenger            mService = new Messenger(service);            mBound = true;        }        public void onServiceDisconnected(ComponentName className) {            // 连接断开            mService = null;            mBound = false;        }    };    public void sayHello(View v) {        if (!mBound) return;        // 发送一个消息        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);        try {            mService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);    }    @Override    protected void onStart() {        super.onStart();        // 绑定服务        bindService(new Intent(this, MessengerService.class), mConnection,            Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        // 解绑服务        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }}

如果要实现Service发送消息给客户端,可以在客户端中发送消息的时候给Message.replyTo字段传入一个Messenger,Service接收到这个Messenger就可以给客户端发送消息了。


2-2-2. 使用AIDL

见AIDL

2-3. 在前台运行Service

前台服务是一种用户清楚知道的服务,所以低内存时并不会被系统杀死掉。要成为前台服务必须提供一个notification,这个notification只有在服务停止或者转为非前台服务的时候才能移除掉。

具体实现,注意notification的id不能为0:

Intent notificationIntent = new Intent(this, ExampleActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);Notification notification = new Notification.Builder(this)    .setContentTitle(getText(R.string.notification_title))    .setContentText(getText(R.string.notification_message))    .setSmallIcon(R.drawable.icon)    .setContentIntent(pendingIntent)    .setTicker(getText(R.string.ticker_text))    .build();startForeground(ONGOING_NOTIFICATION_ID, notification);

要把服务从前台移除,可以调用stopForeground()。

2-4. Service的生命周期

Service的生命周期可以分情况讨论:

  • started service:系统组件调用startService()时创建,然后长期运行直到自身调用stopSelf()或者系统组件调用stopService()时停止。
  • bound service:其他组件(客户端)调用bindService()创建,客户端和Service通过IBinder接口进行通讯,客户端通过调用unbindService()来断开连接。一个Service可以绑定多个客户端,当所有客户端解绑的时候,系统会自动终止掉这个Service。

值得注意的是,以上两种情况并不是完全互斥的,可以调用bindService()来绑定一个以startService()形式启动的Service。这种情况下Service既是started也是bound的。startService()使这个Service可以长期运行,bindService()让其他客户端绑定到了这个Service上。此时所有客户端解绑的话Service并不会马上终止,而是需要调用stopService()或者stopSelf()才会停止这个Service。

Service的生命周期回调,与Activity不同的是,Service的回调无需调用父类实现:

public class ExampleService extends Service {    int mStartMode;       // 指定Service被杀掉后的操作模式    IBinder mBinder;      // 给客户端提供的IBinder接口对象    boolean mAllowRebind; // 是否回调rebind()    @Override    public void onCreate() {        // service正在被创建    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        // 调用startService()会回调这个方法        return mStartMode;    }    @Override    public IBinder onBind(Intent intent) {        // 一个客户端调用bindService()来绑定这个service        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        // 所有的客户端都解绑了,返回值决定之后如果有客户端再绑定是否要回调onRebind()。        return mAllowRebind;    }    @Override    public void onRebind(Intent intent) {        // 在onUnbind()调用之后,一个客户端又绑定了这个service    }    @Override    public void onDestroy() {        // service正在被销毁    }}

started service和bound service的生命周期如图:
这里写图片描述

对于既是started service也是bound service的生命周期如下:
这里写图片描述

service的完整生命周期是从onCreate()开始,到onDestroy()结束,在onCreate()中进行各项初始化,onDestroy()中释放所有资源。

service的活动生命周期是从onStartCommand()或者onBind()开始。如果一个service是started的,那活动生命周期与完整生命周期一同结束;如果一个service是bound的,那活动生命周期结束于onUnbind()方法返回的时候。

AIDL

AIDL(Application Interface Definition Language)与其他IDL类似,都可以通过定义一个接口,让客户端和服务端通过这个接口进行交互。在Android当中,一个进程无法获取另一个进程的内存,所以需要把对象分解成操作系统可以处理的基本对象,并跨进程传递。跨进程传递的代码写起来比较麻烦,所以Android提供AIDL帮我们自动生成。

AIDL适用于不同进程间而且需要多进程处理时的交互。如果不需要跨进程就使用Binder就好;如果需要跨进程但是不需要多线程处理的话,使用Messenger就够了。

注意,调用AIDL接口就是直接的方法调用,调用方法所在线程会根据调用发生在本地进程或者远程进程而改变。具体来说,从服务端的角度来看:

  • 本地进程发起的调用会在本地进程所在线程中执行。所以如果只有本地进程使用这个服务,那我们是可以完全控制方法执行所在线程的,但是这样就没必要使用AIDL了,使用Binder就可以了。
  • 远程进程发起的调用会分配到本地进程所维护的一个线程池当中,这样方法就不一定执行在哪个线程当中,所以AIDL必须要兼顾线程安全的问题。
  • oneway这个关键字可以修改远程调用的行为,如果使用了这个关键字,远程调用并不会阻塞住,只会发送数据然后马上返回。接口的实现会把这个当做普通的来自Binder线程池的调用,作为一个正常的远程调用。如果oneway与本地调用一起使用就不会起作用,本地调用仍是同步的。

定义AIDL接口的步骤

首先需要使用Java语法定义一个.aidl文件,保存到服务端和客户端的目录下,这样SDK会自动帮我们生成一个基于所定义.aidl的IBinder接口,这样客户端就可以通过这个IBinder接口来绑定到服务端并调用方法。


1. 创建.aidl文件

.aidl文件里面是一个接口,里面只能定义客户端可以调用的方法,无法定义静态字段。AIDL默认支持以下的数据类型:

  • Java的基础数据类型
  • String
  • CharSequence
  • List:List内的元素必须是AIDL默认支持的数据类型,或者自定义的实现Parcelable接口的类。List可以声明泛型种类,比如List<String>。尽管方法声明的是List类型,但是接收方接收到的总是一个ArrayList。
  • Map:Map内的元素也必须是AIDL默认支持的数据类型,或者自定义的实现Parcelable接口的类。与List不同的是,Map不能声明泛型参数,比如Map<String, Integer>是不支持的。尽管方法声明的是Map类型,但是接收方接收到的总是一个HashMap。
  • 其他AIDL生成的接口

如果要使用非默认的数据类型,该数据类型需要实现Parcelable接口,然后在.aidl文件这边要声明一个同名的AIDL文件,里面声明这个类为Parcelable,并且.aidl文件中必须要显式import这个类,就算在同一包中也是一样。

定义方法可以设置0个或多个参数,返回值可以为void或其他类型。如果方法参数是非基本数据类型的,参数前面还需要声明这个参数的去向,in、out或者inout。基本数据类型默认都是in的,无法是其他情况。注意参数去向要准确声明,以减少系统开销。

.aidl文件中的comment,除了import和package语句前的comment,都会包含到生成的IBinder接口当中。

以下为一个.aidl文件的示例:

// IRemoteService.aidlpackage com.example.android;// 在这里导入非默认数据类型/** 示例service interface */interface IRemoteService {    /** 定义一个获取进程id的方法 */    int getPid();    /** 举例说明方法参数和返回值可以用的一些类型     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);}

把这个.aidl文件保存到src/目录下,build工程之后,SDK会自动生成IBinder对象文件到gen/目录下,生成的接口名与.aidl名一致,只是后缀为.java。


2. 实现接口

生成的接口中包含一个内部类叫Stub,这个Stub也是所生成接口的子类,是所生成接口的具体实现。

同时Stub还定义了一些帮助方法,比如说asInterface(),这个方法把一个IBinder对象转成了Stub接口的实例,通常用在客户端onServiceConnected()中接收到的IBinder对象上。

要实现.aidl生成的接口的话,继承生成的Binder接口,比如说所生成接口内的Stub对象,然后实现方法。

比如说有个生成接口叫IRemoteService,我们就可以写一个继承内部Stub类的匿名内部类:

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

这样子mBinder就是Stub的一个实例了,就是一个Binder,就定义了这个Service的RPC(Remote Procedure Call 远程过程调用)接口,然后我们把这个接口提供给客户端,客户端就可以和服务端进行交互了。

实现AIDL接口有几个注意点:

  • 接收的调用并不一定会在主线程执行,所以必须要处理多线程的问题,保证线程安全。
  • RPC默认是同步的,所以Service如果需要耗时返回处理结果,客户端就不应该在主线程中进行调用,通常需要从子线程中进行调用。
  • 抛出的异常并不会发送给调用方。

3. 把接口提供给客户端

创建一个Service,在onBind()中返回接口实例。

public class RemoteService extends Service {    @Override    public void onCreate() {        super.onCreate();    }    @Override    public IBinder onBind(Intent intent) {        // 返回接口        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) {        }    };}

客户端和服务端是不同应用时,客户端为了要获取到这个接口类,必须拷贝一份.aidl文件到src/目录下,然后就可以进行转换:

IRemoteService mIRemoteService;private ServiceConnection mConnection = new ServiceConnection() {    public void onServiceConnected(ComponentName className, IBinder service) {        // 把IBinder对象转换为自定义的IRemoteInterface        mIRemoteService = IRemoteService.Stub.asInterface(service);    }    public void onServiceDisconnected(ComponentName className) {        Log.e(TAG, "Service has unexpectedly disconnected");        mIRemoteService = null;    }};

连接损坏的时候会抛出DeadObjectException,应该要捕获这个异常。另外一个需要捕获的异常是SecurityException,当IPC方法调用中两个进程的AIDL定义相冲突时会抛出这个异常。

使用Parcelable通过IPC传递数据

所要传递的数据如果是自定义对象,则必须要实现Parcelable接口,这样才能分解成基本数据类型跨进程传递。
实现Parcelable接口的步骤:

  • 实现Parcelable接口,重写writeToParce(),把对象写到Parcel当中。
  • 添加一个静态对象CREATOR,该对象是Parcelable.Creator接口的实现类。
  • 最后还要创建一个.aidl文件,把这个自定义类声明为Parcelable,这样AIDL才能找到这个类,并且知道这个类实现了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 = newParcelable.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, int flags) {        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();    }    public int describeContents() {        return 0;    }}

为了安全性可以对于从Parcel中读取的数据进行判断处理。

以下为一个AIDL示例代码:

public static class Binding extends Activity {    /** 两个要调用的服务 */    IRemoteService mService = null;    ISecondary mSecondaryService = null;    Button mKillButton;    TextView mCallbackText;    private boolean mIsBound;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.remote_service_binding);        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.");    }    /**     * 连接回调     */    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className,                IBinder service) {            // 获取AIDL接口            mService = IRemoteService.Stub.asInterface(service);            mKillButton.setEnabled(true);            mCallbackText.setText("Attached.");            //             try {                mService.registerCallback(mCallback);            } catch (RemoteException e) {                // Service意外退出,如果可以等待重连,不用进行什么操作            }            Toast.makeText(Binding.this, R.string.remote_service_connected,                    Toast.LENGTH_SHORT).show();        }        public void onServiceDisconnected(ComponentName className) {            // 连接意外断开,服务端进程崩溃了            mService = null;            mKillButton.setEnabled(false);            mCallbackText.setText("Disconnected.");            Toast.makeText(Binding.this, R.string.remote_service_disconnected,                    Toast.LENGTH_SHORT).show();        }    };    /**     * 与第二个服务进行交互     */    private ServiceConnection mSecondaryConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className,                IBinder service) {            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.            Intent intent = new Intent(Binding.this, RemoteService.class);            intent.setAction(IRemoteService.class.getName());            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);            intent.setAction(ISecondary.class.getName());            bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);            mIsBound = true;            mCallbackText.setText("Binding.");        }    };    private OnClickListener mUnbindListener = new OnClickListener() {        public void onClick(View v) {            if (mIsBound) {                // 绑定的情况下解绑                if (mService != null) {                    try {                        mService.unregisterCallback(mCallback);                    } catch (RemoteException e) {                        // Service崩溃的情况下也没什么操作                    }                }                // 解绑服务                unbindService(mConnection);                unbindService(mSecondaryConnection);                mKillButton.setEnabled(false);                mIsBound = false;                mCallbackText.setText("Unbinding.");            }        }    };    private OnClickListener mKillListener = new OnClickListener() {        public void onClick(View v) {            // 如果要终止服务端所在进程,就需要知道其进程id,定义的服务中提供了获取进程id的方法            if (mSecondaryService != null) {                try {                    int pid = mSecondaryService.getPid();                    // 尽管Process.killProcess()可以让我们根据pid请求杀死某个进程,系统内核仍然限制了我们可以杀死的进程,一般来说就是我们应用所在的进程,或者本应用创建的其他进程。享有同一个UID的进程也可以互相杀死                    Process.killProcess(pid);                    mCallbackText.setText("Killed service process.");                } catch (RemoteException ex) {                    // 此处进行从服务端进程死亡的情况中恢复                    Toast.makeText(Binding.this,                            R.string.remote_call_failed,                            Toast.LENGTH_SHORT).show();                }            }        }    };    // ----------------------------------------------------------------------    // 处理回调的代码    // ----------------------------------------------------------------------    /**     * 从远程服务接收回调     */    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {        /**         * 远程服务调用来通知最新值,IPC调用是通过一个运行在每个进程中的线程池来分配的,所以此处执行的代码不会运行在主线程中,就需要Handler来处理         */        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);            }        }    };}