Android中Service的四种使用方式

Service是Android中的四大组件之一,用于在后台进行长时间作业,不提供用户界面。Service可以直接由其他的应用程序组件启动并在后台运行,也可以与其他组件绑定来实现进程间通信。本文介绍了创建Service的基本方法,并通过实例描述了Service的四种使用方式,包括Service的启动和绑定,以及IntentService和AIDL Service的使用。

0. 创建Service

  创建一个Service有两个步骤:实现Service子类,以及在manifest文件中声明该Service。

0.1. 实现Service子类

  与Activity类似,实现自己的Service子类时,需要重写一些回调方法,来管理Service的生命周期,如onStartCommand()onBind()onCreate()onDestroy()等。需要注意的是,onBind()是必须要重写的。举例来说,一个Service的简单实现如[代码0-1]所示,这个Service开启了一个线程来对count 进行累加和打印。

public class MyService extends Service {

    private static final String TAG = "MyService";

    private int count;
    private boolean quit;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service is created.");

        count = 0;
        quit = false;
        new Thread() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                    Log.i(TAG, "count = " + count);
                }
            }
        }.start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "Service is started.");
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        quit = true;
        Log.i(TAG, "Service is destroyed.");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

0.2. 在manifest文件中声明

  在manifest文件中声明Service的方法也与Activity类似,只需将<service…/> 插入到<application> 元素下面即可,如[代码0-2]所示。

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

关于<service> 下的更多配置可以参考这里。Google的教程中指出,从安全角度考虑,在启动服务或绑定服务时尽量显式地指出所要使用的Service,而不要通过向Service加入Intent Filters来指明可以启动该Service的Intent。

1. 启动Service

  Service的启动可以通过调用Context中的startService()实现,相对应的,stopService()用于停止Service。

  新建一个Activity,命名为MyActivity;再新建一个如[代码0-1]所示的MyService,并按照[代码0-2]的形式在AndroidManifest.xml文件中声明MyService。在MyActivity中加入两个按钮btnStartService 和btnStopService ,分别用于启动和停止服务,如[代码1-1]所示。

public class MyActivity extends Activity {

    private static final String TAG = "MyActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        final Intent serviceIntent = new Intent(this, MyService.class);
        Button btnStartService = (Button)findViewById(R.id.btnStartService);
        btnStartService.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                startService(serviceIntent);
            }
        });

        Button btnStopService = (Button)findViewById(R.id.btnStopService);
        btnStopService.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                stopService(serviceIntent);
            }
        });
    }
}

   运行MyActivity,点击按钮启动Service,然后再点击按钮停止Service,可以看到打印信息:

10-21 21:44:14.868  27063-27063/com.nex3z.servicetest I/MyService﹕ Service is created.
10-21 21:44:14.868  27063-27063/com.nex3z.servicetest I/MyService﹕ Service is started.
10-21 21:44:15.869  27063-27296/com.nex3z.servicetest I/MyService﹕ count = 1
10-21 21:44:16.870  27063-27296/com.nex3z.servicetest I/MyService﹕ count = 2
10-21 21:44:17.871  27063-27296/com.nex3z.servicetest I/MyService﹕ count = 3
10-21 21:44:18.802  27063-27063/com.nex3z.servicetest I/MyService﹕ Service is destroyed.

 可见Service能够正常地启动与停止。连续两次点击按钮启动Service,可以看到打印信息:

10-21 21:46:05.866  27733-27733/com.nex3z.servicetest I/MyService﹕ Service is created.
10-21 21:46:05.866  27733-27733/com.nex3z.servicetest I/MyService﹕ Service is started.
10-21 21:46:06.877  27733-28241/com.nex3z.servicetest I/MyService﹕ count = 1
10-21 21:46:07.638  27733-27733/com.nex3z.servicetest I/MyService﹕ Service is started.
10-21 21:46:07.868  27733-28241/com.nex3z.servicetest I/MyService﹕ count = 2

由此可以看出,startService()首先会通过Service的onCreate()来生成Service,然后通过onStartCommand()来启动Service。重复启动同一个Service,onStartCommand()会被重复调用,而onCreate()只会执行一次。

2. 绑定Service

  在上面的例子中,Activity仅仅是控制了Service的启动和停止,如果要实现与Service之间的数据交换,就需要将Service与Activity进行绑定,由Service需要向Activity提供访问其自身数据的方法。

  新建一个Service,命名为MyBindService,其内容与[代码0-1]所示的MyService基本一致,仅仅是更改了TAG 并去掉了对count 的打印,如[代码2-1]所示。

public class MyBindService extends Service {

    private static final String TAG = "MyBindService";

    private int count;
    private boolean quit;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service is created.");

        count = 0;
        quit = false;
        new Thread() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        }.start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "Service is started.");
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        quit = true;
        Log.i(TAG, "Service is destroyed.");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

  为了让MyBindService向MyActivity提供访问其自身数据的方法,需要确实地实现MyBindService的onBind()方法,并在MyActivity中对MyBindService进行绑定。

2.1. 实现onBind()方法

  虽然[代码2-1]所示的MyBindService中也实现了onBind()方法,但只是返回了一个null 值,没有实际意义。这里需要修改让onBind()返回一个IBinder对象,这个IBinder对象会在MyService(或其他组件)绑定MyBindService的时候,由MyBindService返回给MyService,MyService可以通过这个IBinder对象与MyBindService进行通信。

  实现IBinder时一般会直接继承Binder,它是IBinder的实现。这里实现的MyBinder如[代码2-2]所示,它提供了一个getCount()方法,返回了MyBindService中的count 。

public class MyBinder extends Binder {
    public int getCount() {
        return count;
    }
}

  接下来为MyBindService添加成员private MyBinder binder = new MyBinder(); ,并修改MyBindService的onBind()方法返回binder ,如[代码2-3]所示。

@Override
public IBinder onBind(Intent intent) {
    Log.i(TAG, "Service is binded.");
    return binder;
}

   到这里便完成了MyBindService,完整代码如[代码2-4]所示。

public class MyBindService extends Service {

    private static final String TAG = "MyBindService";

    private int count;
    private boolean quit;
    private MyBinder binder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service is created.");

        count = 0;
        quit = false;
        new Thread() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        }.start();
    }

    public class MyBinder extends Binder {
        public int getCount() {
            return count;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "Service is binded.");
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "Service is unbinded.");
        return true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        quit = true;
        Log.i(TAG, "Service is destroyed.");
    }
}

2.2. Service的绑定

   Service的绑定是通过bindService()实现的,其完整签名为:

public abstract boolean bindService (Intent service, ServiceConnection conn, int flags)

 其中service 指明所要绑定的Service,conn 是一个ServiceConnection对象,用于接收Service启动和停止的信息,flags 用于设置绑定的参数,比如是否在绑定时自动创建Service(0表示不自动创建,BIND_AUTO_CREATE 表示自动创建等,其他选项可以参考API文档)。

    为了完成绑定,需要实现bindService()参数中的ServiceConnection,它用于监听所绑定Service的连接状态:当连接成功时,将回调ServiceConnection对象的onServiceConnected()方法,Service中onBind()方法返回的IBinder会作为参数传递给onServiceConnected();当连接意外中断时,则回调onServiceDisconnected()。

    回到例子的具体实现上,首先为MyActivity添加成员private MyBinder binder = new MyBinder(); 用于在本地保存MyBindService返回的IBinder,然后实现ServiceConnection如[代码2-5]所示。

private ServiceConnection serviceConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        binder = (MyBindService.MyBinder) iBinder;
        Log.i(TAG, "Service is connected.");
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.i(TAG, "Service is disconnected.");
    }
};

 这里onServiceConnected()将作为参数传递过来的iBinder 保存为本地的binder ,onServiceDisconnected()只进行了打印。

    在MyActivity中加入三个按钮:btnBindService 用于绑定MyBindService,btnStatus 用于读取MyBindService中count 的值,btnBindService 用于解绑MyBindService。最终结果如[代码2-6]所示。

public class MyActivity extends Activity {
    private static final String TAG = "MyActivity";

    private MyBindService.MyBinder binder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        final Intent bindServiceIntent = new Intent(this, MyBindService.class);

        Button btnBindService = (Button)findViewById(R.id.btnBindService);
        btnBindService.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                bindService(bindServiceIntent, serviceConn, Service.BIND_AUTO_CREATE);
            }
        });

        Button btnStatus = (Button)findViewById(R.id.btnStatus);
        btnStatus.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MyActivity.this, "count = " + binder.getCount(), 
                               Toast.LENGTH_SHORT).show();
                Log.i(TAG, "count = " + binder.getCount());
            }
        });

        Button btnUnbindService = (Button)findViewById(R.id.btnUnbindService);
        btnUnbindService.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                unbindService(serviceConn);
            }
        });
    }

    private ServiceConnection serviceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            binder = (MyBindService.MyBinder) iBinder;
            Log.i(TAG, "Service is connected.");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i(TAG, "Service is disconnected.");
        }
    };
}

其中第17行通过bindService(bindServiceIntent, serviceConn, Service.BIND_AUTO_CREATE); ,使MyActivity与bindServiceIntent 所指定的MyBindService进行了绑定,由serviceConn 管理连接,Service.BIND_AUTO_CREATE 表示在绑定时自动创建Service。

   运行MyActivity,点击btnBindService 绑定MyBindService后,点击btnStatus 读取MyBindService中count 的数值,再点击btnBindService 解除绑定,得到打印信息:

10-18 18:34:38.874    4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is created.
10-18 18:34:38.894    4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is binded.
10-18 18:34:38.894    4492-4492/com.nex3z.servicetest I/MyActivity﹕ Service is connected.
10-18 18:34:41.547    4492-4492/com.nex3z.servicetest I/MyActivity﹕ count = 3
10-18 18:34:44.260    4492-4492/com.nex3z.servicetest I/MyActivity﹕ count = 6
10-18 18:34:46.762    4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is unbinded.
10-18 18:34:46.762    4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is destroyed.

可见,MyActivity可以获取到MyBindService中count 的数值。在绑定服务时,首先执行的是Service中的onCreate()和onBind()方法,然后才会回调在绑定Service时所指定的ServiceConnection对象的onServiceConnected()方法。解除绑定时,onServiceDisconnected()方法没有执行,这是因为上面的例子是通过unBindService()来主动解除绑定的,onServiceConnected()只有在Service所在的宿主进程意外终止导致连接断开时才会被调用。

3. IntentService

   在前面的例子中,Activity所启动的Service与Activity在同一个进程中运行,而且Service也不会自动为自己的业务新建线程,如果在Service中直接处理耗时任务,就可能导致应用程序无响应(Application Not Responding,ANR )。所以在[代码0-1]和[代码2-4]中,都是在Service的onCreate()中手动新建了线程对count 进行计数。

   IntentService是Service的子类,顾名思义,客户端使用startService()通过Intent请求启动IntentService,IntentService使用队列对客户端发来的Intent进行管理,并开启新线程来处理Intent。

   一个例子如[代码3-1]所示,IntentService为onBind()和onStartCommand()提供了默认实现,所以只重写了onHandleEvent()。MyIntentService进行了30秒的计时和打印。

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, "Count start.");
        int count = 1;
        while (count <= 30) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "count = " + count);
            count++;
        }
        Log.i(TAG, "Count complete.");
    }

}

  为了与IntentService对比,建立如[代码3-2]所示的普通Service,CounterService同样是进行30秒的计时和打印,但没有手动新建线程。

public class CounterService extends Service {

    private static final String TAG = "CounterService";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "Count start.");
        int count = 1;
        while (count <= 30) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "count = " + count);
            count++;
        }
        Log.i(TAG, "Count complete.");
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

  执行MyIntentService,打印结果如下:

...
10-25 15:59:29.529    6271-6990/com.nex3z.servicetest I/MyIntentService﹕ count = 27
10-25 15:59:30.530    6271-6990/com.nex3z.servicetest I/MyIntentService﹕ count = 28
10-25 15:59:31.541    6271-6990/com.nex3z.servicetest I/MyIntentService﹕ count = 29
10-25 15:59:31.541    6271-6990/com.nex3z.servicetest I/MyIntentService﹕ Count complete.

 可见MyIntentService顺利完成了计时。而执行CounterService时,打印结果如下:

...
10-25 16:02:02.128    6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 19
10-25 16:02:03.129    6271-6276/com.nex3z.servicetest I/dalvikvm﹕ threadid=3: reacting to signal 3
10-25 16:02:03.159    6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 20
10-25 16:02:03.159    6271-6276/com.nex3z.servicetest I/dalvikvm﹕ Wrote stack traces to '/data/anr/traces.txt'
10-25 16:02:04.160    6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 21
10-25 16:02:05.161    6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 22
10-25 16:02:06.162    6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 23
10-25 16:02:07.153    6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 24
10-25 16:02:07.544    6271-6271/com.nex3z.servicetest A/libc﹕ Fatal signal 6 (SIGABRT) at 0x0000027b (code=0), thread 6271 (x3z.servicetest)

 在CounterService开始执行后,应用便会失去响应,执行时间过长还会出现ANR。

4. AIDL Service

  如前所述,通过绑定Service可以实现Service与其他组件之间的数据交换,但Android系统中的进程之间一般无法直接交换数据,为了让Service能够跨进程与其他组件进行通信,就需要使用AIDL(Android Interface Definition Language)。

  下面依旧通过一个例子说明AIDL Service的使用。例子内容与前面类似,依旧是在Service中进行计数,由Activity读取Service中计数的值,不过这次Activity与Service不在处于同一进程,Service需要定义一个远程调用接口,并提供实现,Activity远程调用Service的接口,与Service进行通信。

4.1. 编写AIDL文件

  AIDL用于定义远程接口,其详细使用方法可以参考官方文档,这里只是通过例子给出一个简单的示范,建立一个MyAIDLInterface.aidl文件,如[代码4-1]所示。

interface IMyAidlInterface {
    int getCount();
}

可以看出,AIDL的语法与Java类似,上面的例子声明了一个getCount()方法,该方法返回一个int型的值。如果使用Eclipse,上面的AIDL文件保存之后,就可以在gen目录下找到一个自动生成的MyAidlInterface.java文件;如果使用的是Android Studio,则需要先“Make Project”,然后就可以在build/generated/source/aidl下找到自动生成的MyAidlInterface.java文件。MyAidlInterface包含一个内部类Stub,该内部类继承了Binder,并实现了IMyAidlInterface。由于Stub继承了Binder,可以将Stub的实例作为Service的onBind()方法的返回值,提供给远程组件;由于与Stub实现了IMyAidlInterface,使得Stub可以向远程组件提供MyAIDLInterface.aidl中定义的接口。

4.2. 建立Service

  MyAIDLInterface.aidl文件中声明了接口getCount(),接下来就要给出getCount()的实现,如代码[4-2]所示。

public class MyAidlServiceBinder extends IMyAidlInterface.Stub {

    @Override
    public int getCount() throws RemoteException {
        return count;
    }
}

 声明MyAidlServiceBinder继承IMyAidlInterface.Stub,并实现getCount()返回count ——Service中的计数值。

  然后为Service添加成员private MyAidlServiceBinder binder; ,并通过onBind()返回binder ,如[代码4-3]所示。

@Override
public IBinder onBind(Intent intent) {
    return binder;
}

   最终得到的Service如[代码4-4]所示:

public class MyAidlService extends Service {
    private static final String TAG = "MyAidlActivity";

    private int count;
    private boolean quit;
    private MyAidlServiceBinder binder;

    public class MyAidlServiceBinder extends IMyAidlInterface.Stub {

        @Override
        public int getCount() throws RemoteException {
            return count;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service is created.");

        binder = new MyAidlServiceBinder();

        count = 0;
        quit = false;
        new Thread() {
            @Override
            public void run() {
                while (!quit) {

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        }.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "Service is destroyed.");
    }
}

 在onCreate()中,通过binder = new MyAidlServiceBinder(); 创建了MyAidlServiceBinder的实例,供onBind()作为返回值。此时可以先运行MyAidlService,供后面MyActivity进行远程调用。

4.3. 远程调用

   依旧使用MyActivity远程调用MyAidlService。为MyActivity添加成员private IMyAidlInterface aidlService; ,添加按钮btnBindAidlService  和btnUnbindAidlService 分别用于绑定Service和解绑Service,如[代码4-5]所示。

final Intent bindAidlServiceIntent = new Intent(this, MyAidlService.class);

Button btnBindAidlService = (Button)findViewById(R.id.btnBindAidlService);
btnBindAidlService.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        bindService(bindAidlServiceIntent, aidlServiceConn, Service.BIND_AUTO_CREATE);
    }
});

Button btnUnbindAidlService = (Button)findViewById(R.id.btnUnbindAidlService);
btnUnbindAidlService.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        unbindService(aidlServiceConn);
    }
});

可以看出,绑定和解绑AIDL Service的流程与绑定普通Service类似,第7行中绑定Service时使用的aidlServiceConn 如[代码4-6]所示。

private ServiceConnection aidlServiceConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        aidlService = IMyAidlInterface.Stub.asInterface(iBinder);
        Log.i(TAG, "AIDL Service is connected.");
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        aidlService = null;
        Log.i(TAG, "AIDL Service is disconnected.");
    }
};

在onServiceConnected()的第4行,把通过参数传递过来的iBinder 保存到本地。注意这里使用了IMyAidlInterface.Stub.asInterface(iBinder) ,而不是直接像[代码2-6]中那样直接对iBinder 进行类型转换,这是因为绑定远程Service的ServiceConnection不能直接获取Service中onBind()方法返回的对象,而只能通过IMyAidlInterface.Stub.asInterface(iBinder) 获取onBind()返回对象的代理。

   为MyActivity添加按钮btnStatusAidl 用于读取并打印MyAidlService中的count 值,如代码[4-7]所示。

Button btnStatusAidl = (Button)findViewById(R.id.btnStatusAidl);
btnStatusAidl.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        try {
            Toast.makeText(MyActivity.this, "count = " + aidlService.getCount(), Toast.LENGTH_SHORT).show();
            Log.i(TAG, "count = " + aidlService.getCount());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
});

直接通过aidlService.getCount() 就实现了远程调用,获取了MyAidlService中count 的值,打印结果如下:

...
10-25 17:15:00.780    9505-9505/com.nex3z.servicetest I/MyActivity﹕ count = 2
10-25 17:15:02.392    9505-9505/com.nex3z.servicetest I/MyActivity﹕ count = 4

可见,MyActivity成功与MyAidlService进行了通信。