今天我们来说Service。正如书中所说Service组件也是可执行程序,他有自己的声明周期。创建,配置Service与创建配置Activity的过程基本相似。
这里我们总结一下:
1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。
特别注意:
1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);
2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;
3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;
4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。
生命周期方法说明
onStartCommand()
当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
onBind()
当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。
onCreate()
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已在运行,则不会调用此方法。
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
下面我们来详细介绍Android Service的开发。
开发步骤:
1:定义一个Service的子类。
2:在AndroidManifest.xml文件配置该Service。
Service组件生命周期方法:
1:IBinder onBind(Intent intent):该方法是Service子类必须实现的方法。该方法返回一个IBinder对象,应用程序可通过该对象与Service组件通信。
2:void onCreate():当该Service第一次被创建后将立即回调该方法。
3:void onDestroy():当该Service被关闭之前将会回调该方法。
4:void onStartCommand(Intent intent ,int flags, int startId):该方法的早期版本是void onStart(Intent intent,int startId),每次客户端调用startService(Intent)方法启动该Service时都会回调该方法。
5:boolean onUnbind(Intent intent):当该Service上绑定的所有客户端都断开连接时将会回调该方法。
举例:定义一个Service组件
public class MService extends Service{
//必须实现的方法
@Override
public IBinder onBind(Intent intent) {
return null;
}
//Service被创建时回调该方法
@Override
public void onCreate() {
super.onCreate();
System.out.println("Service is createed!");
}
//Service被启动时回调该方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("Service is started!");
return START_STICKY;
}
//Service被关闭之前回调
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("Service is destroyed!");
}
}
以上 代码就是Service组件的框架,若希望该Service做些什么可以在onCreate()或onStartCommand()方法中定义相关代码。
下面我们还要在AndroidManifest.xml中配置该Service:
<service android:name=".MService" >
<intent-filter>
<!-- 为该Service组件的intent-filter配置action -->
<action android:name="com.xiyou.service.M_SERVICE" />
</intent-filter>
</service>
Service已经开发完了,下面我们要做的就是如何在Android中启动Service:
1:通过Context的startService()方法:使用该方法启动Service,访问者与Service之间没有关联,及时访问者退出了,Service仍然运行。
2:通过Content的bindService()方法:使用该方法启动Service,访问者与Service绑定在一起,访问者一旦退出,Service也就停止了。
下面我们开发一个启动Service的类:
public class MainActivity extends Activity {
private Button start, stop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.button1);
stop = (Button) findViewById(R.id.button2);
final Intent intent = new Intent();
intent.setAction("com.xiyou.service.M_SERVICE");//为Intent设置Action属性
start.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startService(intent);//启动指定Service
}
});
stop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
stopService(intent);//停止指定Service
}
});
}
}
LogCat输出:
由此可看出每次Service创建都会调用onCreate()方法,每次Service被启动都会调用onStartCommand()方法,但是多次启动Service却不会每次都调用onCreate()方法。
绑定本地Service并与之通信:
使用startService()和stopService()启动,关闭Service时,Service不能与访问者进行通信,数据交换。
而如果使用bindService()和unbindService()方法启动,关闭Service,Service就可以和访问者之间进行方法调用和数据交换。
Context的bindService()方法的完整方法签名为:bindService(Intent service,ServiceConnection conn,int flags);
1:service:该方法通过Intent指定要启动的Service。
2:conn:该参数是一个ServiceConnection对象,该对象用于监听访问者与Service对象之间的连接情况。当访问者与Service之间连接成功时将回调该 ServiceConnection对象的onServiceConnection(ComponentName name,IBinder service)方法,当Service所在的宿主进程由于异常中断或由于其它 原因终止,导致该Service与访问者之间断开连接时回调该ServiceConnection对象的onServiceDisconnected(ComponentName name)方法。
3:flags:指定绑定时是否自动创建Service(如果Service还未创建)。该参数可指定为0(不自动创建)或BIND_AUTO_CREATE(自动创建)。
注:ServiceConnection对象的onServiceConnection方法中有一个IBinder对象,该对象即可实现与被绑定Service之间的通信。
注:当调用者主动通过unBindService()方法断开与Service的连接时,ServiceConnection对象的onServiceDisconnected(ComponentName name)方法并不会被调用。
public class BindService extends Service {
private int count;
private boolean quit;
// 定义onBinder方法所返回的对象
private MyBinder binder = new MyBinder();
// 通过继承Binder来实现IBinder类。
public class MyBinder extends Binder {
public int getCount() {
// 获取Service的运行状态:count
return count;
}
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("Service is Binded");
// 返回IBinder对象
return binder;
}
// Service被创建时回调该方法
@Override
public void onCreate() {
super.onCreate();
System.out.println("Service is created");
// 启动一条线程,动态的修改count状态值
new Thread() {
public void run() {
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count++;
}
};
}.start();
}
// Service被断开连接时回调该方法
@Override
public boolean onUnbind(Intent intent) {
System.out.println("Service is unbinded");
return true;
}
// Service被关闭之前回调该方法
@Override
public void onDestroy() {
this.quit = true;
System.out.println("Service is destroyed");
}
}
再写一个Activity用于绑定Service:
public class BindServiceTest extends Activity {
Button bind, unbind, getServiceStatus;
// 保持所启动的Service的IBinder对象
BindService.MyBinder binder;
// 定义一个ServiceConnection对象
private ServiceConnection conn = new ServiceConnection() {
// 当Activity与service断开连接时回调该方法
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("service disconnected");
}
// 当Activity与service连接成功时回调该方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
System.out.println("service connected");
binder = (MyBinder) service;
}
};
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bind = (Button) findViewById(R.id.button1);
unbind = (Button) findViewById(R.id.button2);
getServiceStatus = (Button) findViewById(R.id.button3);
final Intent intent = new Intent();
intent.setAction("com.xiyou.service.BIND_SERVICE");
bind.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 绑定指定service
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
unbind.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 解除绑定指定service
unbindService(conn);
}
});
getServiceStatus.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 获取并显示,service的count值
Toast.makeText(BindServiceTest.this,
"Service的count值为:" + binder.getCount(),
Toast.LENGTH_LONG).show();
System.out.println("Service的count值为:" + binder.getCount());
}
});
};
}
运行结果:
注:不论点击多少次 bind都只会绑定一次,回调一次onBind()方法。
下面我们再来介绍一种可以在其中处理耗时任务的Service:IntentService。
IntentService的特性:
1:IntentService使用队列来管理请求中的Intent,每当客户端通过Intent启动IntentService,都将这个Intent放到队列中;
2:IntentService创建单独的线程来处理所有的Intent。
3:IntentService创建单独的线程来处理onHandleIntent()方法实现的代码。
4:当所有Intent请求处理完毕,IntentService会自动停止,所以不用调用stop。
5:IntentService中的onBind()方法提供了默认实现,默认return null;
6:IntentService中的onStartCommand()方法提供了默认实现,该实现会将Intent添加到队列中。
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
}
// IntentService会使用单独的线程来执行该方法的代码。
@Override
protected void onHandleIntent(Intent intent) {
// 该方法内可以进行耗时任务
long endTime = System.currentTimeMillis() + 20 * 1000;
System.out.println("on Start");
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("耗时任务完成。");
}
}
跨进程调用Service(AIDL Service):
功能:用于实现Android中跨进程通信,数据交换。
区别:本地Service是将onBind()返回的IBinder本身传递给客户端的ServiceConnection的onServiceConnection()的第二个参数,但是远程Service是将onBind()返回的IBinder对象的代理传给客户端ServiceConnection的onServiceConnection()的第二个参数。
实现AIDLService需要定义远程接口,客户端通过这个接口进行数据交换:
interface ICat{
String getColor();
double getWeight();
}
注:AIDL定义的接口源代码后缀是.aidl。如图
注:gen文件夹中的ICat.java是ADT自动生成的,在Android SDK安装目录下的platform-tools子目录下的aidl.exe为该接口提供实现。
Service类代码:
public class AidlService extends Service{
private String color;
private double weight;
private CatBinder catBinder;
Timer timer = new Timer();
String[] colors = new String[]{"red","black","yellow"};
double[] weights = new double[]{2.5,5.7,4.5};
//继承实现ICat接口,并实现IBinder接口
public class CatBinder extends Stub{
@Override
public String getColor() throws RemoteException {
return color;
}
@Override
public double getWeight() throws RemoteException {
return weight;
}
}
@Override
public void onCreate() {
super.onCreate();
catBinder = new CatBinder();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 随机改变color和weight
int rand = (int)(Math.random()*3);
color = colors[rand];
weight = weights[rand];
}
}, 0, 600);
}
@Override
public IBinder onBind(Intent intent) {
/**
* 返回catBinder对象,即IBinder对象,因为Stub实现了IBinder接口
*
*/
return catBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
timer.cancel();
}
}
客户端访问AIDLService:
public class AidlServiceTest extends Activity {
private ICat catService;
private Button get;
public ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
catService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//实现接收service传过来的对象的代理。
catService = ICat.Stub.asInterface(service);
}
};
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
get = (Button) findViewById(R.id.button1);
Intent intent = new Intent();
intent.setAction("com.example.aidlservice.AIDL_SERVICE");
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
get.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
System.out.println("color:"+catService.getColor());
System.out.println("weight:"+catService.getWeight());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
};
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(serviceConnection);
}
}
文件夹结构,在客户端的代码中也需要ICat.aidl文件接口。
注:客户端ICat.aidl的文件夹要和Service端的ICat.aidl的文件夹名字一样。否则报