Android学习笔记---Service

Service是Android四大组件之一,它适合于执行长时间的任务,例如长时间的网络任务,播放音乐,文件I/O等.

Service的一些基本知识

若要创建一个Service必须继承于系统提供的Service类,并复写Service的一些回调方法.下列是一些重要的回调方法:

  1. onStartCommand()
    此方法会在Service被其他组件通过startService()方法启动时调用.一旦此方法此方法执行后,Service便可在后台无限期的运行.如果实现了此方法,当Service的任务完成时,应该通过stopSelf()stopService()方法停止Service.
  2. onBind()
    当其他组件通过bindService()绑定Service时会调用此方法.此方法是必须要实现的,必须提供一个对外的接口(IBinder)让用户与Service进行交互,但如果不想对Service进行绑定可以在此方法返回null.
  3. onCreate()
    Service第一次创建时会调用此方法.可以在此方法执行一次性的设置工作,当Service已经运行,此方法不会再被调用.
  4. onDestroy()
    Service不再使用并被销毁时会调用此方法.应该在此方法进行资源的回收.

    Service有两种启动创建的方法:

  • 其他组件通过startService()启动Service(此时onStartCommand()会被回调),只有当service自己调用stopSelf()或其他组件调用stopService()时Service才会停止工作.
  • 其他组件通过bindService()也可以启动Service(此时onStartCommand()不会被调用),此时只要有组件绑定了Service,它就会运行,当所有绑定Service的组件都取消绑定时,Service会被系统销毁.

当我们创建了一个Service并想在App中使用它时,必须在manifest文件中对创建的Service进行声明.

1
2
3
4
5
6
7
<manifest ... >
...
<application ... >
<service android:name=".ServiceName" />
...
</application>
</manifest>

创建一个启动的Service

首先我们必须创建一个系统Service的子类,google给我们提供了两个类:

  1. Service
    这是所有Service的基类,当你继承了此类时,应该另启一个线程来执行Service需要完成的所有工作,因为Service默认运行在应用的主线程上,这会影响应用的其他组件运行.
  2. IntentService
    这是系统提供的Service的子类,它对Service进行了一定的封装,它运行在其他的工作线程.你只需实现onHandleIntent()方法来在后台处理每一个启动此Service的请求即可.
继承IntentService类

当你的Service继承IntentService时,IntentService的工作方式如下:

  • 创建一个处理intent所触发的onStartCommand()的工作线程.
  • 创建一个一次处理一个请求的工作队列,此队列会处理实现的onHandleIntent()方法.
  • 当所有的请求都处理完成时,会自动停止Service.
  • 默认实现了返回值为nullonBind()方法.
  • 默认实现onStartCommand()方法,此实现将intent发送到工作队列当中.

所以我们需要做的就是实现onHandleIntent()方法,下面是官方的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HelloIntentService extends IntentService {

/**
* 需要实现构造方法,传入Service的名字初始化父类的构造方法
*/

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

/**
* 处理请求
*/

@Override
protected void onHandleIntent(Intent intent) {
// 通常在此进行请求处理
// 在这个例子我们让线程睡眠5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// 恢复线程中断
Thread.currentThread().interrupt();
}
}
}

如果你要复写其他的回调函数,要确保返回父类的实现,这样才能保证IntentService能正常工作.例如下面的代码:

1
2
3
4
5
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}

继承Service类

前面可以看到继承于IntentService会使实现变得简单,但如果你想让你的Service多线程的工作,你可以继承Service类.下面的例子现实了与上面一样功能的Service,这也是官方的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;

// 接受来自工作线程的Handler
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) {
// 恢复线程中断
Thread.currentThread().interrupt();
}
// 通过StartID来识别并停止Service
stopSelf(msg.arg1);
}
}

@Override
public void onCreate() {
// 新建一个线程用于处理请求任务,并设置为后台优先级防止阻塞UI线程
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();

// 获取Handler Lopper
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的消息用于任务完成时停止相应的Service
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);

// 表示当Service被杀死后,会被重新启动
return START_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
// 此例子不允许绑定Service
return null;
}

@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}

可以看出这比继承于IntentService麻烦多了.
虽然这个例子没有同时处理多个请求,但是由于这种方式可以自己处理onStartCommand()方法,所以可以通过每次创建一个线程处理每个请求来实现同时处理多个请求.
onStartCommand()方法有多个返回值:

  • START_NOT_STICKY
    当系统把Service杀死后,不会再重启Service,除非有PendingIntent请求.
  • START_STICKY
    当系统把Service杀死后,会重新创建Service并回调onStartCommand()方法,但并不会保留上次的Intent参数,此时的Intent参数值为null.
  • START_REDELIVER_INTENT
    与第二个返回值一样,唯一不同就是此返回值会把上次的Intent保留作为参数.

可以通过下面的代码启动创建好的Service:

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

创建一个绑定的Service

当你希望Service与用户有所交互时,我们可以创建一个绑定的Service.
当我们要创建一个绑定的Service时,我们需要完成下列的工作:

  • 实现onBind()回调函数,返回一个定义了如何与Service进行通信的IBinder接口.
  • 定义一个实现了IBinder的接口.

用户可以通过bindService()绑定Service,当调用bindService()时,必须提供一个ServiceConnection的实现来监控用户与Service的连接.当连接建立后,ServiceConnectiononServiceConnected()方法会被调用,此方法会把IBinder传递的用户以此来进行通信.
多个用户可以同时绑定Service,但onBind()方法只会在第一个用户绑定时被调用.系统会将一样的IBinder传递给多个用户,而不会再调用onBind()方法.当最后一个用户通过unbindService()取消对Service的绑定时,系统会自动销毁Service.

当你决定要使用绑定的Service时,最主要的是定义onBind()方法所返回的IBinder.Google提供了3种方法给我们:

  • 继承Binder类
    当你的Service为你自己的应用所私有时或运行在默认进程中可采用这种方式,用户通过这种方式获取的IBinder可以访问BinderService中的方法.
  • 使用Messenger
    若你希望你的接口跨进程运行,你可以通过使用Messenger来创建IBinder.在这种方式中Service定义一个用于响应各种Message对象的Handler,用户通过发送Message对象来完成各种命令.
  • 使用AIDL
    由于官方不推荐这种做法,而且也没有过多的去了解,所以这里不做解释.有兴趣可查看Android Bound Services.

这里我们采用第一种方式来列举例子,通常我们采用下列的步骤来完成:

  1. Service中创建一个Binder的实例并在实现中:
    • 包含用户可调用的共有方法
    • 返回当前的Service实例或一个持有Service实例的其他对象
  2. onBind()中返回该Binder对象.
  3. 用户通过onServiceConnected()回调获取Binder对象,并用此来进行通信.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class LocalService extends Service {
// 传递给用户的Binder
private final IBinder mBinder = new LocalBinder();
// 随机数生成器
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);
}
}

下面是绑定Service的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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();
// 绑定Service
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;
}
}

/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */

public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}

/** 为用户绑定Service定义的回调 */
private ServiceConnection mConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName className,
IBinder service)
{

// 获取到绑定后的Service
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}

上面的代码通过ServiceConnectiononServiceConnected()回调方法对Service进行绑定.

Service的生命周期

Service lifecycle