Service组件是android系统四大组件之一,通常用来处理一些后台数据交互、逻辑处理等工作,和Activity一个很大的不同在于Service没有自己的交互界面。
对Service的调用是通过intent进行的:
private void startMyService() {
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
上面的代码是通过startService()方法启用服务,还有一种启动服务的方法是bindService(),两种方法启动的服务的生命周期也不同,以下图作简单说明:
startService()会触发Service的onStartCommand()回调函数,android2.0以前回调的是onStart(),为了向下兼容,onStart()被封装在了onStartCommand()方法内。在外部多次调用startService()会多次触发onStartCommand(),直到调用一次stopService()为止,才会结束Service的生命周期。即使在调用stopService()前调用者就退出了,但Service依然会运行下去。
bindService()会触发Service的onBind()回调函数,该函数返回一个IBinder类型的对象给ServiceConnection对象,调用者后续可通过该IBinder对象调用服务提供的接口。绑定的服务会随着调用者的退出而终止。
下面用代码来阐释Service的具体用法:
定义MyService类继承Service:
public class MyService extends Service {
static final String TAG = "MyService";
public class MyBinder extends Binder {
public MyBinder() {
Log.i(TAG, "in customBinder constuctor");
}
public void myBinderFunc() {
Log.i(TAG, "MyBinderFunc");
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Log.i(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.i(TAG, "onBind");
return new MyBinder();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.i(TAG, "on Destroy");
super.onDestroy();
}
}
MyService复写了onStartCommand(), onBind(), onDestroy()三种方法,并且定义了MyBinder类,实现了myBinderFunc()方法。
在Activity中启动MyService:
public class MainActivity extends Activity {
static final String TAG = "MyActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
bindMyService();
}
});
}
private void bindMyService() {
Intent intent = new Intent(this, MyService.class);
startService(intent);
//bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
}
运行工程后,可以看到Logcat输出“onStartCommand"提示,说明onStartCommand()函数被调用。通常我们会在该方法中开始数据的处理,但需要注意的是,service是运行在主线程之中的,直接执行耗时的操作会阻塞主线程,因此如果要进行数据下载、大量计算等工作,应该在Service中另起线程进行操作,或者在manifest.xml中指定service组件的process属性,让其属于单独的进程。
如果需要在服务运行的过程中对其施加控制,可以多次调用startService(),在onStartCommand()方法中,对intent中的Bundle对象进行解析来执行相应的操作。但这种方法会使得onStartService()变得臃肿,逻辑和功能代码重叠严重。
解决的办法是通过bindService启动服务,调用者的代码如下:
public class MainActivity extends Activity {
static final String TAG = "MyActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
bindMyService();
}
});
}
MyBinder myBinder;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
iService = IStockQuoteService.Stub.asInterface(service);
myBinder = (MyBinder)service;
myBinder.MyBinderFunc();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.i(TAG, "release service");
}
};
private void bindMyService() {
Intent intent = new Intent(this, MyService.class);
//startService(intent);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
}
这里调用bindService()方法而非startService()方法,传入的参数包括intent,服务连接对象conn和flag。这将导致Service的onBind()方法被调用,一旦onBind()返回,将触发conn的onServiceConnected()方法,方法中的IBinder参数就是onBind()的返回值。在我们的MyService中,返回的是一个MyBinder对象。
conn的onServiceConnected()方法回调后,把IBinder对象保存到本地的MyBinder变量里,调用者之后就可以通过该变量控制服务的运行。
这里的细节在于,Service必须自己定义Binder子类,并且实现相关方法,再由onBind()方法回传。调用者需要在ServiceConnection.onServiceConnected()方法中获取Binder,然后进行相关处理。细心的人可能已经发现,我们定义的是Binder的子类,而onBind()返回值以及onServiceConnected()参数的类型都是IBinder,到底是Binder还是IBinder?事实上,IBinder只是一个接口,具体实现则是在Binder中,因此我们定义的是包含具体实现的Binder子类,否则我们需要自己再把IBinder的所有接口都再实现一遍……至于回传的是IBinder类型的参数,则是android系统设计如此,因为java基础不是很好,不太清楚interface和class在传递上的区别,就不作分析了。不过还是猜想一下,可能跟跨进程调用的实现有关。
提到了跨进程调用,本来想再记述一下有关AIDL的理解,但查阅了相关资料发现先前的理解还是太浅薄,改日再专门写AIDL吧。