Service
是Android四大组件之一,它适合于执行长时间的任务,例如长时间的网络任务,播放音乐,文件I/O等.
Service的一些基本知识
若要创建一个Service必须继承于系统提供的Service
类,并复写Service
的一些回调方法.下列是一些重要的回调方法:
- onStartCommand()
此方法会在Service
被其他组件通过startService()
方法启动时调用.一旦此方法此方法执行后,Service
便可在后台无限期的运行.如果实现了此方法,当Service
的任务完成时,应该通过stopSelf()
或stopService()
方法停止Service
. - onBind()
当其他组件通过bindService()
绑定Service
时会调用此方法.此方法是必须要实现的,必须提供一个对外的接口(IBinder
)让用户与Service
进行交互,但如果不想对Service
进行绑定可以在此方法返回null
. - onCreate()
当Service
第一次创建时会调用此方法.可以在此方法执行一次性的设置工作,当Service
已经运行,此方法不会再被调用. 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给我们提供了两个类:
- Service
这是所有Service
的基类,当你继承了此类时,应该另启一个线程来执行Service
需要完成的所有工作,因为Service
默认运行在应用的主线程上,这会影响应用的其他组件运行. - IntentService
这是系统提供的Service
的子类,它对Service
进行了一定的封装,它运行在其他的工作线程.你只需实现onHandleIntent()
方法来在后台处理每一个启动此Service
的请求即可.
继承IntentService类
当你的Service继承IntentService
时,IntentService
的工作方式如下:
- 创建一个处理
intent
所触发的onStartCommand()
的工作线程. - 创建一个一次处理一个请求的工作队列,此队列会处理实现的
onHandleIntent()
方法. - 当所有的请求都处理完成时,会自动停止
Service
. - 默认实现了返回值为
null
的onBind()
方法. - 默认实现
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
24public class HelloIntentService extends IntentService {
/**
* 需要实现构造方法,传入Service的名字初始化父类的构造方法
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* 处理请求
*/
protected void onHandleIntent(Intent intent) {
// 通常在此进行请求处理
// 在这个例子我们让线程睡眠5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// 恢复线程中断
Thread.currentThread().interrupt();
}
}
}
如果你要复写其他的回调函数,要确保返回父类的实现,这样才能保证IntentService
能正常工作.例如下面的代码:1
2
3
4
5
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
59public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 接受来自工作线程的Handler
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
// 在此处处理请求
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// 恢复线程中断
Thread.currentThread().interrupt();
}
// 通过StartID来识别并停止Service
stopSelf(msg.arg1);
}
}
public void onCreate() {
// 新建一个线程用于处理请求任务,并设置为后台优先级防止阻塞UI线程
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// 获取Handler Lopper
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
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;
}
public IBinder onBind(Intent intent) {
// 此例子不允许绑定Service
return null;
}
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
2Intent intent = new Intent(this, HelloService.class);
startService(intent);
创建一个绑定的Service
当你希望Service
与用户有所交互时,我们可以创建一个绑定的Service
.
当我们要创建一个绑定的Service
时,我们需要完成下列的工作:
- 实现
onBind()
回调函数,返回一个定义了如何与Service
进行通信的IBinder
接口. - 定义一个实现了
IBinder
的接口.
用户可以通过bindService()
绑定Service
,当调用bindService()
时,必须提供一个ServiceConnection
的实现来监控用户与Service
的连接.当连接建立后,ServiceConnection
的onServiceConnected()
方法会被调用,此方法会把IBinder
传递的用户以此来进行通信.
多个用户可以同时绑定Service
,但onBind()
方法只会在第一个用户绑定时被调用.系统会将一样的IBinder
传递给多个用户,而不会再调用onBind()
方法.当最后一个用户通过unbindService()
取消对Service
的绑定时,系统会自动销毁Service
.
当你决定要使用绑定的Service
时,最主要的是定义onBind()
方法所返回的IBinder
.Google提供了3种方法给我们:
- 继承Binder类
当你的Service
为你自己的应用所私有时或运行在默认进程中可采用这种方式,用户通过这种方式获取的IBinder
可以访问Binder
和Service
中的方法. - 使用Messenger
若你希望你的接口跨进程运行,你可以通过使用Messenger
来创建IBinder
.在这种方式中Service
定义一个用于响应各种Message
对象的Handler
,用户通过发送Message
对象来完成各种命令. - 使用AIDL
由于官方不推荐这种做法,而且也没有过多的去了解,所以这里不做解释.有兴趣可查看Android Bound Services.
这里我们采用第一种方式来列举例子,通常我们采用下列的步骤来完成:
- 在
Service
中创建一个Binder
的实例并在实现中:- 包含用户可调用的共有方法
- 返回当前的
Service
实例或一个持有Service
实例的其他对象
- 在
onBind()
中返回该Binder
对象. - 用户通过
onServiceConnected()
回调获取Binder
对象,并用此来进行通信.
1 | public class LocalService extends 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
58public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
protected void onStart() {
super.onStart();
// 绑定Service
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
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() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// 获取到绑定后的Service
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
上面的代码通过ServiceConnection
和onServiceConnected()
回调方法对Service
进行绑定.