接下来说一下Android中Service,顾名思义,Service即服务,就是执行一些比较耗时的操作,例如获取网络数据、播放音乐等。一般服务都是没有界面的,都是在后台默默地运行着。
首先需要说明一点,Android的四大组件所在的进程都是应用程序的UI线程,虽然服务让人感觉在后台运行貌似是在另外一个线程中运行,但事实并不是这样,假如在服务里执行Thread.sleep(20000),那么就会导致程序出现“ANR”。比较好的做法就是在服务里,创建一个子线程,然后在子线程里执行比较耗时的操作。当然Android提供了一个包装好的类IntentService,这个类就能自动在子线程里执行耗时操作,不需程序员手动创建子线程,关于这个类后面会讲到如何使用。注意当用户按返回键退出应用程序时,此时只是关闭了Activity,该程序的主进程并未结束,这也解释了为什么退出程序时仍能够利用服务在后台播放音乐。
Android要开启服务,首先要有自己的服务类,所以要先定义自己的服务类,例如叫MyService,继承自Service类,然后就可以在里面重写一些方法,执行自己想要执行的代码了。当然还需要在清单文件里面配置,配置方法与Activity的差不多。
Service里有几个比较重要的方法用于重写。
onBind(Intent intent):这个方法是在使用绑定方式开启服务用到的,后面会讲到。
onUnbind(Intent intent):只要是正常人就能看出这个上面那个家伙是一伙的,也是在使用绑定方式开启服务用到。
onCreate():这个方法在Activity我们就见过了,在服务被开启的时候调用。
onDestroy():这个方法在Activity我们也见过了,在服务被停止的时候调用。
onStartCommand(Intent intent, int flags, int startId):每次开启服务都会调用该方法,可能你会有疑问,那这不就与onCreate()方法一样吗?其实不然,服务其实只能开启一次,开启后没关闭的话以后再次开启的时候就只会调用onStartCommand方法而是不会再调用onCreate方法。其实我们一般要实现的代码就写在这个方法里。
写完我们自己的服务类就可以开启服务了,一般开启服务有两个方法,startService(Intent service)和bindService(Intent service, ServiceConnection conn, int flags),第一种是直接开启服务,第二种是使用绑定方式开启服务。
区别:第一种开启服务后是手动调用不了服务类里面的方法的,但在退出程序时服务仍会在后台运行,也就是说服务是不依赖于Activity的。第二种方式开启服务可以手动调用服务里的方法(具体方法后面会提到),但此时开启的服务是与当前Activity绑定在一起的,换句话说,当前Activity退出了,服务也就停止了。可能你会问,假如我既需要调用服务里面的方法,又要求服务能够在Activity退出的时候在后台运行,那怎么办?其实我们可以混合这两种方式开启服务,就可以实现我们的需求了。
下面分别看一下这两种开启方式的具体实现方法:
第一种方式:startService(Intent service):这个方法是Context定义的方法,该函数需要一个Intent对象,表示需要开启的服务,可利用Intent里的setClassName()方法设置,这个与Activity的跳转差不多,这里不再累赘。只有在恰当的地方调用这个方法就能开启服务了,关闭服务可利用方法stopService(Intent name)。
第二种方法:bindService(Intent service, ServiceConnection conn, int flags):这种方法实现起来有点复杂,但思路是很清晰的。下面说一下步骤:
第一步:看一下这个方法的参数,第一个是Intent对象,第三个是标识,一般写BIND_AUTO_CREATE,表示在服务没有被开启的时候开启服务,关键是第二个参数,其实 ServiceConnection是一接口,所以我们必须定义一个类,然后实现这个接口,我们需要实现这个接口里面的两个方法,一个是onServiceConnected(ComponentName name, IBinder service),这个暂时写为空实现就行,待会再回来写代码,另外一个是onServiceDisconnected(ComponentName name)方法,这个方法只有在服务异常终止时才会被调用,正常终止时是不会调用该方法的。定义这个类后就可以创建该类的对象然后作为bindService方法的第二个参数了。
第二步:还记得上面我们说的onBind(Intent intent)方法吗?现在需要用到它了,注意它是有返回值的,返回类型是IBinder,这是一个接口,所以我们还是需要定义一个类,然后实现这个接口。其实Android中定义了一个类Binder就实现了这个接口,因此我们只需要继承这个类就行了。由于待会在开启服务的那个地方(可能是一个Activity里)会用到这个类,所以一般就会把这个类定义为public,但一般我们并不这样做,因为我们并不希望暴露出这个类的所有public方法给外面调用,因此我们采用了另外一种方法实现,就是再定义一个接口,然后让这个类声明为private,然后实现这个接口的方法,当然还要继承Binder,这样我们就只暴露出我们定义的那个接口里面的方法了。在重写接口里那些方法时,就可以调用外部类(因为我们将这个类定义为内部类)的那些我们想要调用的方法了。然后在onBind(Intent intent)返回这个类的一个对象就行了。
第三步:上面onBind(Intent intent)函数返回的类对象,其实就传到onServiceConnected(ComponentName name, IBinder service)里面的第二个参数,在绑定方式开启服务时,调用完onBind(Intent intent)函数后就会调用onServiceConnected这个函数,我们就可以将service这个传递过来的对象拿到,然后强制转换为我们第二步定义的那个接口类型,把它做成类的成员变量,然后就可以在别的地方调用其中的方法了。
估计看起来有点费力,这里有个源代码,就是按这种方式写的,可以参考一下。
http://download.csdn.net/detail/u013631187/8301215
上面我们在说到这两种方式开启服务的区别时,我们提到可以采用混合模式来满足我们既要求能够调用服务里的方法又能够当Activity退出时在后台运行的需求,但这样做的话服务的状态就会比较混乱,一不小心就会导致程序出现异常或者甚至挂掉了。比如说,服务绑定之后只能解绑一次,再次解绑程序就会挂掉,服务在绑定后是停不了的,但你还是执行了停止服务操作,在下次解绑服务的时候,服务会会停止掉了。所以有一个技巧,就是在你需要调用服务类里面的方法是去绑定服务,调用完就立即解绑服务,这样就不会出现那种比较混乱的情况了。
最后顺便说一下解绑服务的方法unbindService(ServiceConnection conn),它需要一个实现ServiceConnection接口的类对象,这里需要我们在绑定服务传递bindService(Intent service, ServiceConnection conn,int flags)方法的第二个参数的那个对象,因此需要把它做成类成员变量。