From: http://coolxing.iteye.com/blog/1222602
Service是android中的服务组件, 经常用来执行一些运行在后台的耗时操作. 使用一个Service需要继承Service类, 并根据需要重写生命周期方法. Service的生命周期如下:
|-- public abstract IBinder onBind (Intent intent): 该方法是一个抽象方法, 因此Service子类必须实现这个方法. 它返回一个IBinder对象, 应用程序可以通过这个对象与Service组件通信(关于这一点, 其后会有详细的讲解), 以bindService()方式启动Service时回调该方法.
|-- public void onCreate (): 当Service第一次被创建后回调的方法.
|-- public void onDestroy (): Service关闭之前回调的方法.
|-- public int onStartCommand (Intent intent, int flags, int startId): 以startService(Intent intent)的方式启动Service时, 系统都会回调该方法.
|-- public boolean onUnbind (Intent intent): 当所有绑定该Service的组件都断开连接时回调该方法.
从图中可以看出, Service可以有两种启动方式:
1. 以startService(Intent intent)的方式启动. 此时启动的Service与调用者之间没有关联, 即使调用者已经退出, Service仍然可以继续运行, 而且调用者和Service之间无法进行数据交换和通信. 如果需要停止Service的运行, 只能调用Context类的stopService(intent)方法, 或者由Service本身调用其stopSelf()等方法.
2. 以bindService(Intent service, ServiceConnection conn, int flags)的方式启动.
此时调用者与Service绑定在一起, 如果调用者退出, 则Service也随之退出, 而且调用者和Service之间可以进行数据交换或通信.
根据调用者和Service是否在一个应用程序内, 可以将调用者和Service之间的通信分为进程内通信和进程间通信.
a. 进程内通信. bindService(Intent service, ServiceConnection conn, int flags)方法的第二个参数为ServiceConnection对象, 最后一个参数通常可以是Service.BIND_AUTO_CREATE. ServiceConnection是一个接口, 该接口包含2个方法:
|-- onServiceConnected(ComponentName name, IBinder service): 该方法在调用者和Service成功绑定之后由系统回调.
方法中的第一个参数ComponentName是所绑定的Service的组件名称, 而IBinder对象就是Service中onBinder()方法的返回值. 要实现调用者和Service之间的通信, 只需要调用IBinder对象中定义的方法即可.
|-- onServiceDisconnected(ComponentName name): 该方法在调用者解除和Service的绑定之后由系统回调.
以下是利用Service实现进程内通信的一个例子.
首先自定义Service类:
- public class MyService extends Service {
- public class MyBinder extends Binder {
- /**
- * 获取Service的运行时间
- * @return
- */
- public long getServiceRunTime() {
- return System.currentTimeMillis() - startTime;
- }
- }
- private long startTime;
- /**
- * MyBinder是Binder的子类, 而Binder实现了IBinder接口.
- */
- @Override
- public IBinder onBind(Intent intent) {
- return new MyBinder();
- }
- @Override
- public void onCreate() {
- super.onCreate();
- startTime = System.currentTimeMillis();
- }
- }
然后在activity中绑定上述的Service:
- public class MainActivity extends Activity {
- private MyBinder binder = null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 创建一个指向MyService的intent
- Intent intent = new Intent("cn.xing.action.my_service");
- this.bindService(intent, new MyServiceConnection(),
- Service.BIND_AUTO_CREATE);
- Button button = (Button) this.findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (binder != null) {
- Toast.makeText(getApplicationContext(), "MyService已经运行了" + binder.getServiceRunTime()
- + "毫秒", Toast.LENGTH_LONG).show();
- }
- }
- });
- }
- /**
- * 实现ServiceConnection接口
- *
- * @author xing
- *
- */
- private final class MyServiceConnection implements ServiceConnection {
- /**
- * 和MyService绑定时系统回调这个方法
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // MyService中的onBinder()方法的返回值实际上是一个MyBinder对象, 因此可以使用强制转换.
- binder = (MyBinder) service;
- }
- /**
- * 解除和MyService的绑定时系统回调这个方法
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // 解除和MyService的绑定后, 将binder设置为null.
- binder = null;
- }
- }
- }
b.进程间通信. 调用者和Service如果不在一个进程内, 就需要使用android中的远程Service调用机制.
android使用AIDL定义进程间的通信接口. AIDL的语法与java接口类似, 需要注意以下几点:
1. AIDL文件必须以.aidl作为后缀名.
2. AIDL接口中用到的数据类型, 除了基本类型, String, List, Map, CharSequence之外, 其他类型都需要导包, 即使两种在同一个包内. List和Map中的元素类型必须是AIDL支持的类型.
3. 接口名需要和文件名相同.
4. 方法的参数或返回值是自定义类型时, 该自定义的类型必须实现了Parcelable接口.
5. 所有非java基本类型参数都需要加上in, out, inout标记, 以表明参数是输入参数, 输出参数, 还是输入输出参数.
6. 接口和方法前不能使用访问修饰符和static, final等修饰.
下面通过一个例子来演示android远程Service调用机制的各个步骤:
1. 创建remoteService项目.
2. 在cn.xing.remoteservice包下定义aidl文件--IRemoteService.aidl:
- package cn.xing.remoteservice;
- interface IRemoteService{
- int getServiceRunTime();
- }
Eclipse的ADT插件会在gen目录的cn.xing.remoteservice包下自动根据aidl文件生成IRemoteService接口.
接口中有一个static的抽象内部类Stub, Stub类继承了Binder类并实现了IRemoteService接口. Stub类有如下的静态方法:
public static cn.xing.remoteservice.IRemoteService asInterface(android.os.IBinder obj)
该方法接受一个IBinder对象, 返回值是IRemoteService的instance, 通过这个instance我们就可以调用IRemoteService中定义的方法了.
3. 在remoteService项目的cn.xing.remoteservice包下创建远程服务类RemoteService:
- public class RemoteService extends Service {
- private long startTime;
- /**
- * IRemoteService.Stub类实现了IBinder和IRemoteService接口
- * 因此Stub的子类对象可以作为onBinder()方法的返回值.
- * @author xing
- *
- */
- public class MyBinder extends IRemoteService.Stub {
- @Override
- public long getServiceRunTime() throws RemoteException {
- return System.currentTimeMillis() - startTime;
- }
- };
- @Override
- public IBinder onBind(Intent intent) {
- return new MyBinder();
- }
- @Override
- public void onCreate() {
- super.onCreate();
- startTime = System.currentTimeMillis();
- }
- }
4. 创建一个新的android项目invokeRemoteService, 并复制remoteService项目中的aidl文件(连同包结构一起复制)到invokeRemoteService项目中.
5. 在invokeRemoteService项目中绑定远程服务, 并调用远程方法.
- public class MainActivity extends Activity {
- private IRemoteService iRemoteService = null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 创建一个指向RemoteService的intent
- Intent intent = new Intent("cn.xing.action.remote_service");
- this.bindService(intent, new MyServiceConnection(),
- Service.BIND_AUTO_CREATE);
- Button button = (Button) this.findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (iRemoteService != null) {
- try {
- Toast.makeText(getApplicationContext(), "MyService已经运行了" + iRemoteService.getServiceRunTime()
- + "毫秒", Toast.LENGTH_LONG).show();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
- });
- }
- /**
- * 实现ServiceConnection接口
- *
- * @author xing
- *
- */
- private final class MyServiceConnection implements ServiceConnection {
- /**
- * 和RemoteService绑定时系统回调这个方法
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // 此处不能使用强制转换, 应该调用Stub类的静态方法获得IRemoteService接口的实例对象
- iRemoteService = IRemoteService.Stub.asInterface(service);
- }
- /**
- * 解除和RemoteService的绑定时系统回调这个方法
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // 解除和RemoteService的绑定后, 将iRemoteService设置为null.
- iRemoteService = null;
- }
- }
- }