8.1 理论概述
Service是一个应用组件, 它用来在后台完成一个时间跨度比较大的工作
且没有关联任何界面
一个Service可以完成下面这些工作:
访问网络
播放音乐
文件IO操作
大数据量的数据库操作
……
服务的特点:
• Service在后台运行,不用与用户进行交互
• 即使程序被切换到后台,或者用户打开新的应用,服务仍可运行
• 服务并非运行在独立的进程中,而是依赖于创建服务的应用程序进
程。如果某个应用进程被杀掉,该服务也会停止
• 在默认情况下, Service运行在应用程序进程的主线程(UI线程)中,
如果需要在Service中处理一些网络连接等耗时的操作,那么应该将
这些任务放在Service的分线程中处理,避免阻塞用户界面
8.1.2 区别Service与Activity
Activity:
-
Activity对应一个界面
-
应用退出, Activity对象就会死亡
-
应用再次进入, 启动的Activity对象是重新创建的
Service
-
不与任何界面关联,绝不会到前台来
-
应用退出, Service仍在运行
-
应用再次进入, 启动的Service还是前面运行的Service对象
8.1.3 Activity 与 Service 的选择标准:
- 如果某个应用程序组件需要运行时向用户呈现某种界面,或者该程序
需要与用户交互,就需要使用Activity,否则就应该考虑使用Service。
8.1.4 区别Service与Thread
Service
-
用来在后台完成一个时间跨度比较大的工作的应用组件
-
Service的生命周期方法运行在主线程, 如果Service想做持续时间比
较长的工作, 需要启动一个分线程(Thread) -
应用退出: Service不会停止
-
应用再次进入: 可以与正在运行的Service进行通信
Thread
-
用来开启一个分线程的类, 做一个长时间的工作
-
Thread对象的run()在分线程执行
-
应用退出: Thread不会停止
-
应用再次进入: 不能再控制前面启动的Thread对象
8.1.5 Service的分类
1. Local Service(****本地服务)
Service对象与Service的启动者在同个进程中运行, 两者的通信是进程
内通信
2. Remote Service(****远程服务)
Service对象与Service的启动者不在同一个进程中运行, 这时存在一个
进程间通信的问题, Android专门为此设计了AIDL来实现进程间通信
8.1.6 使用本地Service


8.1.6 AIDL
每个应用程序都运行在自己的独立进程中,并且可以启动另一个应用
进程的服务,而且经常需要在不同的进程间传递数据对象。
在Android平台,一个进程不能直接访问另一个进程的内存空间,所
以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且
有序的通过进程边界。
AIDL (Android Interface Definition Language) 用于生成可以在Android设备上两个进程之间进行进程间通信
(interprocess communication, IPC)的代码。
如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)
对象的操作,就可以使用AIDL生成可序列化的参数。

8.2 服务的创建
创建一个MyService类继承自Service,此时会自动实现onBind()方法,MyService中的代码如下所示:
public class MyService extends Service
{
public IBinder onBind(Intent intent)
{
return null;
}
}
onBind()方法是Service类中唯一的抽象方法,所以必须要在子类中实现。
在功能清单文件中注册MyService,否则服务是不生效的
<service android:name="cn.itcast.servicetest.MyService"/>
8.3 服务的生命周期
与其他组件不同的是,Service不能自己主动运行,需要调用相应的方法来启动。启动服务方法有两个,分别是Context。startService()和Context.bindService()。使用不同的方法启动服务,服务的生命周期也会不同。

8.3.1 startService()方式开启服务的生命周期
当其他组件调用startService()方法时,服务会先执行onCreate()方法,接着执行onStartCommand()方法,此时服务处于运行状态,直到自身调用stopSelf()方法或者其他组件调用stopService()方法时服务停止,最终被系统销毁。这种方式开启的服务会长期的在后台运行,并且服务的状态与开启者的状态没有关系。需要注意的是,onCreate()方法只有在服务创建时执行,而onStartCommand()方法则是每次启动服务时调用。
8.3.2 bindService方式开启服务的生命周期
当其他组件调用bindService()方法时,服务被创建,接着客户端通过Ibinder接口与服务通信。客户端通过unbindService()方法关闭连接,多个客户端能绑定到同一个服务上,并且当它们都解绑时,系统直接消耗服务。这种方式开启的服务与开启者的状态有关,当调用者销毁了,服务也会被销毁。
8.3.3 生命周期方法作用
-
onCreate():第一次创建服务时执行的方法
-
onDestory():服务被销毁时执行的方法
-
onStartCommand():客户端通过调用startService(Intent service)显示启动服务时执行该方法。
-
onBind():客户端通过调用bindService(Intent,Service,int)启动服务时执行该方法。
-
onUnbind():客户端调用unBindService(ServiceConnection conn)断开服务绑定时执行的方法。
8.4 服务的启动方式
startService()和bindService()方法启动服务
8.4.1 start方式启动服务
Context的startService()和stopService()方法来启动、关闭服务,代码示例如下:
Intent intent=new Intent(this,StartService.class);
Context.startService(intetnt);//开启服务
Context.stopService(intent);//关闭服务
Intent对象时用于指定要启动或者关闭服务的。当然,还需要在清单文件中注册服务MyService,如下:
<service android:name="cn.itcast.bindservice.MySerivce"/>
8.4.2 bind方式启动服务
当程序使用startService()和stopService()启动、关闭服务时,服务于调用者之间基本不存在太多的关联,也无法与访问者进行通信、数据交互等。如果服务需要与调用者进行方法调用和数据交互时,应该使用bindService()和unbindService()启动、关闭服务。
bindService()方法的完整方法名为bindService(Intent service,ServiceConnection conn,int flags),该方三个参数解释如下:
-
Intent对象用于指定要启动的Service
-
ServiceConnection对象用于监听者调用者与Service之间的连接状态。当调用者与Service连接成功时将回调该对象的onServiceConnected(ComponentName name,IBinder service)方法。断开连接时将回调该对象的onServiceDisconnected(ComponentName name)方法。
-
flags指定绑定时是否自动创建Service(如果Service还未创建)。该参数可指定为0,即不自动创建,也可以指定为BIND_AUTO_CREATE即自动创建。
Intent intent=new Intent(this,MyService.class);
//创建MyConn类,用于实现连接服务
private class MyConn implements ServiceConnection
{
//当成功绑定服务时调用的方法,返回MyService里面的Ibinder对象
public void onServiceConnected(ComponentName name,IBinder service)
{
//MyBinder是服务中继承Binder的内部类
MyBinder myBinder=(MyBinder)service;
}
//当服务失去连接时调用的方法
public void onServiceDisconnected(ComponentName name)
{
}
}
Myconn myconn=new MyConn();
//参数1是Intent,参数2是连接对象,参数3是flag表示如果服务不存在就直接创建
Context.bindService(intetn,myconn,BIND_AUTO_CREATE);
Context.unbindService(myconn);//解绑服务
需要注意的是ServiceConnection中的onServiceConnected()方法有一个参数IBinder service,这个参数是在服务中的onBind()方法中返回的。
创建服务类
public class Myservice extends Service
{
//创建服务的代理,调用服务中的方法
class MyBinder extends Binder
{
public void callMethodInService()
{
methodInservice();
}
}
public void methodInService()
{
Log.i("MyService","自定义方法methodInService()");
}
//其他回调方法和自定义方法
......
}
编写界面交互代码
public class MainActivity extends Activity
{
private MyBinder myBinder;
private MyConn myconn;
protected void onCreate(){}
//绑定服务
public void btn_bind(View view)
{
if(myconn==null)
{
myconn=new MyConn();
}
Intent intent=new Intent(this,MyService.class);
bindService(intent,myconn,BIND_AUTO_CREATE);
}
//解绑服务
public void btn_unbind(View view)
{
if(myconn!=null)
{
unbindService(myconn);
myconn=null;
}
}
//调用服务中的方法
public void btn_call(View view)
{
myBinder.callMethodInService();
}
//创建MyConn类,用于实现连接服务
private class MyConn implements ServiceConnection
{
//当成功绑定到服务时调用的方法,返回MyService里面的Ibinder对象
public void onServiceConnected(ComponentName name,IBinder service)
{
myBinder=(MyBinder)service;
Log.i("MainActivity","服务绑定成功,内存地址为:"+myBinder.toString());
}
//当服务失去连接时调用的方法
public void onServiceDisConnected(ComponentName name)
{
}
}
}
8.5 服务通信
在Android中,服务的通信有2种:一是本地服务通信,一种是远程服务通信。本地服务通信是指应用程序内部的通信,而远程通信是指两个应用程序之间的通信。使用这两者方式进行通信时必须满足一个前提,就是服务必须以绑定方式开启。
8.5.1 本地服务通信
首先需要开发一个Service类,该类提供一个IBinder onBind(Intent intent)方法,onBind()方法返回的IBinder对象会作为参数传递给ServiceConnection类中的onServiceConnected(ComponentName name,IBinder service)方法,这样访问者就可以通过IBinder对象与Service进行通信
服务在进行通信时实际上使用的就是IBinder对象,在ServiceConnection类中得到的IBinder对象,通过该对象可以获取到服务中定义的方法,执行具体操作。
8.5.2 远程服务通信
在Android中,各个应用程序都运行在自己的进程中,进程之间一般无法直接进行通信,如果想要完成不同进程之间的通信,就需要使用远程服务通信。远程服务通信通过AIDL(Android Interface Definition Language)实现,它是一种接口定义语言(Interface Definition Language),其语法非常简单,与Java中定义的接口很相似,但是存在几点差异,具体如下:
- AIDL定义接口的源代码必须以.aidl 结尾
- AIDL接口中用到的数据类型,除了基本数据类型、String、List、Map、CharSEquence之外,其他类型全部都需要导入包,即使他们在同一个包中。
- 开发人员定义的AIDL接口只是定义了进程之间的通信接口,服务端、客户端都需要使用Android SDK安装目录下的platform-tools子目录下的aidl.exe工具为该接口提供实现。如果开发者使用ADT工具进行开发,那么ADT工具会自动实现AIDL接口。
定义AIDL接口的示例代码
package cn.itcast.service;
interface IService
{
String getName();
int getPrice();
}
需要注意的是,AIDL没有类型修饰符,在编写AIDL文件时,不能加上类型修饰符
定义好AIDL接口之后,接着需要在应用程序中创建Service的子类。该Service的onBind()方法所返回的IBinder对象应该是ADT所生成的IService.Stub的子类。如下:
public class MyService extends Service
{
//继承IService.stub
private class IServiceBinder extends Stub
{
public String getName()throw RemoteException
{
return "HASH";
}
public int getPrice()throws RmoteException
{
return 100;
}
}
public IBinder onBind(Intent intetn)
{
//第一步执行onBind方法
return new IServiceBind();
}
public void onCreate()
{
super.onCreate();
}
}
SeekBar的使用
SeekBar允许用户拖动滑块改变SeekBar的值,例如手机音量调节,同时SeekBar还允许用户改变滑块的外观,
- andriod:thumb:指定一个Drawable对象,该对象将作为自定义滑块。
- 监听器OnSeekBarChangeListener:监听滑块位置的改变
- 方法setProgress():用来设置SeekBar的当前值
- 方法SetMax():设置SeekBar的最大值。
MediaPlayer的使用
常用方法名称 | 功能描述 |
---|---|
setAudioStreamType() | 指定音频文件的类型必须在prepare()方法之前调用 |
setDataSource() | 设置要播放的音频文件的位置 |
prepare() | 在开始播放之前调用这个方法完成准备工作 |
start() | 开始或继续播放音频 |
pause() | 暂停播放音频 |
reset() | 将MediaPlayer对象重置到刚刚创建的状态 |
seekTo() | 从指定的位置开始播放音频 |
release() | 释放掉与MediaPlayer对象相关的资源 |
isPlaying() | 判断当前MediaPlayer是否正在播放音频 |
getDuration() | 获取载入音频文件的时长 |
getCurrentPosition() | 获取当前播放音频文件的位置 |
网友评论