4.2 千变万化的服务-Service开发
Service是Android系统中运行在后台、不和用户交互应用组件。它和Activity的级别差不多,只能在后台运行。每个Service必须在manifest文件中 通过<service>来声明。
4.2.1 Service的生命周期
Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们第一次启动Service的时候,先后调用onCreate()、onStart()这两个方法,当停止Service的时候,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会再执行onCreate()方法,而是直接执行onStart()方法。Service的启动有StartService和BindService两种方法,这两种方法对Service生命周期的影响是不一样的。
下面,我们分别来看看这两种方法是如何影响Service生命周期的:
1) StartService启动Service
用这种方法启动Service,Service会经历 onCreate 然后是onStart,接着一直处于运行状态,直到stopService的时候调用onDestroy方法。如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。
2) BindService启动Service
通过这种方法启动Service,Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Srevice就会调用onUnbindonUnbind->onDestroyed方法。
4.2.2 Service的启动和停止
我们已经对Service的生命周期有了一定的了解,Service的启动方式不同,它的生命周期也不相同。下面,就让我们来看看Service到底是如何启动和停止的。
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。
1)使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
2)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同生,必须同死”的特点。
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。
4.2.3 我的服务我来用—本地服务(Localservice)开发
本地服务用于应用程序内部。它可以启动并运行,直到有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()来启动,调用Context.stopService()来停止。它也可以调用Service.stopSelf() 或Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()就可以停止服务。
它用于实现应用程序自己的一些耗时任务,比如查询升级信息,它并不占用应用程序Activity所属线程,而只是单开线程后台执行,这样用户体验比较好。
有些服务是不需要和Activity交互就能直接运行的,而有些则是需要与Activity进行交互。下面,我们通过一些例子来加以说明。
1)不和Activity交互的本地服务
首先,我们新建一个LocalService类继承自Service,代码如下:
// import略 public class LocalService extends Service{
@Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); } } |
然后,新建一个类ServiceActivity继承自Actvity,代码如下:
// import略 public class ServiceActivity extends Activity{
private Button startBtn,stopBtn;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.localservice); startBtn = (Button)findViewById(R.id.start_button); stopBtn = (Button)findViewById(R.id.stop_button); startBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startService(new Intent("com.char4.LOCAL_SERVICE")); } }); stopBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { stopService(new Intent("com.char4.LOCAL_SERVICE")); } }); }
} |
布局文件localservice.xml代码如下,它定义了两个按钮,一个用来启动Service,一个用来停止Service:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/start_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动"/> <Button android:id="@+id/stop_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止"/> </LinearLayout> |
别忘了,在AndroidMainfest.xml中注册Service:
<service android:name=".LocalService"> <intent-filter> <action android:name="com.char4.LOCAL_SERVICE " /> <category android:name="android.intent.category.default" /> </intent-filter> </service> |
下面,我们来看看效果,如图4-7所示:
图4-7 startService启动顺序
通过日志打印我们可以发现,第一次点击“启动”按钮时,会调用onCreate和onStart方法,在没有点击“停止”按钮前,无论点击多少次“启动”按钮,都只会调用onStart。而点击“停止”按钮时则调用onDestroy。再次点击 “停止”按钮,会发现不会进入service的生命周期的,即不会再调用onCreate,onStart和onDestroy,而onBind在点击“启动”和“停止”按钮时都没有调用。