Android开发史上最全系列之Android开发基础Android之service
sfshine
于 2013-6-13 22:12
发表在 [电子书/电子文档]
今天学习Android Service 这几天看了很多Service Framework 层的东西 但是感觉在这里说明还是比较麻烦的具体还是查看 老罗的两篇博客吧:
Android系统在新进程中启动自定义服务过程(startService)的原理分析
Android应用程序绑定服务(bindService)的过程源代码分析
这里只是介绍一个大体的概念:
1.Service的启动一般都是 某个组件调用startService 或者 bindService
2. 通过 Android特有的 Binder IPC机制 通知 ActivityServiceManager ,
3.Ams就会直接通过Binder IPC机制 通知 Service 所在的ActivityThread 把这个服务启动起来 (把这个Service的class文件load到内存)
4.如果是BindService ,Ams还会从Service哪里取到一个binder 然后同样通过IPC传给启动他的Activity ,Activity 拿到这个binder后就可以和Service通讯了
ok 下面注重介绍的是Service的用法
1.通过startService方式启动的Service
a 生命周期
context.startService() 的生命周期
context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop
如果Service还没有运行,则android先调用onCreate(),然后调用onStart();
如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。
如果stopService的时候会直接onDestroy,如果是调用者自己直接退出而没有调用stopService或者Service.stopSelfResult()的话,Service会一直在后台运行,该Service的调用者再启动起来后可以通过stopService或者Service.stopSelfResult()关闭Service。
所以调用startService的生命周期为:
onCreate-->onStart(可多次调用) -->onDestroy
如图:
![](http://www.eoeandroid.com/data/attachment/forum/201306/13/172329h5nrb34xysczysxn.jpg)
实例代码
MainActivity.java
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
package
com.example.testservice;
public
class
MainActivity
extends
Activity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Intent service = new Intent(this, MyService.class);//通过组件名调用
Intent service =
new
Intent(
"com.example.testservice.MYSERVICEFROMACTION"
);
// 隐式调用,注意在manifest中指定action
startService(service);
}
}
|
Service.java
01
02
03
04
05
06
07
08
09
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
|
public
class
MyService
extends
Service {
String TAG =
"MyService"
;
@Override
public
void
onCreate() {
new
AsyncTask<Void, Void, Void>() {
@Override
protected
Void doInBackground(Void... params) {
for
(
int
i =
0
; i <
1000
; i++) {
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e(TAG, i +
"service"
);
}
return
null
;
}
}.execute();
super
.onCreate();
}
@Override
public
IBinder onBind(Intent intent) {
Log.e(TAG,
"onBind"
);
return
null
;
}
}
|
AndroidManifest.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
<
application
android:allowBackup
=
"true"
android:icon
=
"@drawable/ic_launcher"
android:label
=
"@string/app_name"
android:theme
=
"@style/AppTheme"
>
<
activity
android:name
=
"com.example.testservice.MainActivity"
android:label
=
"@string/app_name"
>
<
intent-filter
>
<
action
android:name
=
"android.intent.action.MAIN"
/>
<
category
android:name
=
"android.intent.category.LAUNCHER"
/>
</
intent-filter
>
</
activity
>
<
service
android:name
=
"com.example.testservice.MyService"
>
<
intent-filter
>
<
action
android:name
=
"com.example.testservice.MYSERVICEFROMACTION"
/>
</
intent-filter
>
</
service
>
</
application
>
|
这里注意一个问题,不要以为Service是一个大的Thread 其实Service就是一个组件 一个没有界面的Activity 所以耗时任务同样是需要在异步线程中完成的! 所以我添加了一个AsyncTask如果不添加异步,将会发送阻塞
2.通过bindService启动Service
context.bindService()启动的Service的生命周期
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop
onBind()将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。 所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。
如图所示:
![](http://www.eoeandroid.com/data/attachment/forum/201306/13/173143ctgghghqlqcwwfwo.jpg)
其实bind的意思就是绑定。也就是把一个Activity和一个Service绑定在一块 有些时候的一些业务需要这样做,比如,你让Service处理网络数据,完成后Activity把数据放到View中去
所以Activity bind一个Service会拿到这个Service的引用可以操作Service中的方法
简单的代码如下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public
class
MainActivity
extends
Activity {
public
MyService myService;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent service =
new
Intent(
this
, MyService.
class
);
// 通过组件名调用
bindService(service, conn, Context.BIND_AUTO_CREATE);
}
// Ams开启的Service后 会通知MainActivity在不同的时机执行这些回调
private
ServiceConnection conn =
new
ServiceConnection() {
@Override
public
void
onServiceDisconnected(ComponentName name) {
}
// 在这里可以拿到Service中设置返回的东西,一般是一个Service对象
@Override
public
void
onServiceConnected(ComponentName name, IBinder service) {
myService = ((MyService.MyBinder) service).getService();
//拿到了Service的引用
Toast.makeText(MainActivity.
this
, myService.getVal(),
10
).show();
}
};
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package
com.example.testservice;
import
android.app.Service;
import
android.content.Intent;
import
android.os.AsyncTask;
import
android.os.Binder;
import
android.os.IBinder;
import
android.util.Log;
public
class
MyService
extends
Service {
String TAG =
"MyService"
;
public
String getVal() {
return
"Hi I am from Service!"
;
}
// 封装后Activity就可以通过getService方法获取这个服务实例了
@Override
public
IBinder onBind(Intent intent) {
Log.e(TAG,
"onBind"
);
return
new
MyBinder();
}
public
class
MyBinder
extends
Binder {
MyService getService() {
return
MyService.
this
;
}
}
}
|
其实,通常我们会做一个接口类,让Service实现他 ,然后在Activity 用这个接口类型就可以调用这些方法了 有点类似c++的头文件机制 如下:
01
02
03
|
public
interface
IMyService {
public
String getVal();
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public
class
MainActivity
extends
Activity {
public
IMyService myService;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent service =
new
Intent(
this
, MyService.
class
);
// 通过组件名调用
bindService(service, conn, Context.BIND_AUTO_CREATE);
}
// Ams开启的Service后 会通知MainActivity在不同的时机执行这些回调
private
ServiceConnection conn =
new
ServiceConnection() {
@Override
public
void
onServiceDisconnected(ComponentName name) {
}
// 在这里可以拿到Service中设置返回的东西,一般是一个Service对象
@Override
public
void
onServiceConnected(ComponentName name, IBinder service) {
myService = ((MyService.MyBinder) service).getService();
Log.e(
"TAG"
, name.toString());
// 组件的ComponentName
Toast.makeText(MainActivity.
this
, myService.getVal(),
10
).show();
}
};
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
MyService
extends
Service
implements
IMyService {
String TAG =
"MyService"
;
public
String getVal() {
return
"Hi I am from Service!"
;
}
// 封装后Activity就可以通过getService方法获取这个服务实例了
@Override
public
IBinder onBind(Intent intent) {
Log.e(TAG,
"onBind"
);
return
new
MyBinder();
}
public
class
MyBinder
extends
Binder {
MyService getService() {
return
MyService.
this
;
}
}
}
|
3、关于Service.onStart()和Service.onStartCommand()
谢谢版主的提醒,这个问题还是和值得研究的!其实涉及的是Service的重启问题 还是说一下原理吧:
首先看 Service.onStartCommand()的代码
01
02
03
04
|
public
int
onStartCommand(Intent intent,
int
flags,
int
startId) {
onStart(intent, startId);
return
mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
|
原来其实 Service.onStartCommand()也是调用了 onStart(intent, startId);与onStart相比只不过多了一个flags 并且返回了一个参数而已
首先是这个flags是什么呢?
其实 Service.onStartCommand()是在ActivityThread的void handleServiceArgs(ServiceArgsData data)调用的 这个方法是在ActivityThread控制
Service的消息队列中等待执行的:
01
02
03
04
|
case
SERVICE_ARGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"serviceStart"
);
handleServiceArgs((ServiceArgsData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
最后 我会找到调用他的本源
01
02
03
04
05
06
07
08
09
10
11
|
public
final
void
scheduleServiceArgs(IBinder token,
boolean
taskRemoved,
int
startId,
int
flags ,Intent args) {
ServiceArgsData s =
new
ServiceArgsData();
s.token = token;
s.taskRemoved = taskRemoved;
s.startId = startId;
s.flags = flags;
s.args = args;
queueOrSendMessage(H.SERVICE_ARGS, s);
}
|
这个方法是在ActiviceService这个类中调用的:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
private
final
void
sendServiceArgsLocked(ServiceRecord r,
boolean
oomAdjusted) {
final
int
N = r.pendingStarts.size();
if
(N ==
0
) {
return
;
}
while
(r.pendingStarts.size() >
0
) {
try
{
ServiceRecord.StartItem si = r.pendingStarts.remove(
0
);
si.deliveredTime = SystemClock.uptimeMillis();
r.deliveredStarts.add(si);
si.deliveryCount++;
if
(si.neededGrants !=
null
) {
mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
si.getUriPermissionsLocked());
}
int
flags =
0
;
if
(si.deliveryCount >
1
) {
flags |= Service.START_FLAG_RETRY;
}
if
(si.doneExecutingCount >
0
) {
flags |= Service.START_FLAG_REDELIVERY;
}
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
}
catch
(RemoteException e) {
....
}
}
|
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); [/mw_shl_code]
我们看的这里他的参数有两个可能 START_FLAG_RETRY,START_FLAG_REDELIVERY 这个Sdk有说明哦 现在从源码看来 这个flags是为了告诉你
当前是 START_FLAG_RETRY还是START_FLAG_REDELIVERY
都说到这里了 继续往下吧:
那么谁调用的sendServiceArgsLocked() 呢?其实是ActivityThread通过下面的方法使用aidl通知的ActiveService 于是 我们就发现 是在Service调用了onCreate之后调用的这个方法。
01
02
03
04
05
06
07
08
09
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
|
private
void
handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service =
null
;
try
{
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
}
catch
(Exception e) {
... ...
}
try
{
if
(localLOGV) Slog.v(TAG,
"Creating service "
+ data.info.name);
ContextImpl context =
new
ContextImpl();
context.init(packageInfo,
null
,
this
);
Application app = packageInfo.makeApplication(
false
, mInstrumentation);
context.setOuterContext(service);
service.attach(context,
this
, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try
{
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token,
0
,
0
,
0
);
}
catch
(RemoteException e) {
// nothing to do.
}
}
catch
(Exception e) {
... ...
}
}
|
好了 说完了 参数说法返回值吧
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
private
void
handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if
(s !=
null
) {
try
{
if
(data.args !=
null
) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
int
res;
if
(!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
}
else
{
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
QueuedWork.waitToFinish();
try
{
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token,
1
, data.startId, res);
//重新调用了 serviceDoneExecuting
}
catch
(RemoteException e) {
// nothing to do.
}
ensureJitEnabled();
}
catch
(Exception e) {
if
(!mInstrumentation.onException(s, e)) {
throw
new
RuntimeException(
"Unable to start service "
+ s
+
" with "
+ data.args +
": "
+ e.toString(), e);
}
}
}
}
|
这个方法后两个作用
1. 调用s.onStartCommand去做相关的操作,onStartCommand会返回一个integer. The integer is a value that describes how the system should continue the service in the event that the system kills it.
2. 告诉AMS已经完成了ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 1, data.startId, res); ActiveServices会收到通知,在ActiveServices.serviceDoneExecutingLocked中会对onStartCommand返回的值作出对应的处理。
01
02
03
04
05
06
07
08
09
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
60
61
|
void
serviceDoneExecutingLocked(ServiceRecord r,
int
type,
int
startId,
int
res) {
boolean
inStopping = mStoppingServices.contains(r);
if
(r !=
null
) {
if
(type ==
1
) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart =
true
;
switch
(res) {
case
Service.START_STICKY_COMPATIBILITY:
case
Service.START_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId,
true
);
// Don't stop if killed.
r.stopIfKilled =
false
;
break
;
}
case
Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId,
true
);
if
(r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled =
true
;
}
break
;
}
case
Service.START_REDELIVER_INTENT: {
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
ServiceRecord.StartItem si = r.findDeliveredStart(startId,
false
);
if
(si !=
null
) {
si.deliveryCount =
0
;
si.doneExecutingCount++;
// Don't stop if killed.
r.stopIfKilled =
true
;
}
break
;
}
case
Service.START_TASK_REMOVED_COMPLETE: {
// Special processing for onTaskRemoved(). Don't
// impact normal onStartCommand() processing.
r.findDeliveredStart(startId,
true
);
break
;
}
default
:
throw
new
IllegalArgumentException(
"Unknown service start result: "
+ res);
}
if
(res == Service.START_STICKY_COMPATIBILITY) {
r.callStart =
false
;
}
}
final
long
origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
}
else
{
Slog.w(TAG,
"Done executing unknown service from pid "
+ Binder.getCallingPid());
}
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private
void
serviceDoneExecutingLocked(ServiceRecord r,
boolean
inStopping) {
if
(DEBUG_SERVICE) Slog.v(TAG,
"<<< DONE EXECUTING "
+ r
+
": nesting="
+ r.executeNesting
+
", inStopping="
+ inStopping +
", app="
+ r.app);
else
if
(DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
"<<< DONE EXECUTING "
+ r.shortName);
r.executeNesting--;
if
(r.executeNesting <=
0
&& r.app !=
null
) {
if
(DEBUG_SERVICE) Slog.v(TAG,
"Nesting at 0 of "
+ r.shortName);
r.app.executingServices.remove(r);
if
(r.app.executingServices.size() ==
0
) {
if
(DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
"No more executingServices of "
+ r.shortName);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}
if
(inStopping) {
if
(DEBUG_SERVICE) Slog.v(TAG,
"doneExecuting remove stopping "
+ r);
mStoppingServices.remove(r);
r.bindings.clear();
}
mAm.updateOomAdjLocked(r.app);
}
}
|
说了一堆废话 我们会发现,其实这个返回值就是用来告诉Android系统:在系统资源紧张时服务的任务还没有完成而被回收后,系统对这个被强行回收的服务所做的事情。
还是 参考《 Android开发精要》一书的解释吧:(强烈推荐这本书啊)
“当系统资源紧张时,后台服务组件可能会被系统强制回收,而此时,服务组件可能尚未完成。”“onStartCommand函数增加了返回值和控制参数用于指定后台服务自己的运行方式”
介绍一下他们的几个返回值:具体请看Sdk
START_STICKY
如果资源紧张,service进程被kill掉,保留service的状态为开始状态,但不保留传递的的intent参数。随后系统会尝试重新创建service,由于服务状态回到了开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令(startService)被传递到service,那么参数Intent将为null。这种模式可以应用于形如后台播放音乐这种随时可能会被启动和停止的场景中,Android原生音乐播放器就是这么做的了,详情查看源码。注意返回这值请显示stop这个Service,否则浪费系统资源。
START_NOT_STICKY
“非粘性的”。如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务,系统不会关注这个服务的任务是否完成。邮件服务就使用了这个方案配合
alarmmanager每隔一段时间唤醒一次服务。服务执行完毕很有可能被系统回收。
START_REDELIVER_INTENT
使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,原来的intent参数不会被丢失,这是那个flags参数会被标志为START_FLAG_REDELIVERY以区分是否是首次调用onstartCommand()方法、参考Android原生的日历应用就是使用这个参数,在系统把服务回收后,他重启了扔可以获得intent中的数据。
START_STICKY_COMPATIBILITY
START_STICKY的兼容版本,不保证服务被kill后一定能重启。
我们知道这个方法只能支持2.0以上,那么2.0以下呢?
。
01
02
03
04
|
public
int
onStartCommand(Intent intent,
int
flags,
int
startId) {
onStart(intent, startId);
return
mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
|
mStartCompatibility 是什么?
01
02
03
04
|
private
boolean
mStartCompatibility =
false
;
....
//省略若干无关方法与代码
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
|
如果系统版本低于ECLAIR(Android 2.0以下)就兼容模式了
4、 拓展知识(进程和声明周期)
Android操作系统尝试尽可能长时间的保持应用的进程,但当可用内存很低时最终要移走一部分进程。怎样确定那些程序可以运行,那些要被销毁,Android让每一个进程在一个重要级的基础上运行,重要级低的进程最有可能被淘汰,一共有5级,下面这个列表就是按照重要性排列的:
1 一个前台进程显示的是用户此时需要处理和显示的。下列的条件有任何一个成立,这个进程都被认为是在前台运行的。
a 与用户正发生交互的。
b 它控制一个与用户交互的必须的基本的服务。
c 有一个正在调用生命周期的回调函数的service(如onCreate()、onStar()、onDestroy())
d 它有一个正在运行onReceive()方法的广播接收对象。
只有少数的前台进程可以在任何给定的时间内运行,销毁他们是系统万不得已的、最后的选择——当内存不够系统继续运行下去时。通常,在这一点上,设备已经达到了内存分页状态,所以杀掉一些前台进程来保证能够响应用户的需求。
2 一个可用进程没有任何前台组件,但它仍然可以影响到用户的界面。下面两种情况发生时,可以称该进程为可用进程。
它是一个非前台的activity,但对用户仍然可用(onPause()方法已经被调用)这是可能发生的,例如:前台的activity是一个允许上一个activity可见的对话框,即当前activity半透明,能看到前一个activity的界面,它是一个服务于可用activity的服务。
3 一个服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据。所以系统保证他们的运行,直到不能保证所有的前台可见程序都正常运行时才会终止他们。
4 一个后台进程就是一个非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后台进程会有很多个在运行,所以他们维护一个LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。如果一个activity正确的实现了生命周期的方法,并且保存它当前状态,杀死这些进程将不会影响到用户体验。
5 一个空线程没有运行任何可用应用程序组,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。
Android把进程里优先级最高的activity或服务,作为这个进程的优先级。例如,一个进程拥有一个服务和一个可见的activity,那么这个进程将会被定义为可见进程,而不是服务进程。
此外,如果别的进程依赖某一个进程的话,那么被依赖的进程会提高优先级。一个进程服务于另一个进程,那么提供服务的进程不会低于获得服务的进程。例如,如果进程A的一个内容提供商服务于进程B的一个客户端,或者进程A的一个service被进程B的一个组件绑定,那么进程A至少拥有和进程B一样的优先级,或者更高。
因为一个运行服务的进程的优先级高于运行后台activity的进程,一个activity会准备一个长时间运行的操作来启动一个服务,而不是启动一个线程–尤其是这个操作可能会拖垮这个activity。例如后台播放音乐的同时,通过照相机向服务器发送一张照片,启动一个服务会保证这个操作至少运行在service 进程的优先级下,无论这个activity发生了什么,广播接收者应该作为一个空服务而不是简单的把耗时的操作单独放在一个线程里。