Android四大组件之Service

时间:2023-12-31 19:36:50

Android四大组件之Service

Android支持服务的概念,服务是在后台运行的组件,没有用户界面,Android服务可用有与活动独立的生命周期。Android支持两种类型的服务:

本地服务:

本地服务只能由承载该服务的应用程序访问,无法供在设备上运行的其他应用程序访问。客户端调用Context.startService()启动该服务。

远程服务:

远程服务除了可从承载服务的应用程序访问,还可以从其他应用程序访问。远程服务使用AIDL向客户端定义。服务支持onBind()方法,客户端通过Context.bindService()进行调用。

1)本地服务

1.1、startService

本地服务可由Context.startService()启动,启动后这些服务将持续运行,直到客户端调用Context.stopService()或服务自己调用stopSelf()。

注意:如果调用Context.startService()时还未创建服务,系统将实例化服务并调用服务的onStartCommand()方法。如果在调用Context.startService()时服务已经启动,那么不会再创建一个实例,而是重新调用正在运行的服务的onStartCommand()方法。

Demo:我们在MainActivity中新建两个两个Button,一个用于启动服务,另外一个用于停止服务。建立一个MyService类继承于Service,当收到服务的时候在通知栏弹出通知,一直到我们的服务退出才清除通知,同时收到启动服务的消息时我们建立一个线程sleep 10秒。当我们退出MainActivity的时候停止服务,同时清除通知栏的通知。

MainActivity.xml:就两个Button用于启动和停止服务。

  1. <SPAN style="FONT-SIZE: 14px"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:paddingBottom="@dimen/activity_vertical_margin"
  6. android:paddingLeft="@dimen/activity_horizontal_margin"
  7. android:paddingRight="@dimen/activity_horizontal_margin"
  8. android:paddingTop="@dimen/activity_vertical_margin"
  9. tools:context=".MainActivity" >
  10. <Button
  11. android:id="@+id/btnStart"
  12. android:layout_width="wrap_content"
  13. android:layout_height="wrap_content"
  14. android:text="startService" >
  15. </Button>
  16. <Button
  17. android:id="@+id/btnStop"
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. android:text="stopService"
  21. android:layout_below="@id/btnStart">
  22. </Button>
  23. </RelativeLayout></SPAN>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > <Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="startService" >
</Button> <Button
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stopService"
android:layout_below="@id/btnStart">
</Button> </RelativeLayout>

然后是MainActivity,用于响应Button的单击事件:

  1. <SPAN style="FONT-SIZE: 14px">public class MainActivity extends Activity implements OnClickListener{
  2. private static final String TAG = "MainActivity";
  3. private int counter = 1;
  4. private Button btnStart, btnStop;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. btnStart = (Button)this.findViewById(R.id.btnStart);
  10. btnStop = (Button)this.findViewById(R.id.btnStop);
  11. btnStart.setOnClickListener(this);
  12. btnStop.setOnClickListener(this);
  13. }
  14. @Override
  15. public void onClick(View v) {
  16. // TODO Auto-generated method stub
  17. Log.v(TAG, "id:"+v.getId() + "btn:"+R.id.btnStart);
  18. switch (v.getId()) {
  19. case R.id.btnStart:
  20. Log.v(TAG, "Starting Service...counter=" + counter);
  21. Intent intent = new Intent(MainActivity.this, MyService.class);
  22. intent.putExtra("counter", counter);
  23. startService(intent);
  24. break;
  25. case R.id.btnStop:
  26. Log.v(TAG, "Stopping Service...");
  27. if( stopService(new Intent(MainActivity.this, MyService.class)) ) {
  28. Log.v(TAG, "stopService successful");
  29. } else {
  30. Log.v(TAG, "stopService failed");
  31. }
  32. break;
  33. default:
  34. break;
  35. }
  36. }
  37. @Override
  38. protected void onDestroy() {
  39. // TODO Auto-generated method stub
  40. stopService(new Intent(MainActivity.this, MyService.class));
  41. super.onDestroy();
  42. }
  43. }</SPAN>
public class MainActivity extends Activity implements OnClickListener{

	private static final String TAG = "MainActivity";
private int counter = 1;
private Button btnStart, btnStop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStart = (Button)this.findViewById(R.id.btnStart);
btnStop = (Button)this.findViewById(R.id.btnStop);
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.v(TAG, "id:"+v.getId() + "btn:"+R.id.btnStart);
switch (v.getId()) {
case R.id.btnStart:
Log.v(TAG, "Starting Service...counter=" + counter);
Intent intent = new Intent(MainActivity.this, MyService.class);
intent.putExtra("counter", counter);
startService(intent);
break; case R.id.btnStop:
Log.v(TAG, "Stopping Service...");
if( stopService(new Intent(MainActivity.this, MyService.class)) ) {
Log.v(TAG, "stopService successful");
} else {
Log.v(TAG, "stopService failed");
}
break; default:
break;
}
} @Override
protected void onDestroy() {
// TODO Auto-generated method stub
stopService(new Intent(MainActivity.this, MyService.class));
super.onDestroy();
}
}

最后是我们的MyService,当收到服务的时候,在通知栏弹出通知,并启动一个sleep 10秒的线程。

  1. <SPAN style="FONT-SIZE: 14px">public class MyService extends Service {
  2. private static final String TAG = "MyService";
  3. private NotificationManager notificationMgr;
  4. private ThreadGroup threadGroup = new ThreadGroup("ServiceWorkder");
  5. @Override
  6. public void onCreate() {
  7. // TODO Auto-generated method stub
  8. super.onCreate();
  9. Log.v(TAG, "in onCreate");
  10. notificationMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
  11. Notification notification = new Notification(R.drawable.ic_launcher, "Service is running", System.currentTimeMillis());
  12. notification.flags = Notification.FLAG_NO_CLEAR;
  13. PendingIntent intent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
  14. notification.setLatestEventInfo(this, TAG, "Service is running", intent);
  15. notificationMgr.notify(0, notification);
  16. }
  17. @Override
  18. public int onStartCommand(Intent intent, int flags, int startId) {
  19. // TODO Auto-generated method stub
  20. super.onStartCommand(intent, flags, startId);
  21. int counter = intent.getExtras().getInt("counter");
  22. Log.v(TAG, "in onStartCommand, counter = "+counter+",startId = "+startId);
  23. new Thread(threadGroup, new ServiceWorker(counter)).start();
  24. return START_STICKY;
  25. }
  26. class ServiceWorker implements Runnable {
  27. private int counter = -1;
  28. public ServiceWorker(int counter) {
  29. this.counter = counter;
  30. }
  31. public void run() {
  32. final String TAG = "ServiceWorker" + Thread.currentThread().getId();
  33. try {
  34. Log.v(TAG, "Sleeping for 10 seconds.counter="+counter);
  35. Thread.sleep(10000);
  36. Log.v(TAG, "...waking up");
  37. } catch (Exception e) {
  38. // TODO: handle exception
  39. Log.v(TAG, "...sleep interrupt");
  40. }
  41. }
  42. }
  43. @Override
  44. public void onDestroy() {
  45. // TODO Auto-generated method stub
  46. Log.v(TAG, "in onDestroy. Interrupt threads and canceling notifications");
  47. threadGroup.interrupt();
  48. notificationMgr.cancelAll();
  49. super.onDestroy();
  50. }
  51. @Override
  52. public IBinder onBind(Intent intent) {
  53. // TODO Auto-generated method stub
  54. return null;
  55. }
  56. }</SPAN>
public class MyService extends Service {

	private static final String TAG = "MyService";
private NotificationManager notificationMgr;
private ThreadGroup threadGroup = new ThreadGroup("ServiceWorkder"); @Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.v(TAG, "in onCreate");
notificationMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.ic_launcher, "Service is running", System.currentTimeMillis());
notification.flags = Notification.FLAG_NO_CLEAR;
PendingIntent intent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
notification.setLatestEventInfo(this, TAG, "Service is running", intent);
notificationMgr.notify(0, notification);
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
super.onStartCommand(intent, flags, startId); int counter = intent.getExtras().getInt("counter");
Log.v(TAG, "in onStartCommand, counter = "+counter+",startId = "+startId);
new Thread(threadGroup, new ServiceWorker(counter)).start();
return START_STICKY;
} class ServiceWorker implements Runnable {
private int counter = -1; public ServiceWorker(int counter) {
this.counter = counter;
} public void run() {
final String TAG = "ServiceWorker" + Thread.currentThread().getId();
try {
Log.v(TAG, "Sleeping for 10 seconds.counter="+counter);
Thread.sleep(10000);
Log.v(TAG, "...waking up");
} catch (Exception e) {
// TODO: handle exception
Log.v(TAG, "...sleep interrupt");
}
}
} @Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.v(TAG, "in onDestroy. Interrupt threads and canceling notifications");
threadGroup.interrupt();
notificationMgr.cancelAll();
super.onDestroy();
} @Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
} }

最后不要忘了在AndroidManifest.xml中声明我们的Service:

  1. <SPAN style="FONT-SIZE: 14px">        <service
  2. android:name=".MyService">
  3. </service></SPAN>
        <service
android:name=".MyService">
</service>

最后通知栏运行效果如下:

Android四大组件之Service

1.2、bindService

本地服务也可由Context.bindService()启动,这样调用者和服务绑在一起,调用者一旦退出,服务也就终止了。客户端建立一个与Service连接,使用此连接与Service通信,通过Context.bindService()绑定服务,使用Context.unbindService()关闭服务。多个客户端可以绑定同一个服务,如果Service未启动,bindService()可以启动服务。

注意:上面的startService()和bindService()是完全独立的两种模式,你可以绑定一个已经通过startService()启动的服务。例如:一个后台播放音乐的服务可以通过startService()启动播放,然后activity可以通过调用bindService()方法建立于Service的联系,执行切换歌曲等操作。这种情况下:stopService()不会停止服务,直到最后一个unbindService()调用。

当创建一个能够提供绑定功能的服务时,我们必须提供一个IBinder对象,客户端能够使用这个对象与服务通信,Android中有三种方式:

(1)扩展Binder类

一般用于服务和Activity属于同一个进程的情况。类似上面的startService()我们在MainActivity中建立两个Button,一个用于bindService,另外一个unbindService()。在获得Service的IBinder接口之后就可以调用Service的内部方法了。

  1. <SPAN style="FONT-SIZE: 14px">public class MainActivity extends Activity implements OnClickListener{
  2. private static final String TAG = "MainActivity";
  3. private boolean isBindFlag = false;
  4. private Button btnBind, btnUnbind;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. btnBind = (Button)this.findViewById(R.id.btnBind);
  10. btnUnbind = (Button)this.findViewById(R.id.btnUnbind);
  11. btnBind.setOnClickListener(this);
  12. btnUnbind.setOnClickListener(this);
  13. }
  14. @Override
  15. public void onClick(View v) {
  16. // TODO Auto-generated method stub
  17. switch (v.getId()) {
  18. case R.id.btnBind:
  19. Intent intent2 = new Intent(MainActivity.this, MyBindService.class);
  20. bindService(intent2, serviceConnection, Context.BIND_AUTO_CREATE);
  21. break;
  22. case R.id.btnUnbind:
  23. unbindService(serviceConnection);
  24. break;
  25. default:
  26. break;
  27. }
  28. }
  29. private ServiceConnection serviceConnection = new ServiceConnection() {
  30. @Override
  31. public void onServiceDisconnected(ComponentName name) {
  32. // TODO Auto-generated method stub
  33. isBindFlag = false;
  34. }
  35. @Override
  36. public void onServiceConnected(ComponentName name, IBinder service) {
  37. // TODO Auto-generated method stub
  38. MyBindService.MyBinder binder = (MyBinder)service;
  39. MyBindService bndService = binder.getService();
  40. bndService.myMethod();
  41. isBindFlag = true;
  42. }
  43. };
  44. @Override
  45. protected void onDestroy() {
  46. // TODO Auto-generated method stub
  47. if(isBindFlag == true) {
  48. unbindService(serviceConnection);
  49. }
  50. super.onDestroy();
  51. }
  52. }</SPAN>
public class MainActivity extends Activity implements OnClickListener{

	private static final String TAG = "MainActivity";
private boolean isBindFlag = false;
private Button btnBind, btnUnbind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBind = (Button)this.findViewById(R.id.btnBind);
btnUnbind = (Button)this.findViewById(R.id.btnUnbind);
btnBind.setOnClickListener(this);
btnUnbind.setOnClickListener(this);
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) { case R.id.btnBind:
Intent intent2 = new Intent(MainActivity.this, MyBindService.class);
bindService(intent2, serviceConnection, Context.BIND_AUTO_CREATE);
break; case R.id.btnUnbind:
unbindService(serviceConnection);
break;
default:
break;
}
}
private ServiceConnection serviceConnection = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
isBindFlag = false;
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
MyBindService.MyBinder binder = (MyBinder)service;
MyBindService bndService = binder.getService();
bndService.myMethod();
isBindFlag = true;
}
}; @Override
protected void onDestroy() {
// TODO Auto-generated method stub
if(isBindFlag == true) {
unbindService(serviceConnection);
}
super.onDestroy();
}
}

这里当绑定到MyBindService之后,就可以通过bndService实例调用其方法myMethod()了。下面MyBindService比较简单就是继承于Service,并实现其onBind()接口,返回一个MyBinder实例,客户端拿到这个MyBinder之后可以通过它获取到MyBindService实例,然后调用其提供的myMethod()方法了。

  1. <SPAN style="FONT-SIZE: 14px">public class MyBindService extends Service {
  2. private static final String TAG = "MyBindService";
  3. public void myMethod() {
  4. Log.i(TAG, "myBindService->myMethod()");
  5. }
  6. @Override
  7. public IBinder onBind(Intent intent) {
  8. // TODO Auto-generated method stub
  9. return myBinder;
  10. }
  11. public class MyBinder extends Binder {
  12. public MyBindService getService() {
  13. return MyBindService.this;
  14. }
  15. }
  16. private MyBinder myBinder = new MyBinder();
  17. }</SPAN>
public class MyBindService extends Service {

	private static final String TAG = "MyBindService";

	public void myMethod() {
Log.i(TAG, "myBindService->myMethod()");
} @Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return myBinder;
} public class MyBinder extends Binder { public MyBindService getService() {
return MyBindService.this;
}
} private MyBinder myBinder = new MyBinder();
}

同理最后我们也需要在AndroidManifest.xml中声明我们的服务。

(2)使用Messenger

(3)Remote Service 也就是我们下面要说的AIDL服务了。

2)AIDL服务

2.1)构建远程服务  

上面介绍的各种服务只能由承载它的应用程序使用,如果想构建可由其他进程通过RPC使用的服务,需要使用IDL来定义向客户端公开的接口,在Android中这个IDL就称为AIDL。构建远程服务的一般步骤为:

1、编写一个AIDL文件用来向客户端定义接口。AIDL文件使用Java语法扩展名为.aidl,其内部使用的包名和Android项目使用的包名相同。

首先在项目的src目录下新建一个IStudentInfo.aidl文件,在AIDL文件中定义服务接口。提供了double getScore(String name)接口,根据给定的String类型的学生姓名,返回一个double类型的分数。

  1. package com.myAndroid.aidlService;
  2. interface IStudentInfoService {
  3. double getScore(String name);
  4. }
package com.myAndroid.aidlService;

interface IStudentInfoService {
double getScore(String name);
}

2、将AIDL文件添加到Eclipse项目的src目录下,Android Eclipse插件将调用AIDL编译器从AIDL文件生成Java接口。

生成的java接口文件位于gen/com.myAndroid.aidlService下,名为IStudengInfoService.java:

  1. /*
  2. * This file is auto-generated.  DO NOT MODIFY.
  3. * Original file: C:\\Documents and Settings\\Administrator\\workspace\\Android使用AIDL创建Service\\src\\com\\myAndroid\\aidlService\\IStudentInfoService.aidl
  4. */
  5. package com.myAndroid.aidlService;
  6. public interface IStudentInfoService extends android.os.IInterface
  7. {
  8. /** Local-side IPC implementation stub class. */
  9. public static abstract class Stub extends android.os.Binder implements com.myAndroid.aidlService.IStudentInfoService
  10. {
  11. private static final java.lang.String DESCRIPTOR = "com.myAndroid.aidlService.IStudentInfoService";
  12. /** Construct the stub at attach it to the interface. */
  13. public Stub()
  14. {
  15. this.attachInterface(this, DESCRIPTOR);
  16. }
  17. /**
  18. * Cast an IBinder object into an com.myAndroid.aidlService.IStudentInfoService interface,
  19. * generating a proxy if needed.
  20. */
  21. public static com.myAndroid.aidlService.IStudentInfoService asInterface(android.os.IBinder obj)
  22. {
  23. if ((obj==null)) {
  24. return null;
  25. }
  26. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  27. if (((iin!=null)&&(iin instanceof com.myAndroid.aidlService.IStudentInfoService))) {
  28. return ((com.myAndroid.aidlService.IStudentInfoService)iin);
  29. }
  30. return new com.myAndroid.aidlService.IStudentInfoService.Stub.Proxy(obj);
  31. }
  32. @Override public android.os.IBinder asBinder()
  33. {
  34. return this;
  35. }
  36. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  37. {
  38. switch (code)
  39. {
  40. case INTERFACE_TRANSACTION:
  41. {
  42. reply.writeString(DESCRIPTOR);
  43. return true;
  44. }
  45. case TRANSACTION_getScore:
  46. {
  47. data.enforceInterface(DESCRIPTOR);
  48. java.lang.String _arg0;
  49. _arg0 = data.readString();
  50. double _result = this.getScore(_arg0);
  51. reply.writeNoException();
  52. reply.writeDouble(_result);
  53. return true;
  54. }
  55. }
  56. return super.onTransact(code, data, reply, flags);
  57. }
  58. private static class Proxy implements com.myAndroid.aidlService.IStudentInfoService
  59. {
  60. private android.os.IBinder mRemote;
  61. Proxy(android.os.IBinder remote)
  62. {
  63. mRemote = remote;
  64. }
  65. @Override public android.os.IBinder asBinder()
  66. {
  67. return mRemote;
  68. }
  69. public java.lang.String getInterfaceDescriptor()
  70. {
  71. return DESCRIPTOR;
  72. }
  73. @Override public double getScore(java.lang.String name) throws android.os.RemoteException
  74. {
  75. android.os.Parcel _data = android.os.Parcel.obtain();
  76. android.os.Parcel _reply = android.os.Parcel.obtain();
  77. double _result;
  78. try {
  79. _data.writeInterfaceToken(DESCRIPTOR);
  80. _data.writeString(name);
  81. mRemote.transact(Stub.TRANSACTION_getScore, _data, _reply, 0);
  82. _reply.readException();
  83. _result = _reply.readDouble();
  84. }
  85. finally {
  86. _reply.recycle();
  87. _data.recycle();
  88. }
  89. return _result;
  90. }
  91. }
  92. static final int TRANSACTION_getScore = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  93. }
  94. public double getScore(java.lang.String name) throws android.os.RemoteException;
  95. }
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Documents and Settings\\Administrator\\workspace\\Android使用AIDL创建Service\\src\\com\\myAndroid\\aidlService\\IStudentInfoService.aidl
*/
package com.myAndroid.aidlService;
public interface IStudentInfoService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.myAndroid.aidlService.IStudentInfoService
{
private static final java.lang.String DESCRIPTOR = "com.myAndroid.aidlService.IStudentInfoService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.myAndroid.aidlService.IStudentInfoService interface,
* generating a proxy if needed.
*/
public static com.myAndroid.aidlService.IStudentInfoService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.myAndroid.aidlService.IStudentInfoService))) {
return ((com.myAndroid.aidlService.IStudentInfoService)iin);
}
return new com.myAndroid.aidlService.IStudentInfoService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getScore:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
double _result = this.getScore(_arg0);
reply.writeNoException();
reply.writeDouble(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.myAndroid.aidlService.IStudentInfoService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public double getScore(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
double _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_getScore, _data, _reply, 0);
_reply.readException();
_result = _reply.readDouble();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getScore = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public double getScore(java.lang.String name) throws android.os.RemoteException;
}

对于所生成的类,注意几点:在IStudentInfoService中有一个名为IStudentInfoService的接口,实现了IInterface接口。

内部有一个名为Stub的static final 抽象类扩展了android.os.Binder并实现了IStudentInfoService接口。

内部还有一个名为Proxy的static类,实现了IStudentInfoService接口,它是Stub类的代理。

3、实现一个服务并从onBind()方法返回所生成的接口。

要实现服务的接口,需要编写一个类来扩展android.app.Service并实现IStudentInfoService接口,这个类需要提供onBind()方法将服务向客户端公开。

  1. package com.myAndroid.aidlService;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.IBinder;
  5. import android.os.RemoteException;
  6. import android.util.Log;
  7. public class StudentInfoService extends Service {
  8. private static final String TAG = "StudentInfoService";
  9. public class StudentInfoServiceImpl extends IStudentInfoService.Stub {
  10. @Override
  11. public double getScore(String name) throws RemoteException {
  12. // TODO Auto-generated method stub
  13. Log.v(TAG, "getScore() called for "+ name);
  14. return 85.0;
  15. }
  16. }
  17. @Override
  18. public IBinder onBind(Intent arg0) {
  19. // TODO Auto-generated method stub
  20. Log.v(TAG, "onBind called");
  21. return new StudentInfoServiceImpl();
  22. }
  23. }
package com.myAndroid.aidlService;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log; public class StudentInfoService extends Service { private static final String TAG = "StudentInfoService"; public class StudentInfoServiceImpl extends IStudentInfoService.Stub { @Override
public double getScore(String name) throws RemoteException {
// TODO Auto-generated method stub
Log.v(TAG, "getScore() called for "+ name);
return 85.0;
}
} @Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
Log.v(TAG, "onBind called");
return new StudentInfoServiceImpl();
}
}

从AIDL文件生成的Stub类是抽象类,且实现了IStudentInfoService接口。在我们的服务实现中内部类StudentInfoServiceIml扩展了Stub类,实现了getScore()方法,充当着远程服务具体实现,当客户端bind到服务时,返回一个此类的实例。

4、最后将服务配置添加到AndroidManifest.xml文件中

这次我们需要用一个Intent过滤器来公开服务。

  1. <service android:name="StudentInfoService">
  2. <intent-filter >
  3. <action android:name="com.myAndroid.aidlService.IStudentInfoService"/>
  4. </intent-filter>
  5. </service>
  <service android:name="StudentInfoService">
<intent-filter >
<action android:name="com.myAndroid.aidlService.IStudentInfoService"/>
</intent-filter>
</service>

2.2)调用远程服务  

当客户端与服务通信时,它们之间需要一个协议或契约,在Android中这个协议就是AIDL文件。所以客户端调用服务的第一步就是获取服务的AIDL文件并将其复制到客户端项目中,同理AIDL编译器会创建一个接口定义公开文件,这个文件与服务器中的文件一样。

我们创建一个新的Android项目名为 StudentInfoClient,包名为com.myAndroid.studentInfoClient。然后在这个项目下新建一个Java包名为

com.myAndroid.aidlService,并将IStudentInfoService.aidl文件拷贝到当前包下面。

最后我们在MainActivity中通过bindService()获取服务的引用,然后调用其getScore()方法,即可跟服务端通信。下面给出客户端源码:

  1. package com.myAndroid.studentInfoClient;
  2. import com.myAndroid.aidlService.IStudengInfoService;
  3. import android.os.Bundle;
  4. import android.os.IBinder;
  5. import android.app.Activity;
  6. import android.content.ComponentName;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.ServiceConnection;
  10. import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
  11. import android.view.Menu;
  12. import android.view.View;
  13. import android.view.View.OnClickListener;
  14. import android.widget.Button;
  15. import android.widget.Toast;
  16. import android.widget.ToggleButton;
  17. public class MainActivity extends Activity implements View.OnClickListener {
  18. private ToggleButton toggleButton;
  19. private Button callButton;
  20. private IStudengInfoService myService = null;
  21. @Override
  22. protected void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25. toggleButton = (ToggleButton)this.findViewById(R.id.bindBtn);
  26. callButton = (Button)this.findViewById(R.id.callBtn);
  27. toggleButton.setOnClickListener(this);
  28. callButton.setOnClickListener(this);
  29. }
  30. @Override
  31. public void onClick(View v) {
  32. // TODO Auto-generated method stub
  33. switch (v.getId()) {
  34. case R.id.bindBtn:
  35. if(((ToggleButton)v).isChecked()) {
  36. bindService(new Intent(IStudengInfoService.class.getName()), conn, Context.BIND_AUTO_CREATE);
  37. } else {
  38. unbindService(conn);
  39. callButton.setEnabled(false);
  40. }
  41. break;
  42. case R.id.callBtn:
  43. callService();
  44. break;
  45. default:
  46. break;
  47. }
  48. }
  49. private void callService() {
  50. try {
  51. double val = myService.getScore("Lucy");
  52. Toast.makeText(MainActivity.this, "Value from service is "+ val, Toast.LENGTH_LONG).show();
  53. } catch (Exception e) {
  54. // TODO: handle exception
  55. }
  56. }
  57. private ServiceConnection conn = new ServiceConnection() {
  58. @Override
  59. public void onServiceDisconnected(ComponentName name) {
  60. // TODO Auto-generated method stub
  61. myService = null;
  62. toggleButton.setChecked(false);
  63. callButton.setEnabled(false);
  64. }
  65. @Override
  66. public void onServiceConnected(ComponentName name, IBinder service) {
  67. // TODO Auto-generated method stub
  68. myService = IStudengInfoService.Stub.asInterface(service);
  69. toggleButton.setChecked(true);
  70. callButton.setEnabled(true);
  71. }
  72. };
  73. protected void onDestroy() {
  74. if(callButton.isEnabled()) {
  75. unbindService(conn);
  76. }
  77. super.onDestroy();
  78. }
  79. }
package com.myAndroid.studentInfoClient;

import com.myAndroid.aidlService.IStudengInfoService;

import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ToggleButton; public class MainActivity extends Activity implements View.OnClickListener { private ToggleButton toggleButton;
private Button callButton;
private IStudengInfoService myService = null; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toggleButton = (ToggleButton)this.findViewById(R.id.bindBtn);
callButton = (Button)this.findViewById(R.id.callBtn);
toggleButton.setOnClickListener(this);
callButton.setOnClickListener(this);
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.bindBtn:
if(((ToggleButton)v).isChecked()) {
bindService(new Intent(IStudengInfoService.class.getName()), conn, Context.BIND_AUTO_CREATE);
} else {
unbindService(conn);
callButton.setEnabled(false);
}
break; case R.id.callBtn:
callService();
break;
default:
break;
}
} private void callService() {
try {
double val = myService.getScore("Lucy");
Toast.makeText(MainActivity.this, "Value from service is "+ val, Toast.LENGTH_LONG).show();
} catch (Exception e) {
// TODO: handle exception
}
} private ServiceConnection conn = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
myService = null;
toggleButton.setChecked(false);
callButton.setEnabled(false);
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
myService = IStudengInfoService.Stub.asInterface(service);
toggleButton.setChecked(true);
callButton.setEnabled(true);
}
}; protected void onDestroy() {
if(callButton.isEnabled()) {
unbindService(conn);
}
super.onDestroy();
}
}

代码中对于AIDL服务我们需要提供ServiceConnection接口的实现,此接口定义两个方法:一个供系统建立服务连接时调用,另一个在销毁服务连接时调用。当建立服务时回调onServiceConnected()方法,我们根据参数service调用IStudengInfoService.Stub.asInterface()获得服务端的代理,然后调用其相应方法getScore()。

注意:bindService()是异步调用,因为进程或服务可能没有运行,但是我们不能在主线程上等待服务启动。当从服务解除绑定时我们不会调用onServiceDisConnected(),只有在服务崩溃时才会调用它。如果调用了它,我们可能需要重写调用bindService()。

2.3)向服务传递复杂类型

注意:AIDL对非原语的支持:

1、AIDL支持String和CharSequence。

2、AIDL支持传递其他AIDL接口,但你引用的每个AIDL接口都需要一个import语句。

3、AIDL支持传递实现android.os.Parcelable接口的复杂类型。需要在AIDL文件中包含针对这些类型的Import语句。

4、AIDL支持java.util.List和java.util.Map,但是具有一些限制,集合中的项允许数据类型包括Java原语、String、CharSequence和android.os.Parcelable。无需为List和Map提供import语句,但是需要为Parcelable提供。

5、除字符串外。非原语类型需要一个方向指示符。方向指示符包括in、out和inout。in表示由客户端设置,out表示值由服务设置,inout表示客户端和服务都设置了该值。

Parcelable接口告诉Android运行时在封送marshalling和解unmarshalling过程中如何序列化和反序列化对象。

  1. public class Person implements Parcelable {
  2. private int age;
  3. private String name;
  4. public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
  5. public Person createFromParcel(Parcel in) {
  6. return new Person(in);
  7. }
  8. public Person[] newArray(int size) {
  9. return new Person[size];
  10. }
  11. };
  12. public Person() {
  13. }
  14. private Person(Parcel in) {
  15. readFromParcel(in);
  16. }
  17. @Override
  18. public int describeContents() {
  19. // TODO Auto-generated method stub
  20. return 0;
  21. }
  22. @Override
  23. public void writeToParcel(Parcel dest, int flags) {
  24. // TODO Auto-generated method stub
  25. dest.writeInt(age);
  26. dest.writeString(name);
  27. }
  28. public void readFromParcel(Parcel in) {
  29. age = in.readInt();
  30. name = in.readString();
  31. }
  32. public int getAge() {
  33. return age;
  34. }
  35. public void setAge(int age) {
  36. this.age = age;
  37. }
  38. public String getName() {
  39. return name;
  40. }
  41. public void setName(String name) {
  42. this.name = name;
  43. }
  44. }
public class Person implements Parcelable {

	private int age;
private String name; public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() { public Person createFromParcel(Parcel in) {
return new Person(in);
} public Person[] newArray(int size) {
return new Person[size];
}
}; public Person() {
} private Person(Parcel in) {
readFromParcel(in);
} @Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(age);
dest.writeString(name);
} public void readFromParcel(Parcel in) {
age = in.readInt();
name = in.readString();
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

Parcelable接口定义在封送/解封送过程中混合和分解对象的契约,Parcelable接口的底层是Parcel容器对象,Parcel类是一种最快的序列号和反序列化机制,专为Android中的进程间通信而设计。

要实现Parcelable接口,需要实现writeToParecl()和readFromParcel()方法。写入对象到包裹和从包裹中读取对象,注意:写入属性的顺序和读取属性的顺序必须相同。

向Person类添加一个名为CREATOR的static final属性,该属性需要实现android.os.Parcelable.Creator<T>接口。

为Parcelable提供一个构造函数,知道如何从Parcel创建对象。

在.aidl文件中我们需要导入该类:import com.myAndroid.aidlService.Person。

interface IStudentInfoServie{

String getScore(in String name, in Person requester);    // 后面非原语类型需要一个方向指示符。

}